Uncommited changes synced
[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 {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     pilltype : true,
5769     
5770     navItems : false, 
5771     
5772     getAutoCreate : function()
5773     {
5774         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5775         
5776         cfg = {
5777             tag : 'ul',
5778             cls: 'nav' 
5779         };
5780         if (Roo.bootstrap.version == 4) {
5781             if (['tabs','pills'].indexOf(this.type) != -1) {
5782                 cfg.cls += ' nav-' + this.type; 
5783             } else {
5784                 // trying to remove so header bar can right align top?
5785                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5786                     // do not use on header bar... 
5787                     cfg.cls += ' navbar-nav';
5788                 }
5789             }
5790             
5791         } else {
5792             if (['tabs','pills'].indexOf(this.type) != -1) {
5793                 cfg.cls += ' nav-' + this.type
5794             } else {
5795                 if (this.type !== 'nav') {
5796                     Roo.log('nav type must be nav/tabs/pills')
5797                 }
5798                 cfg.cls += ' navbar-nav'
5799             }
5800         }
5801         
5802         if (this.parent() && this.parent().sidebar) {
5803             cfg = {
5804                 tag: 'ul',
5805                 cls: 'dashboard-menu sidebar-menu'
5806             };
5807             
5808             return cfg;
5809         }
5810         
5811         if (this.form === true) {
5812             cfg = {
5813                 tag: 'form',
5814                 cls: 'navbar-form form-inline'
5815             };
5816             //nav navbar-right ml-md-auto
5817             if (this.align === 'right') {
5818                 cfg.cls += ' navbar-right ml-md-auto';
5819             } else {
5820                 cfg.cls += ' navbar-left';
5821             }
5822         }
5823         
5824         if (this.align === 'right') {
5825             cfg.cls += ' navbar-right ml-md-auto';
5826         } else {
5827             cfg.cls += ' mr-auto';
5828         }
5829         
5830         if (this.inverse) {
5831             cfg.cls += ' navbar-inverse';
5832             
5833         }
5834         
5835         
5836         return cfg;
5837     },
5838     /**
5839     * sets the active Navigation item
5840     * @param {Roo.bootstrap.NavItem} the new current navitem
5841     */
5842     setActiveItem : function(item)
5843     {
5844         var prev = false;
5845         Roo.each(this.navItems, function(v){
5846             if (v == item) {
5847                 return ;
5848             }
5849             if (v.isActive()) {
5850                 v.setActive(false, true);
5851                 prev = v;
5852                 
5853             }
5854             
5855         });
5856
5857         item.setActive(true, true);
5858         this.fireEvent('changed', this, item, prev);
5859         
5860         
5861     },
5862     /**
5863     * gets the active Navigation item
5864     * @return {Roo.bootstrap.NavItem} the current navitem
5865     */
5866     getActive : function()
5867     {
5868         
5869         var prev = false;
5870         Roo.each(this.navItems, function(v){
5871             
5872             if (v.isActive()) {
5873                 prev = v;
5874                 
5875             }
5876             
5877         });
5878         return prev;
5879     },
5880     
5881     indexOfNav : function()
5882     {
5883         
5884         var prev = false;
5885         Roo.each(this.navItems, function(v,i){
5886             
5887             if (v.isActive()) {
5888                 prev = i;
5889                 
5890             }
5891             
5892         });
5893         return prev;
5894     },
5895     /**
5896     * adds a Navigation item
5897     * @param {Roo.bootstrap.NavItem} the navitem to add
5898     */
5899     addItem : function(cfg)
5900     {
5901         if (this.form && Roo.bootstrap.version == 4) {
5902             cfg.tag = 'div';
5903         }
5904         var cn = new Roo.bootstrap.NavItem(cfg);
5905         this.register(cn);
5906         cn.parentId = this.id;
5907         cn.onRender(this.el, null);
5908         return cn;
5909     },
5910     /**
5911     * register a Navigation item
5912     * @param {Roo.bootstrap.NavItem} the navitem to add
5913     */
5914     register : function(item)
5915     {
5916         this.navItems.push( item);
5917         item.navId = this.navId;
5918     
5919     },
5920     
5921     /**
5922     * clear all the Navigation item
5923     */
5924    
5925     clearAll : function()
5926     {
5927         this.navItems = [];
5928         this.el.dom.innerHTML = '';
5929     },
5930     
5931     getNavItem: function(tabId)
5932     {
5933         var ret = false;
5934         Roo.each(this.navItems, function(e) {
5935             if (e.tabId == tabId) {
5936                ret =  e;
5937                return false;
5938             }
5939             return true;
5940             
5941         });
5942         return ret;
5943     },
5944     
5945     setActiveNext : function()
5946     {
5947         var i = this.indexOfNav(this.getActive());
5948         if (i > this.navItems.length) {
5949             return;
5950         }
5951         this.setActiveItem(this.navItems[i+1]);
5952     },
5953     setActivePrev : function()
5954     {
5955         var i = this.indexOfNav(this.getActive());
5956         if (i  < 1) {
5957             return;
5958         }
5959         this.setActiveItem(this.navItems[i-1]);
5960     },
5961     clearWasActive : function(except) {
5962         Roo.each(this.navItems, function(e) {
5963             if (e.tabId != except.tabId && e.was_active) {
5964                e.was_active = false;
5965                return false;
5966             }
5967             return true;
5968             
5969         });
5970     },
5971     getWasActive : function ()
5972     {
5973         var r = false;
5974         Roo.each(this.navItems, function(e) {
5975             if (e.was_active) {
5976                r = e;
5977                return false;
5978             }
5979             return true;
5980             
5981         });
5982         return r;
5983     }
5984     
5985     
5986 });
5987
5988  
5989 Roo.apply(Roo.bootstrap.NavGroup, {
5990     
5991     groups: {},
5992      /**
5993     * register a Navigation Group
5994     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5995     */
5996     register : function(navgrp)
5997     {
5998         this.groups[navgrp.navId] = navgrp;
5999         
6000     },
6001     /**
6002     * fetch a Navigation Group based on the navigation ID
6003     * @param {string} the navgroup to add
6004     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6005     */
6006     get: function(navId) {
6007         if (typeof(this.groups[navId]) == 'undefined') {
6008             return false;
6009             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6010         }
6011         return this.groups[navId] ;
6012     }
6013     
6014     
6015     
6016 });
6017
6018  /*
6019  * - LGPL
6020  *
6021  * row
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.NavItem
6027  * @extends Roo.bootstrap.Component
6028  * Bootstrap Navbar.NavItem class
6029  * @cfg {String} href  link to
6030  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6031  * @cfg {Boolean} button_outline show and outlined button
6032  * @cfg {String} html content of button
6033  * @cfg {String} badge text inside badge
6034  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6035  * @cfg {String} glyphicon DEPRICATED - use fa
6036  * @cfg {String} icon DEPRICATED - use fa
6037  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6038  * @cfg {Boolean} active Is item active
6039  * @cfg {Boolean} disabled Is item disabled
6040  * @cfg {String} linkcls  Link Class
6041  * @cfg {Boolean} preventDefault (true | false) default false
6042  * @cfg {String} tabId the tab that this item activates.
6043  * @cfg {String} tagtype (a|span) render as a href or span?
6044  * @cfg {Boolean} animateRef (true|false) link to element default false  
6045   
6046  * @constructor
6047  * Create a new Navbar Item
6048  * @param {Object} config The config object
6049  */
6050 Roo.bootstrap.NavItem = function(config){
6051     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6052     this.addEvents({
6053         // raw events
6054         /**
6055          * @event click
6056          * The raw click event for the entire grid.
6057          * @param {Roo.EventObject} e
6058          */
6059         "click" : true,
6060          /**
6061             * @event changed
6062             * Fires when the active item active state changes
6063             * @param {Roo.bootstrap.NavItem} this
6064             * @param {boolean} state the new state
6065              
6066          */
6067         'changed': true,
6068         /**
6069             * @event scrollto
6070             * Fires when scroll to element
6071             * @param {Roo.bootstrap.NavItem} this
6072             * @param {Object} options
6073             * @param {Roo.EventObject} e
6074              
6075          */
6076         'scrollto': true
6077     });
6078    
6079 };
6080
6081 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6082     
6083     href: false,
6084     html: '',
6085     badge: '',
6086     icon: false,
6087     fa : false,
6088     glyphicon: false,
6089     active: false,
6090     preventDefault : false,
6091     tabId : false,
6092     tagtype : 'a',
6093     tag: 'li',
6094     disabled : false,
6095     animateRef : false,
6096     was_active : false,
6097     button_weight : '',
6098     button_outline : false,
6099     linkcls : '',
6100     navLink: false,
6101     
6102     getAutoCreate : function(){
6103          
6104         var cfg = {
6105             tag: this.tag,
6106             cls: 'nav-item'
6107         };
6108         
6109         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6110         
6111         if (this.active) {
6112             cfg.cls +=  ' active' ;
6113         }
6114         if (this.disabled) {
6115             cfg.cls += ' disabled';
6116         }
6117         
6118         // BS4 only?
6119         if (this.button_weight.length) {
6120             cfg.tag = this.href ? 'a' : 'button';
6121             cfg.html = this.html || '';
6122             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6123             if (this.href) {
6124                 cfg.href = this.href;
6125             }
6126             if (this.fa) {
6127                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6128             }
6129             
6130             // menu .. should add dropdown-menu class - so no need for carat..
6131             
6132             if (this.badge !== '') {
6133                  
6134                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6135             }
6136             return cfg;
6137         }
6138         
6139         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6140             cfg.cn = [
6141                 {
6142                     tag: this.tagtype,
6143                     href : this.href || "#",
6144                     html: this.html || ''
6145                 }
6146             ];
6147             if (this.tagtype == 'a') {
6148                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6149         
6150             }
6151             if (this.icon) {
6152                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6153             }
6154             if (this.fa) {
6155                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6156             }
6157             if(this.glyphicon) {
6158                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6159             }
6160             
6161             if (this.menu) {
6162                 
6163                 cfg.cn[0].html += " <span class='caret'></span>";
6164              
6165             }
6166             
6167             if (this.badge !== '') {
6168                  
6169                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6170             }
6171         }
6172         
6173         
6174         
6175         return cfg;
6176     },
6177     onRender : function(ct, position)
6178     {
6179        // Roo.log("Call onRender: " + this.xtype);
6180         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6181             this.tag = 'div';
6182         }
6183         
6184         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6185         this.navLink = this.el.select('.nav-link',true).first();
6186         return ret;
6187     },
6188       
6189     
6190     initEvents: function() 
6191     {
6192         if (typeof (this.menu) != 'undefined') {
6193             this.menu.parentType = this.xtype;
6194             this.menu.triggerEl = this.el;
6195             this.menu = this.addxtype(Roo.apply({}, this.menu));
6196         }
6197         
6198         this.el.on('click', this.onClick, this);
6199         
6200         //if(this.tagtype == 'span'){
6201         //    this.el.select('span',true).on('click', this.onClick, this);
6202         //}
6203        
6204         // at this point parent should be available..
6205         this.parent().register(this);
6206     },
6207     
6208     onClick : function(e)
6209     {
6210         if (e.getTarget('.dropdown-menu-item')) {
6211             // did you click on a menu itemm.... - then don't trigger onclick..
6212             return;
6213         }
6214         
6215         if(
6216                 this.preventDefault || 
6217                 this.href == '#' 
6218         ){
6219             Roo.log("NavItem - prevent Default?");
6220             e.preventDefault();
6221         }
6222         
6223         if (this.disabled) {
6224             return;
6225         }
6226         
6227         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6228         if (tg && tg.transition) {
6229             Roo.log("waiting for the transitionend");
6230             return;
6231         }
6232         
6233         
6234         
6235         //Roo.log("fire event clicked");
6236         if(this.fireEvent('click', this, e) === false){
6237             return;
6238         };
6239         
6240         if(this.tagtype == 'span'){
6241             return;
6242         }
6243         
6244         //Roo.log(this.href);
6245         var ael = this.el.select('a',true).first();
6246         //Roo.log(ael);
6247         
6248         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6249             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6250             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6251                 return; // ignore... - it's a 'hash' to another page.
6252             }
6253             Roo.log("NavItem - prevent Default?");
6254             e.preventDefault();
6255             this.scrollToElement(e);
6256         }
6257         
6258         
6259         var p =  this.parent();
6260    
6261         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6262             if (typeof(p.setActiveItem) !== 'undefined') {
6263                 p.setActiveItem(this);
6264             }
6265         }
6266         
6267         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6268         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6269             // remove the collapsed menu expand...
6270             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6271         }
6272     },
6273     
6274     isActive: function () {
6275         return this.active
6276     },
6277     setActive : function(state, fire, is_was_active)
6278     {
6279         if (this.active && !state && this.navId) {
6280             this.was_active = true;
6281             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6282             if (nv) {
6283                 nv.clearWasActive(this);
6284             }
6285             
6286         }
6287         this.active = state;
6288         
6289         if (!state ) {
6290             this.el.removeClass('active');
6291             this.navLink ? this.navLink.removeClass('active') : false;
6292         } else if (!this.el.hasClass('active')) {
6293             
6294             this.el.addClass('active');
6295             if (Roo.bootstrap.version == 4 && this.navLink ) {
6296                 this.navLink.addClass('active');
6297             }
6298             
6299         }
6300         if (fire) {
6301             this.fireEvent('changed', this, state);
6302         }
6303         
6304         // show a panel if it's registered and related..
6305         
6306         if (!this.navId || !this.tabId || !state || is_was_active) {
6307             return;
6308         }
6309         
6310         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6311         if (!tg) {
6312             return;
6313         }
6314         var pan = tg.getPanelByName(this.tabId);
6315         if (!pan) {
6316             return;
6317         }
6318         // if we can not flip to new panel - go back to old nav highlight..
6319         if (false == tg.showPanel(pan)) {
6320             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6321             if (nv) {
6322                 var onav = nv.getWasActive();
6323                 if (onav) {
6324                     onav.setActive(true, false, true);
6325                 }
6326             }
6327             
6328         }
6329         
6330         
6331         
6332     },
6333      // this should not be here...
6334     setDisabled : function(state)
6335     {
6336         this.disabled = state;
6337         if (!state ) {
6338             this.el.removeClass('disabled');
6339         } else if (!this.el.hasClass('disabled')) {
6340             this.el.addClass('disabled');
6341         }
6342         
6343     },
6344     
6345     /**
6346      * Fetch the element to display the tooltip on.
6347      * @return {Roo.Element} defaults to this.el
6348      */
6349     tooltipEl : function()
6350     {
6351         return this.el.select('' + this.tagtype + '', true).first();
6352     },
6353     
6354     scrollToElement : function(e)
6355     {
6356         var c = document.body;
6357         
6358         /*
6359          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6360          */
6361         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6362             c = document.documentElement;
6363         }
6364         
6365         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6366         
6367         if(!target){
6368             return;
6369         }
6370
6371         var o = target.calcOffsetsTo(c);
6372         
6373         var options = {
6374             target : target,
6375             value : o[1]
6376         };
6377         
6378         this.fireEvent('scrollto', this, options, e);
6379         
6380         Roo.get(c).scrollTo('top', options.value, true);
6381         
6382         return;
6383     }
6384 });
6385  
6386
6387  /*
6388  * - LGPL
6389  *
6390  * sidebar item
6391  *
6392  *  li
6393  *    <span> icon </span>
6394  *    <span> text </span>
6395  *    <span>badge </span>
6396  */
6397
6398 /**
6399  * @class Roo.bootstrap.NavSidebarItem
6400  * @extends Roo.bootstrap.NavItem
6401  * Bootstrap Navbar.NavSidebarItem class
6402  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6403  * {Boolean} open is the menu open
6404  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6405  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6406  * {String} buttonSize (sm|md|lg)the extra classes for the button
6407  * {Boolean} showArrow show arrow next to the text (default true)
6408  * @constructor
6409  * Create a new Navbar Button
6410  * @param {Object} config The config object
6411  */
6412 Roo.bootstrap.NavSidebarItem = function(config){
6413     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6414     this.addEvents({
6415         // raw events
6416         /**
6417          * @event click
6418          * The raw click event for the entire grid.
6419          * @param {Roo.EventObject} e
6420          */
6421         "click" : true,
6422          /**
6423             * @event changed
6424             * Fires when the active item active state changes
6425             * @param {Roo.bootstrap.NavSidebarItem} this
6426             * @param {boolean} state the new state
6427              
6428          */
6429         'changed': true
6430     });
6431    
6432 };
6433
6434 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6435     
6436     badgeWeight : 'default',
6437     
6438     open: false,
6439     
6440     buttonView : false,
6441     
6442     buttonWeight : 'default',
6443     
6444     buttonSize : 'md',
6445     
6446     showArrow : true,
6447     
6448     getAutoCreate : function(){
6449         
6450         
6451         var a = {
6452                 tag: 'a',
6453                 href : this.href || '#',
6454                 cls: '',
6455                 html : '',
6456                 cn : []
6457         };
6458         
6459         if(this.buttonView){
6460             a = {
6461                 tag: 'button',
6462                 href : this.href || '#',
6463                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6464                 html : this.html,
6465                 cn : []
6466             };
6467         }
6468         
6469         var cfg = {
6470             tag: 'li',
6471             cls: '',
6472             cn: [ a ]
6473         };
6474         
6475         if (this.active) {
6476             cfg.cls += ' active';
6477         }
6478         
6479         if (this.disabled) {
6480             cfg.cls += ' disabled';
6481         }
6482         if (this.open) {
6483             cfg.cls += ' open x-open';
6484         }
6485         // left icon..
6486         if (this.glyphicon || this.icon) {
6487             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6488             a.cn.push({ tag : 'i', cls : c }) ;
6489         }
6490         
6491         if(!this.buttonView){
6492             var span = {
6493                 tag: 'span',
6494                 html : this.html || ''
6495             };
6496
6497             a.cn.push(span);
6498             
6499         }
6500         
6501         if (this.badge !== '') {
6502             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6503         }
6504         
6505         if (this.menu) {
6506             
6507             if(this.showArrow){
6508                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6509             }
6510             
6511             a.cls += ' dropdown-toggle treeview' ;
6512         }
6513         
6514         return cfg;
6515     },
6516     
6517     initEvents : function()
6518     { 
6519         if (typeof (this.menu) != 'undefined') {
6520             this.menu.parentType = this.xtype;
6521             this.menu.triggerEl = this.el;
6522             this.menu = this.addxtype(Roo.apply({}, this.menu));
6523         }
6524         
6525         this.el.on('click', this.onClick, this);
6526         
6527         if(this.badge !== ''){
6528             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6529         }
6530         
6531     },
6532     
6533     onClick : function(e)
6534     {
6535         if(this.disabled){
6536             e.preventDefault();
6537             return;
6538         }
6539         
6540         if(this.preventDefault){
6541             e.preventDefault();
6542         }
6543         
6544         this.fireEvent('click', this, e);
6545     },
6546     
6547     disable : function()
6548     {
6549         this.setDisabled(true);
6550     },
6551     
6552     enable : function()
6553     {
6554         this.setDisabled(false);
6555     },
6556     
6557     setDisabled : function(state)
6558     {
6559         if(this.disabled == state){
6560             return;
6561         }
6562         
6563         this.disabled = state;
6564         
6565         if (state) {
6566             this.el.addClass('disabled');
6567             return;
6568         }
6569         
6570         this.el.removeClass('disabled');
6571         
6572         return;
6573     },
6574     
6575     setActive : function(state)
6576     {
6577         if(this.active == state){
6578             return;
6579         }
6580         
6581         this.active = state;
6582         
6583         if (state) {
6584             this.el.addClass('active');
6585             return;
6586         }
6587         
6588         this.el.removeClass('active');
6589         
6590         return;
6591     },
6592     
6593     isActive: function () 
6594     {
6595         return this.active;
6596     },
6597     
6598     setBadge : function(str)
6599     {
6600         if(!this.badgeEl){
6601             return;
6602         }
6603         
6604         this.badgeEl.dom.innerHTML = str;
6605     }
6606     
6607    
6608      
6609  
6610 });
6611  
6612
6613  /*
6614  * - LGPL
6615  *
6616  *  Breadcrumb Nav
6617  * 
6618  */
6619 Roo.namespace('Roo.bootstrap.breadcrumb');
6620
6621
6622 /**
6623  * @class Roo.bootstrap.breadcrumb.Nav
6624  * @extends Roo.bootstrap.Component
6625  * Bootstrap Breadcrumb Nav Class
6626  *  
6627  * @children Roo.bootstrap.breadcrumb.Item
6628  * 
6629  * @constructor
6630  * Create a new breadcrumb.Nav
6631  * @param {Object} config The config object
6632  */
6633
6634
6635 Roo.bootstrap.breadcrumb.Nav = function(config){
6636     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6637     
6638     
6639 };
6640
6641 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6642     
6643     getAutoCreate : function()
6644     {
6645
6646         var cfg = {
6647             tag: 'nav',
6648             cn : [
6649                 {
6650                     tag : 'ol',
6651                     cls : 'breadcrumb'
6652                 }
6653             ]
6654             
6655         };
6656           
6657         return cfg;
6658     },
6659     
6660     initEvents: function()
6661     {
6662         this.olEl = this.el.select('ol',true).first();    
6663     },
6664     getChildContainer : function()
6665     {
6666         return this.olEl;  
6667     }
6668     
6669 });
6670
6671  /*
6672  * - LGPL
6673  *
6674  *  Breadcrumb Item
6675  * 
6676  */
6677
6678
6679 /**
6680  * @class Roo.bootstrap.breadcrumb.Nav
6681  * @extends Roo.bootstrap.Component
6682  * Bootstrap Breadcrumb Nav Class
6683  *  
6684  * @children Roo.bootstrap.breadcrumb.Component
6685  * @cfg {String} html the content of the link.
6686  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6687  * @cfg {Boolean} active is it active
6688
6689  * 
6690  * @constructor
6691  * Create a new breadcrumb.Nav
6692  * @param {Object} config The config object
6693  */
6694
6695 Roo.bootstrap.breadcrumb.Item = function(config){
6696     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6697     this.addEvents({
6698         // img events
6699         /**
6700          * @event click
6701          * The img click event for the img.
6702          * @param {Roo.EventObject} e
6703          */
6704         "click" : true
6705     });
6706     
6707 };
6708
6709 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6710     
6711     href: false,
6712     html : '',
6713     
6714     getAutoCreate : function()
6715     {
6716
6717         var cfg = {
6718             tag: 'li',
6719             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6720         };
6721         if (this.href !== false) {
6722             cfg.cn = [{
6723                 tag : 'a',
6724                 href : this.href,
6725                 html : this.html
6726             }];
6727         } else {
6728             cfg.html = this.html;
6729         }
6730         
6731         return cfg;
6732     },
6733     
6734     initEvents: function()
6735     {
6736         if (this.href) {
6737             this.el.select('a', true).first().on('click',this.onClick, this)
6738         }
6739         
6740     },
6741     onClick : function(e)
6742     {
6743         e.preventDefault();
6744         this.fireEvent('click',this,  e);
6745     }
6746     
6747 });
6748
6749  /*
6750  * - LGPL
6751  *
6752  * row
6753  * 
6754  */
6755
6756 /**
6757  * @class Roo.bootstrap.Row
6758  * @extends Roo.bootstrap.Component
6759  * Bootstrap Row class (contains columns...)
6760  * 
6761  * @constructor
6762  * Create a new Row
6763  * @param {Object} config The config object
6764  */
6765
6766 Roo.bootstrap.Row = function(config){
6767     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6768 };
6769
6770 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6771     
6772     getAutoCreate : function(){
6773        return {
6774             cls: 'row clearfix'
6775        };
6776     }
6777     
6778     
6779 });
6780
6781  
6782
6783  /*
6784  * - LGPL
6785  *
6786  * pagination
6787  * 
6788  */
6789
6790 /**
6791  * @class Roo.bootstrap.Pagination
6792  * @extends Roo.bootstrap.Component
6793  * Bootstrap Pagination class
6794  * @cfg {String} size xs | sm | md | lg
6795  * @cfg {Boolean} inverse false | true
6796  * 
6797  * @constructor
6798  * Create a new Pagination
6799  * @param {Object} config The config object
6800  */
6801
6802 Roo.bootstrap.Pagination = function(config){
6803     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6804 };
6805
6806 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6807     
6808     cls: false,
6809     size: false,
6810     inverse: false,
6811     
6812     getAutoCreate : function(){
6813         var cfg = {
6814             tag: 'ul',
6815                 cls: 'pagination'
6816         };
6817         if (this.inverse) {
6818             cfg.cls += ' inverse';
6819         }
6820         if (this.html) {
6821             cfg.html=this.html;
6822         }
6823         if (this.cls) {
6824             cfg.cls += " " + this.cls;
6825         }
6826         return cfg;
6827     }
6828    
6829 });
6830
6831  
6832
6833  /*
6834  * - LGPL
6835  *
6836  * Pagination item
6837  * 
6838  */
6839
6840
6841 /**
6842  * @class Roo.bootstrap.PaginationItem
6843  * @extends Roo.bootstrap.Component
6844  * Bootstrap PaginationItem class
6845  * @cfg {String} html text
6846  * @cfg {String} href the link
6847  * @cfg {Boolean} preventDefault (true | false) default true
6848  * @cfg {Boolean} active (true | false) default false
6849  * @cfg {Boolean} disabled default false
6850  * 
6851  * 
6852  * @constructor
6853  * Create a new PaginationItem
6854  * @param {Object} config The config object
6855  */
6856
6857
6858 Roo.bootstrap.PaginationItem = function(config){
6859     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6860     this.addEvents({
6861         // raw events
6862         /**
6863          * @event click
6864          * The raw click event for the entire grid.
6865          * @param {Roo.EventObject} e
6866          */
6867         "click" : true
6868     });
6869 };
6870
6871 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6872     
6873     href : false,
6874     html : false,
6875     preventDefault: true,
6876     active : false,
6877     cls : false,
6878     disabled: false,
6879     
6880     getAutoCreate : function(){
6881         var cfg= {
6882             tag: 'li',
6883             cn: [
6884                 {
6885                     tag : 'a',
6886                     href : this.href ? this.href : '#',
6887                     html : this.html ? this.html : ''
6888                 }
6889             ]
6890         };
6891         
6892         if(this.cls){
6893             cfg.cls = this.cls;
6894         }
6895         
6896         if(this.disabled){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6898         }
6899         
6900         if(this.active){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6902         }
6903         
6904         return cfg;
6905     },
6906     
6907     initEvents: function() {
6908         
6909         this.el.on('click', this.onClick, this);
6910         
6911     },
6912     onClick : function(e)
6913     {
6914         Roo.log('PaginationItem on click ');
6915         if(this.preventDefault){
6916             e.preventDefault();
6917         }
6918         
6919         if(this.disabled){
6920             return;
6921         }
6922         
6923         this.fireEvent('click', this, e);
6924     }
6925    
6926 });
6927
6928  
6929
6930  /*
6931  * - LGPL
6932  *
6933  * slider
6934  * 
6935  */
6936
6937
6938 /**
6939  * @class Roo.bootstrap.Slider
6940  * @extends Roo.bootstrap.Component
6941  * Bootstrap Slider class
6942  *    
6943  * @constructor
6944  * Create a new Slider
6945  * @param {Object} config The config object
6946  */
6947
6948 Roo.bootstrap.Slider = function(config){
6949     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6950 };
6951
6952 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6953     
6954     getAutoCreate : function(){
6955         
6956         var cfg = {
6957             tag: 'div',
6958             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6959             cn: [
6960                 {
6961                     tag: 'a',
6962                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6963                 }
6964             ]
6965         };
6966         
6967         return cfg;
6968     }
6969    
6970 });
6971
6972  /*
6973  * Based on:
6974  * Ext JS Library 1.1.1
6975  * Copyright(c) 2006-2007, Ext JS, LLC.
6976  *
6977  * Originally Released Under LGPL - original licence link has changed is not relivant.
6978  *
6979  * Fork - LGPL
6980  * <script type="text/javascript">
6981  */
6982  
6983
6984 /**
6985  * @class Roo.grid.ColumnModel
6986  * @extends Roo.util.Observable
6987  * This is the default implementation of a ColumnModel used by the Grid. It defines
6988  * the columns in the grid.
6989  * <br>Usage:<br>
6990  <pre><code>
6991  var colModel = new Roo.grid.ColumnModel([
6992         {header: "Ticker", width: 60, sortable: true, locked: true},
6993         {header: "Company Name", width: 150, sortable: true},
6994         {header: "Market Cap.", width: 100, sortable: true},
6995         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6996         {header: "Employees", width: 100, sortable: true, resizable: false}
6997  ]);
6998  </code></pre>
6999  * <p>
7000  
7001  * The config options listed for this class are options which may appear in each
7002  * individual column definition.
7003  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7004  * @constructor
7005  * @param {Object} config An Array of column config objects. See this class's
7006  * config objects for details.
7007 */
7008 Roo.grid.ColumnModel = function(config){
7009         /**
7010      * The config passed into the constructor
7011      */
7012     this.config = config;
7013     this.lookup = {};
7014
7015     // if no id, create one
7016     // if the column does not have a dataIndex mapping,
7017     // map it to the order it is in the config
7018     for(var i = 0, len = config.length; i < len; i++){
7019         var c = config[i];
7020         if(typeof c.dataIndex == "undefined"){
7021             c.dataIndex = i;
7022         }
7023         if(typeof c.renderer == "string"){
7024             c.renderer = Roo.util.Format[c.renderer];
7025         }
7026         if(typeof c.id == "undefined"){
7027             c.id = Roo.id();
7028         }
7029         if(c.editor && c.editor.xtype){
7030             c.editor  = Roo.factory(c.editor, Roo.grid);
7031         }
7032         if(c.editor && c.editor.isFormField){
7033             c.editor = new Roo.grid.GridEditor(c.editor);
7034         }
7035         this.lookup[c.id] = c;
7036     }
7037
7038     /**
7039      * The width of columns which have no width specified (defaults to 100)
7040      * @type Number
7041      */
7042     this.defaultWidth = 100;
7043
7044     /**
7045      * Default sortable of columns which have no sortable specified (defaults to false)
7046      * @type Boolean
7047      */
7048     this.defaultSortable = false;
7049
7050     this.addEvents({
7051         /**
7052              * @event widthchange
7053              * Fires when the width of a column changes.
7054              * @param {ColumnModel} this
7055              * @param {Number} columnIndex The column index
7056              * @param {Number} newWidth The new width
7057              */
7058             "widthchange": true,
7059         /**
7060              * @event headerchange
7061              * Fires when the text of a header changes.
7062              * @param {ColumnModel} this
7063              * @param {Number} columnIndex The column index
7064              * @param {Number} newText The new header text
7065              */
7066             "headerchange": true,
7067         /**
7068              * @event hiddenchange
7069              * Fires when a column is hidden or "unhidden".
7070              * @param {ColumnModel} this
7071              * @param {Number} columnIndex The column index
7072              * @param {Boolean} hidden true if hidden, false otherwise
7073              */
7074             "hiddenchange": true,
7075             /**
7076          * @event columnmoved
7077          * Fires when a column is moved.
7078          * @param {ColumnModel} this
7079          * @param {Number} oldIndex
7080          * @param {Number} newIndex
7081          */
7082         "columnmoved" : true,
7083         /**
7084          * @event columlockchange
7085          * Fires when a column's locked state is changed
7086          * @param {ColumnModel} this
7087          * @param {Number} colIndex
7088          * @param {Boolean} locked true if locked
7089          */
7090         "columnlockchange" : true
7091     });
7092     Roo.grid.ColumnModel.superclass.constructor.call(this);
7093 };
7094 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7095     /**
7096      * @cfg {String} header The header text to display in the Grid view.
7097      */
7098     /**
7099      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7100      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7101      * specified, the column's index is used as an index into the Record's data Array.
7102      */
7103     /**
7104      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7105      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7106      */
7107     /**
7108      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7109      * Defaults to the value of the {@link #defaultSortable} property.
7110      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7111      */
7112     /**
7113      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7114      */
7115     /**
7116      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7117      */
7118     /**
7119      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7120      */
7121     /**
7122      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7123      */
7124     /**
7125      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7126      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7127      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7128      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7129      */
7130        /**
7131      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7132      */
7133     /**
7134      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7135      */
7136     /**
7137      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7138      */
7139     /**
7140      * @cfg {String} cursor (Optional)
7141      */
7142     /**
7143      * @cfg {String} tooltip (Optional)
7144      */
7145     /**
7146      * @cfg {Number} xs (Optional)
7147      */
7148     /**
7149      * @cfg {Number} sm (Optional)
7150      */
7151     /**
7152      * @cfg {Number} md (Optional)
7153      */
7154     /**
7155      * @cfg {Number} lg (Optional)
7156      */
7157     /**
7158      * Returns the id of the column at the specified index.
7159      * @param {Number} index The column index
7160      * @return {String} the id
7161      */
7162     getColumnId : function(index){
7163         return this.config[index].id;
7164     },
7165
7166     /**
7167      * Returns the column for a specified id.
7168      * @param {String} id The column id
7169      * @return {Object} the column
7170      */
7171     getColumnById : function(id){
7172         return this.lookup[id];
7173     },
7174
7175     
7176     /**
7177      * Returns the column for a specified dataIndex.
7178      * @param {String} dataIndex The column dataIndex
7179      * @return {Object|Boolean} the column or false if not found
7180      */
7181     getColumnByDataIndex: function(dataIndex){
7182         var index = this.findColumnIndex(dataIndex);
7183         return index > -1 ? this.config[index] : false;
7184     },
7185     
7186     /**
7187      * Returns the index for a specified column id.
7188      * @param {String} id The column id
7189      * @return {Number} the index, or -1 if not found
7190      */
7191     getIndexById : function(id){
7192         for(var i = 0, len = this.config.length; i < len; i++){
7193             if(this.config[i].id == id){
7194                 return i;
7195             }
7196         }
7197         return -1;
7198     },
7199     
7200     /**
7201      * Returns the index for a specified column dataIndex.
7202      * @param {String} dataIndex The column dataIndex
7203      * @return {Number} the index, or -1 if not found
7204      */
7205     
7206     findColumnIndex : function(dataIndex){
7207         for(var i = 0, len = this.config.length; i < len; i++){
7208             if(this.config[i].dataIndex == dataIndex){
7209                 return i;
7210             }
7211         }
7212         return -1;
7213     },
7214     
7215     
7216     moveColumn : function(oldIndex, newIndex){
7217         var c = this.config[oldIndex];
7218         this.config.splice(oldIndex, 1);
7219         this.config.splice(newIndex, 0, c);
7220         this.dataMap = null;
7221         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7222     },
7223
7224     isLocked : function(colIndex){
7225         return this.config[colIndex].locked === true;
7226     },
7227
7228     setLocked : function(colIndex, value, suppressEvent){
7229         if(this.isLocked(colIndex) == value){
7230             return;
7231         }
7232         this.config[colIndex].locked = value;
7233         if(!suppressEvent){
7234             this.fireEvent("columnlockchange", this, colIndex, value);
7235         }
7236     },
7237
7238     getTotalLockedWidth : function(){
7239         var totalWidth = 0;
7240         for(var i = 0; i < this.config.length; i++){
7241             if(this.isLocked(i) && !this.isHidden(i)){
7242                 this.totalWidth += this.getColumnWidth(i);
7243             }
7244         }
7245         return totalWidth;
7246     },
7247
7248     getLockedCount : function(){
7249         for(var i = 0, len = this.config.length; i < len; i++){
7250             if(!this.isLocked(i)){
7251                 return i;
7252             }
7253         }
7254         
7255         return this.config.length;
7256     },
7257
7258     /**
7259      * Returns the number of columns.
7260      * @return {Number}
7261      */
7262     getColumnCount : function(visibleOnly){
7263         if(visibleOnly === true){
7264             var c = 0;
7265             for(var i = 0, len = this.config.length; i < len; i++){
7266                 if(!this.isHidden(i)){
7267                     c++;
7268                 }
7269             }
7270             return c;
7271         }
7272         return this.config.length;
7273     },
7274
7275     /**
7276      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7277      * @param {Function} fn
7278      * @param {Object} scope (optional)
7279      * @return {Array} result
7280      */
7281     getColumnsBy : function(fn, scope){
7282         var r = [];
7283         for(var i = 0, len = this.config.length; i < len; i++){
7284             var c = this.config[i];
7285             if(fn.call(scope||this, c, i) === true){
7286                 r[r.length] = c;
7287             }
7288         }
7289         return r;
7290     },
7291
7292     /**
7293      * Returns true if the specified column is sortable.
7294      * @param {Number} col The column index
7295      * @return {Boolean}
7296      */
7297     isSortable : function(col){
7298         if(typeof this.config[col].sortable == "undefined"){
7299             return this.defaultSortable;
7300         }
7301         return this.config[col].sortable;
7302     },
7303
7304     /**
7305      * Returns the rendering (formatting) function defined for the column.
7306      * @param {Number} col The column index.
7307      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7308      */
7309     getRenderer : function(col){
7310         if(!this.config[col].renderer){
7311             return Roo.grid.ColumnModel.defaultRenderer;
7312         }
7313         return this.config[col].renderer;
7314     },
7315
7316     /**
7317      * Sets the rendering (formatting) function for a column.
7318      * @param {Number} col The column index
7319      * @param {Function} fn The function to use to process the cell's raw data
7320      * to return HTML markup for the grid view. The render function is called with
7321      * the following parameters:<ul>
7322      * <li>Data value.</li>
7323      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7324      * <li>css A CSS style string to apply to the table cell.</li>
7325      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7326      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7327      * <li>Row index</li>
7328      * <li>Column index</li>
7329      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7330      */
7331     setRenderer : function(col, fn){
7332         this.config[col].renderer = fn;
7333     },
7334
7335     /**
7336      * Returns the width for the specified column.
7337      * @param {Number} col The column index
7338      * @return {Number}
7339      */
7340     getColumnWidth : function(col){
7341         return this.config[col].width * 1 || this.defaultWidth;
7342     },
7343
7344     /**
7345      * Sets the width for a column.
7346      * @param {Number} col The column index
7347      * @param {Number} width The new width
7348      */
7349     setColumnWidth : function(col, width, suppressEvent){
7350         this.config[col].width = width;
7351         this.totalWidth = null;
7352         if(!suppressEvent){
7353              this.fireEvent("widthchange", this, col, width);
7354         }
7355     },
7356
7357     /**
7358      * Returns the total width of all columns.
7359      * @param {Boolean} includeHidden True to include hidden column widths
7360      * @return {Number}
7361      */
7362     getTotalWidth : function(includeHidden){
7363         if(!this.totalWidth){
7364             this.totalWidth = 0;
7365             for(var i = 0, len = this.config.length; i < len; i++){
7366                 if(includeHidden || !this.isHidden(i)){
7367                     this.totalWidth += this.getColumnWidth(i);
7368                 }
7369             }
7370         }
7371         return this.totalWidth;
7372     },
7373
7374     /**
7375      * Returns the header for the specified column.
7376      * @param {Number} col The column index
7377      * @return {String}
7378      */
7379     getColumnHeader : function(col){
7380         return this.config[col].header;
7381     },
7382
7383     /**
7384      * Sets the header for a column.
7385      * @param {Number} col The column index
7386      * @param {String} header The new header
7387      */
7388     setColumnHeader : function(col, header){
7389         this.config[col].header = header;
7390         this.fireEvent("headerchange", this, col, header);
7391     },
7392
7393     /**
7394      * Returns the tooltip for the specified column.
7395      * @param {Number} col The column index
7396      * @return {String}
7397      */
7398     getColumnTooltip : function(col){
7399             return this.config[col].tooltip;
7400     },
7401     /**
7402      * Sets the tooltip for a column.
7403      * @param {Number} col The column index
7404      * @param {String} tooltip The new tooltip
7405      */
7406     setColumnTooltip : function(col, tooltip){
7407             this.config[col].tooltip = tooltip;
7408     },
7409
7410     /**
7411      * Returns the dataIndex for the specified column.
7412      * @param {Number} col The column index
7413      * @return {Number}
7414      */
7415     getDataIndex : function(col){
7416         return this.config[col].dataIndex;
7417     },
7418
7419     /**
7420      * Sets the dataIndex for a column.
7421      * @param {Number} col The column index
7422      * @param {Number} dataIndex The new dataIndex
7423      */
7424     setDataIndex : function(col, dataIndex){
7425         this.config[col].dataIndex = dataIndex;
7426     },
7427
7428     
7429     
7430     /**
7431      * Returns true if the cell is editable.
7432      * @param {Number} colIndex The column index
7433      * @param {Number} rowIndex The row index - this is nto actually used..?
7434      * @return {Boolean}
7435      */
7436     isCellEditable : function(colIndex, rowIndex){
7437         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7438     },
7439
7440     /**
7441      * Returns the editor defined for the cell/column.
7442      * return false or null to disable editing.
7443      * @param {Number} colIndex The column index
7444      * @param {Number} rowIndex The row index
7445      * @return {Object}
7446      */
7447     getCellEditor : function(colIndex, rowIndex){
7448         return this.config[colIndex].editor;
7449     },
7450
7451     /**
7452      * Sets if a column is editable.
7453      * @param {Number} col The column index
7454      * @param {Boolean} editable True if the column is editable
7455      */
7456     setEditable : function(col, editable){
7457         this.config[col].editable = editable;
7458     },
7459
7460
7461     /**
7462      * Returns true if the column is hidden.
7463      * @param {Number} colIndex The column index
7464      * @return {Boolean}
7465      */
7466     isHidden : function(colIndex){
7467         return this.config[colIndex].hidden;
7468     },
7469
7470
7471     /**
7472      * Returns true if the column width cannot be changed
7473      */
7474     isFixed : function(colIndex){
7475         return this.config[colIndex].fixed;
7476     },
7477
7478     /**
7479      * Returns true if the column can be resized
7480      * @return {Boolean}
7481      */
7482     isResizable : function(colIndex){
7483         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7484     },
7485     /**
7486      * Sets if a column is hidden.
7487      * @param {Number} colIndex The column index
7488      * @param {Boolean} hidden True if the column is hidden
7489      */
7490     setHidden : function(colIndex, hidden){
7491         this.config[colIndex].hidden = hidden;
7492         this.totalWidth = null;
7493         this.fireEvent("hiddenchange", this, colIndex, hidden);
7494     },
7495
7496     /**
7497      * Sets the editor for a column.
7498      * @param {Number} col The column index
7499      * @param {Object} editor The editor object
7500      */
7501     setEditor : function(col, editor){
7502         this.config[col].editor = editor;
7503     }
7504 });
7505
7506 Roo.grid.ColumnModel.defaultRenderer = function(value)
7507 {
7508     if(typeof value == "object") {
7509         return value;
7510     }
7511         if(typeof value == "string" && value.length < 1){
7512             return "&#160;";
7513         }
7514     
7515         return String.format("{0}", value);
7516 };
7517
7518 // Alias for backwards compatibility
7519 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7520 /*
7521  * Based on:
7522  * Ext JS Library 1.1.1
7523  * Copyright(c) 2006-2007, Ext JS, LLC.
7524  *
7525  * Originally Released Under LGPL - original licence link has changed is not relivant.
7526  *
7527  * Fork - LGPL
7528  * <script type="text/javascript">
7529  */
7530  
7531 /**
7532  * @class Roo.LoadMask
7533  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7534  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7535  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7536  * element's UpdateManager load indicator and will be destroyed after the initial load.
7537  * @constructor
7538  * Create a new LoadMask
7539  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7540  * @param {Object} config The config object
7541  */
7542 Roo.LoadMask = function(el, config){
7543     this.el = Roo.get(el);
7544     Roo.apply(this, config);
7545     if(this.store){
7546         this.store.on('beforeload', this.onBeforeLoad, this);
7547         this.store.on('load', this.onLoad, this);
7548         this.store.on('loadexception', this.onLoadException, this);
7549         this.removeMask = false;
7550     }else{
7551         var um = this.el.getUpdateManager();
7552         um.showLoadIndicator = false; // disable the default indicator
7553         um.on('beforeupdate', this.onBeforeLoad, this);
7554         um.on('update', this.onLoad, this);
7555         um.on('failure', this.onLoad, this);
7556         this.removeMask = true;
7557     }
7558 };
7559
7560 Roo.LoadMask.prototype = {
7561     /**
7562      * @cfg {Boolean} removeMask
7563      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7564      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7565      */
7566     /**
7567      * @cfg {String} msg
7568      * The text to display in a centered loading message box (defaults to 'Loading...')
7569      */
7570     msg : 'Loading...',
7571     /**
7572      * @cfg {String} msgCls
7573      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7574      */
7575     msgCls : 'x-mask-loading',
7576
7577     /**
7578      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7579      * @type Boolean
7580      */
7581     disabled: false,
7582
7583     /**
7584      * Disables the mask to prevent it from being displayed
7585      */
7586     disable : function(){
7587        this.disabled = true;
7588     },
7589
7590     /**
7591      * Enables the mask so that it can be displayed
7592      */
7593     enable : function(){
7594         this.disabled = false;
7595     },
7596     
7597     onLoadException : function()
7598     {
7599         Roo.log(arguments);
7600         
7601         if (typeof(arguments[3]) != 'undefined') {
7602             Roo.MessageBox.alert("Error loading",arguments[3]);
7603         } 
7604         /*
7605         try {
7606             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7607                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7608             }   
7609         } catch(e) {
7610             
7611         }
7612         */
7613     
7614         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7615     },
7616     // private
7617     onLoad : function()
7618     {
7619         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7620     },
7621
7622     // private
7623     onBeforeLoad : function(){
7624         if(!this.disabled){
7625             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7626         }
7627     },
7628
7629     // private
7630     destroy : function(){
7631         if(this.store){
7632             this.store.un('beforeload', this.onBeforeLoad, this);
7633             this.store.un('load', this.onLoad, this);
7634             this.store.un('loadexception', this.onLoadException, this);
7635         }else{
7636             var um = this.el.getUpdateManager();
7637             um.un('beforeupdate', this.onBeforeLoad, this);
7638             um.un('update', this.onLoad, this);
7639             um.un('failure', this.onLoad, this);
7640         }
7641     }
7642 };/*
7643  * - LGPL
7644  *
7645  * table
7646  * 
7647  */
7648
7649 /**
7650  * @class Roo.bootstrap.Table
7651  * @extends Roo.bootstrap.Component
7652  * Bootstrap Table class
7653  * @cfg {String} cls table class
7654  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7655  * @cfg {String} bgcolor Specifies the background color for a table
7656  * @cfg {Number} border Specifies whether the table cells should have borders or not
7657  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7658  * @cfg {Number} cellspacing Specifies the space between cells
7659  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7660  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7661  * @cfg {String} sortable Specifies that the table should be sortable
7662  * @cfg {String} summary Specifies a summary of the content of a table
7663  * @cfg {Number} width Specifies the width of a table
7664  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7665  * 
7666  * @cfg {boolean} striped Should the rows be alternative striped
7667  * @cfg {boolean} bordered Add borders to the table
7668  * @cfg {boolean} hover Add hover highlighting
7669  * @cfg {boolean} condensed Format condensed
7670  * @cfg {boolean} responsive Format condensed
7671  * @cfg {Boolean} loadMask (true|false) default false
7672  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7673  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7674  * @cfg {Boolean} rowSelection (true|false) default false
7675  * @cfg {Boolean} cellSelection (true|false) default false
7676  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7677  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7678  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7679  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7680  
7681  * 
7682  * @constructor
7683  * Create a new Table
7684  * @param {Object} config The config object
7685  */
7686
7687 Roo.bootstrap.Table = function(config){
7688     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7689     
7690   
7691     
7692     // BC...
7693     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7694     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7695     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7696     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7697     
7698     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7699     if (this.sm) {
7700         this.sm.grid = this;
7701         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7702         this.sm = this.selModel;
7703         this.sm.xmodule = this.xmodule || false;
7704     }
7705     
7706     if (this.cm && typeof(this.cm.config) == 'undefined') {
7707         this.colModel = new Roo.grid.ColumnModel(this.cm);
7708         this.cm = this.colModel;
7709         this.cm.xmodule = this.xmodule || false;
7710     }
7711     if (this.store) {
7712         this.store= Roo.factory(this.store, Roo.data);
7713         this.ds = this.store;
7714         this.ds.xmodule = this.xmodule || false;
7715          
7716     }
7717     if (this.footer && this.store) {
7718         this.footer.dataSource = this.ds;
7719         this.footer = Roo.factory(this.footer);
7720     }
7721     
7722     /** @private */
7723     this.addEvents({
7724         /**
7725          * @event cellclick
7726          * Fires when a cell is clicked
7727          * @param {Roo.bootstrap.Table} this
7728          * @param {Roo.Element} el
7729          * @param {Number} rowIndex
7730          * @param {Number} columnIndex
7731          * @param {Roo.EventObject} e
7732          */
7733         "cellclick" : true,
7734         /**
7735          * @event celldblclick
7736          * Fires when a cell is double clicked
7737          * @param {Roo.bootstrap.Table} this
7738          * @param {Roo.Element} el
7739          * @param {Number} rowIndex
7740          * @param {Number} columnIndex
7741          * @param {Roo.EventObject} e
7742          */
7743         "celldblclick" : true,
7744         /**
7745          * @event rowclick
7746          * Fires when a row is clicked
7747          * @param {Roo.bootstrap.Table} this
7748          * @param {Roo.Element} el
7749          * @param {Number} rowIndex
7750          * @param {Roo.EventObject} e
7751          */
7752         "rowclick" : true,
7753         /**
7754          * @event rowdblclick
7755          * Fires when a row is double clicked
7756          * @param {Roo.bootstrap.Table} this
7757          * @param {Roo.Element} el
7758          * @param {Number} rowIndex
7759          * @param {Roo.EventObject} e
7760          */
7761         "rowdblclick" : true,
7762         /**
7763          * @event mouseover
7764          * Fires when a mouseover occur
7765          * @param {Roo.bootstrap.Table} this
7766          * @param {Roo.Element} el
7767          * @param {Number} rowIndex
7768          * @param {Number} columnIndex
7769          * @param {Roo.EventObject} e
7770          */
7771         "mouseover" : true,
7772         /**
7773          * @event mouseout
7774          * Fires when a mouseout occur
7775          * @param {Roo.bootstrap.Table} this
7776          * @param {Roo.Element} el
7777          * @param {Number} rowIndex
7778          * @param {Number} columnIndex
7779          * @param {Roo.EventObject} e
7780          */
7781         "mouseout" : true,
7782         /**
7783          * @event rowclass
7784          * Fires when a row is rendered, so you can change add a style to it.
7785          * @param {Roo.bootstrap.Table} this
7786          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7787          */
7788         'rowclass' : true,
7789           /**
7790          * @event rowsrendered
7791          * Fires when all the  rows have been rendered
7792          * @param {Roo.bootstrap.Table} this
7793          */
7794         'rowsrendered' : true,
7795         /**
7796          * @event contextmenu
7797          * The raw contextmenu event for the entire grid.
7798          * @param {Roo.EventObject} e
7799          */
7800         "contextmenu" : true,
7801         /**
7802          * @event rowcontextmenu
7803          * Fires when a row is right clicked
7804          * @param {Roo.bootstrap.Table} this
7805          * @param {Number} rowIndex
7806          * @param {Roo.EventObject} e
7807          */
7808         "rowcontextmenu" : true,
7809         /**
7810          * @event cellcontextmenu
7811          * Fires when a cell is right clicked
7812          * @param {Roo.bootstrap.Table} this
7813          * @param {Number} rowIndex
7814          * @param {Number} cellIndex
7815          * @param {Roo.EventObject} e
7816          */
7817          "cellcontextmenu" : true,
7818          /**
7819          * @event headercontextmenu
7820          * Fires when a header is right clicked
7821          * @param {Roo.bootstrap.Table} this
7822          * @param {Number} columnIndex
7823          * @param {Roo.EventObject} e
7824          */
7825         "headercontextmenu" : true
7826     });
7827 };
7828
7829 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7830     
7831     cls: false,
7832     align: false,
7833     bgcolor: false,
7834     border: false,
7835     cellpadding: false,
7836     cellspacing: false,
7837     frame: false,
7838     rules: false,
7839     sortable: false,
7840     summary: false,
7841     width: false,
7842     striped : false,
7843     scrollBody : false,
7844     bordered: false,
7845     hover:  false,
7846     condensed : false,
7847     responsive : false,
7848     sm : false,
7849     cm : false,
7850     store : false,
7851     loadMask : false,
7852     footerShow : true,
7853     headerShow : true,
7854   
7855     rowSelection : false,
7856     cellSelection : false,
7857     layout : false,
7858     
7859     // Roo.Element - the tbody
7860     mainBody: false,
7861     // Roo.Element - thead element
7862     mainHead: false,
7863     
7864     container: false, // used by gridpanel...
7865     
7866     lazyLoad : false,
7867     
7868     CSS : Roo.util.CSS,
7869     
7870     auto_hide_footer : false,
7871     
7872     getAutoCreate : function()
7873     {
7874         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7875         
7876         cfg = {
7877             tag: 'table',
7878             cls : 'table',
7879             cn : []
7880         };
7881         if (this.scrollBody) {
7882             cfg.cls += ' table-body-fixed';
7883         }    
7884         if (this.striped) {
7885             cfg.cls += ' table-striped';
7886         }
7887         
7888         if (this.hover) {
7889             cfg.cls += ' table-hover';
7890         }
7891         if (this.bordered) {
7892             cfg.cls += ' table-bordered';
7893         }
7894         if (this.condensed) {
7895             cfg.cls += ' table-condensed';
7896         }
7897         if (this.responsive) {
7898             cfg.cls += ' table-responsive';
7899         }
7900         
7901         if (this.cls) {
7902             cfg.cls+=  ' ' +this.cls;
7903         }
7904         
7905         // this lot should be simplifed...
7906         var _t = this;
7907         var cp = [
7908             'align',
7909             'bgcolor',
7910             'border',
7911             'cellpadding',
7912             'cellspacing',
7913             'frame',
7914             'rules',
7915             'sortable',
7916             'summary',
7917             'width'
7918         ].forEach(function(k) {
7919             if (_t[k]) {
7920                 cfg[k] = _t[k];
7921             }
7922         });
7923         
7924         
7925         if (this.layout) {
7926             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7927         }
7928         
7929         if(this.store || this.cm){
7930             if(this.headerShow){
7931                 cfg.cn.push(this.renderHeader());
7932             }
7933             
7934             cfg.cn.push(this.renderBody());
7935             
7936             if(this.footerShow){
7937                 cfg.cn.push(this.renderFooter());
7938             }
7939             // where does this come from?
7940             //cfg.cls+=  ' TableGrid';
7941         }
7942         
7943         return { cn : [ cfg ] };
7944     },
7945     
7946     initEvents : function()
7947     {   
7948         if(!this.store || !this.cm){
7949             return;
7950         }
7951         if (this.selModel) {
7952             this.selModel.initEvents();
7953         }
7954         
7955         
7956         //Roo.log('initEvents with ds!!!!');
7957         
7958         this.mainBody = this.el.select('tbody', true).first();
7959         this.mainHead = this.el.select('thead', true).first();
7960         this.mainFoot = this.el.select('tfoot', true).first();
7961         
7962         
7963         
7964         var _this = this;
7965         
7966         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7967             e.on('click', _this.sort, _this);
7968         });
7969         
7970         this.mainBody.on("click", this.onClick, this);
7971         this.mainBody.on("dblclick", this.onDblClick, this);
7972         
7973         // why is this done????? = it breaks dialogs??
7974         //this.parent().el.setStyle('position', 'relative');
7975         
7976         
7977         if (this.footer) {
7978             this.footer.parentId = this.id;
7979             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7980             
7981             if(this.lazyLoad){
7982                 this.el.select('tfoot tr td').first().addClass('hide');
7983             }
7984         } 
7985         
7986         if(this.loadMask) {
7987             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7988         }
7989         
7990         this.store.on('load', this.onLoad, this);
7991         this.store.on('beforeload', this.onBeforeLoad, this);
7992         this.store.on('update', this.onUpdate, this);
7993         this.store.on('add', this.onAdd, this);
7994         this.store.on("clear", this.clear, this);
7995         
7996         this.el.on("contextmenu", this.onContextMenu, this);
7997         
7998         this.mainBody.on('scroll', this.onBodyScroll, this);
7999         
8000         this.cm.on("headerchange", this.onHeaderChange, this);
8001         
8002         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8003         
8004     },
8005     
8006     onContextMenu : function(e, t)
8007     {
8008         this.processEvent("contextmenu", e);
8009     },
8010     
8011     processEvent : function(name, e)
8012     {
8013         if (name != 'touchstart' ) {
8014             this.fireEvent(name, e);    
8015         }
8016         
8017         var t = e.getTarget();
8018         
8019         var cell = Roo.get(t);
8020         
8021         if(!cell){
8022             return;
8023         }
8024         
8025         if(cell.findParent('tfoot', false, true)){
8026             return;
8027         }
8028         
8029         if(cell.findParent('thead', false, true)){
8030             
8031             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8032                 cell = Roo.get(t).findParent('th', false, true);
8033                 if (!cell) {
8034                     Roo.log("failed to find th in thead?");
8035                     Roo.log(e.getTarget());
8036                     return;
8037                 }
8038             }
8039             
8040             var cellIndex = cell.dom.cellIndex;
8041             
8042             var ename = name == 'touchstart' ? 'click' : name;
8043             this.fireEvent("header" + ename, this, cellIndex, e);
8044             
8045             return;
8046         }
8047         
8048         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8049             cell = Roo.get(t).findParent('td', false, true);
8050             if (!cell) {
8051                 Roo.log("failed to find th in tbody?");
8052                 Roo.log(e.getTarget());
8053                 return;
8054             }
8055         }
8056         
8057         var row = cell.findParent('tr', false, true);
8058         var cellIndex = cell.dom.cellIndex;
8059         var rowIndex = row.dom.rowIndex - 1;
8060         
8061         if(row !== false){
8062             
8063             this.fireEvent("row" + name, this, rowIndex, e);
8064             
8065             if(cell !== false){
8066             
8067                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8068             }
8069         }
8070         
8071     },
8072     
8073     onMouseover : function(e, el)
8074     {
8075         var cell = Roo.get(el);
8076         
8077         if(!cell){
8078             return;
8079         }
8080         
8081         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8082             cell = cell.findParent('td', false, true);
8083         }
8084         
8085         var row = cell.findParent('tr', false, true);
8086         var cellIndex = cell.dom.cellIndex;
8087         var rowIndex = row.dom.rowIndex - 1; // start from 0
8088         
8089         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8090         
8091     },
8092     
8093     onMouseout : function(e, el)
8094     {
8095         var cell = Roo.get(el);
8096         
8097         if(!cell){
8098             return;
8099         }
8100         
8101         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8102             cell = cell.findParent('td', false, true);
8103         }
8104         
8105         var row = cell.findParent('tr', false, true);
8106         var cellIndex = cell.dom.cellIndex;
8107         var rowIndex = row.dom.rowIndex - 1; // start from 0
8108         
8109         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8110         
8111     },
8112     
8113     onClick : function(e, el)
8114     {
8115         var cell = Roo.get(el);
8116         
8117         if(!cell || (!this.cellSelection && !this.rowSelection)){
8118             return;
8119         }
8120         
8121         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8122             cell = cell.findParent('td', false, true);
8123         }
8124         
8125         if(!cell || typeof(cell) == 'undefined'){
8126             return;
8127         }
8128         
8129         var row = cell.findParent('tr', false, true);
8130         
8131         if(!row || typeof(row) == 'undefined'){
8132             return;
8133         }
8134         
8135         var cellIndex = cell.dom.cellIndex;
8136         var rowIndex = this.getRowIndex(row);
8137         
8138         // why??? - should these not be based on SelectionModel?
8139         if(this.cellSelection){
8140             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8141         }
8142         
8143         if(this.rowSelection){
8144             this.fireEvent('rowclick', this, row, rowIndex, e);
8145         }
8146         
8147         
8148     },
8149         
8150     onDblClick : function(e,el)
8151     {
8152         var cell = Roo.get(el);
8153         
8154         if(!cell || (!this.cellSelection && !this.rowSelection)){
8155             return;
8156         }
8157         
8158         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8159             cell = cell.findParent('td', false, true);
8160         }
8161         
8162         if(!cell || typeof(cell) == 'undefined'){
8163             return;
8164         }
8165         
8166         var row = cell.findParent('tr', false, true);
8167         
8168         if(!row || typeof(row) == 'undefined'){
8169             return;
8170         }
8171         
8172         var cellIndex = cell.dom.cellIndex;
8173         var rowIndex = this.getRowIndex(row);
8174         
8175         if(this.cellSelection){
8176             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8177         }
8178         
8179         if(this.rowSelection){
8180             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8181         }
8182     },
8183     
8184     sort : function(e,el)
8185     {
8186         var col = Roo.get(el);
8187         
8188         if(!col.hasClass('sortable')){
8189             return;
8190         }
8191         
8192         var sort = col.attr('sort');
8193         var dir = 'ASC';
8194         
8195         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8196             dir = 'DESC';
8197         }
8198         
8199         this.store.sortInfo = {field : sort, direction : dir};
8200         
8201         if (this.footer) {
8202             Roo.log("calling footer first");
8203             this.footer.onClick('first');
8204         } else {
8205         
8206             this.store.load({ params : { start : 0 } });
8207         }
8208     },
8209     
8210     renderHeader : function()
8211     {
8212         var header = {
8213             tag: 'thead',
8214             cn : []
8215         };
8216         
8217         var cm = this.cm;
8218         this.totalWidth = 0;
8219         
8220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8221             
8222             var config = cm.config[i];
8223             
8224             var c = {
8225                 tag: 'th',
8226                 cls : 'x-hcol-' + i,
8227                 style : '',
8228                 html: cm.getColumnHeader(i)
8229             };
8230             
8231             var hh = '';
8232             
8233             if(typeof(config.sortable) != 'undefined' && config.sortable){
8234                 c.cls = 'sortable';
8235                 c.html = '<i class="glyphicon"></i>' + c.html;
8236             }
8237             
8238             // could use BS4 hidden-..-down 
8239             
8240             if(typeof(config.lgHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.mdHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.smHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.xsHeader) != 'undefined'){
8253                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8254             }
8255             
8256             if(hh.length){
8257                 c.html = hh;
8258             }
8259             
8260             if(typeof(config.tooltip) != 'undefined'){
8261                 c.tooltip = config.tooltip;
8262             }
8263             
8264             if(typeof(config.colspan) != 'undefined'){
8265                 c.colspan = config.colspan;
8266             }
8267             
8268             if(typeof(config.hidden) != 'undefined' && config.hidden){
8269                 c.style += ' display:none;';
8270             }
8271             
8272             if(typeof(config.dataIndex) != 'undefined'){
8273                 c.sort = config.dataIndex;
8274             }
8275             
8276            
8277             
8278             if(typeof(config.align) != 'undefined' && config.align.length){
8279                 c.style += ' text-align:' + config.align + ';';
8280             }
8281             
8282             if(typeof(config.width) != 'undefined'){
8283                 c.style += ' width:' + config.width + 'px;';
8284                 this.totalWidth += config.width;
8285             } else {
8286                 this.totalWidth += 100; // assume minimum of 100 per column?
8287             }
8288             
8289             if(typeof(config.cls) != 'undefined'){
8290                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8291             }
8292             
8293             ['xs','sm','md','lg'].map(function(size){
8294                 
8295                 if(typeof(config[size]) == 'undefined'){
8296                     return;
8297                 }
8298                  
8299                 if (!config[size]) { // 0 = hidden
8300                     // BS 4 '0' is treated as hide that column and below.
8301                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8302                     return;
8303                 }
8304                 
8305                 c.cls += ' col-' + size + '-' + config[size] + (
8306                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8307                 );
8308                 
8309                 
8310             });
8311             
8312             header.cn.push(c)
8313         }
8314         
8315         return header;
8316     },
8317     
8318     renderBody : function()
8319     {
8320         var body = {
8321             tag: 'tbody',
8322             cn : [
8323                 {
8324                     tag: 'tr',
8325                     cn : [
8326                         {
8327                             tag : 'td',
8328                             colspan :  this.cm.getColumnCount()
8329                         }
8330                     ]
8331                 }
8332             ]
8333         };
8334         
8335         return body;
8336     },
8337     
8338     renderFooter : function()
8339     {
8340         var footer = {
8341             tag: 'tfoot',
8342             cn : [
8343                 {
8344                     tag: 'tr',
8345                     cn : [
8346                         {
8347                             tag : 'td',
8348                             colspan :  this.cm.getColumnCount()
8349                         }
8350                     ]
8351                 }
8352             ]
8353         };
8354         
8355         return footer;
8356     },
8357     
8358     
8359     
8360     onLoad : function()
8361     {
8362 //        Roo.log('ds onload');
8363         this.clear();
8364         
8365         var _this = this;
8366         var cm = this.cm;
8367         var ds = this.store;
8368         
8369         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8370             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8371             if (_this.store.sortInfo) {
8372                     
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8375                 }
8376                 
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8379                 }
8380             }
8381         });
8382         
8383         var tbody =  this.mainBody;
8384               
8385         if(ds.getCount() > 0){
8386             ds.data.each(function(d,rowIndex){
8387                 var row =  this.renderRow(cm, ds, rowIndex);
8388                 
8389                 tbody.createChild(row);
8390                 
8391                 var _this = this;
8392                 
8393                 if(row.cellObjects.length){
8394                     Roo.each(row.cellObjects, function(r){
8395                         _this.renderCellObject(r);
8396                     })
8397                 }
8398                 
8399             }, this);
8400         }
8401         
8402         var tfoot = this.el.select('tfoot', true).first();
8403         
8404         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8405             
8406             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8407             
8408             var total = this.ds.getTotalCount();
8409             
8410             if(this.footer.pageSize < total){
8411                 this.mainFoot.show();
8412             }
8413         }
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseover', _this.onMouseover, _this);
8417         });
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseout', _this.onMouseout, _this);
8421         });
8422         this.fireEvent('rowsrendered', this);
8423         
8424         this.autoSize();
8425     },
8426     
8427     
8428     onUpdate : function(ds,record)
8429     {
8430         this.refreshRow(record);
8431         this.autoSize();
8432     },
8433     
8434     onRemove : function(ds, record, index, isUpdate){
8435         if(isUpdate !== true){
8436             this.fireEvent("beforerowremoved", this, index, record);
8437         }
8438         var bt = this.mainBody.dom;
8439         
8440         var rows = this.el.select('tbody > tr', true).elements;
8441         
8442         if(typeof(rows[index]) != 'undefined'){
8443             bt.removeChild(rows[index].dom);
8444         }
8445         
8446 //        if(bt.rows[index]){
8447 //            bt.removeChild(bt.rows[index]);
8448 //        }
8449         
8450         if(isUpdate !== true){
8451             //this.stripeRows(index);
8452             //this.syncRowHeights(index, index);
8453             //this.layout();
8454             this.fireEvent("rowremoved", this, index, record);
8455         }
8456     },
8457     
8458     onAdd : function(ds, records, rowIndex)
8459     {
8460         //Roo.log('on Add called');
8461         // - note this does not handle multiple adding very well..
8462         var bt = this.mainBody.dom;
8463         for (var i =0 ; i < records.length;i++) {
8464             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8465             //Roo.log(records[i]);
8466             //Roo.log(this.store.getAt(rowIndex+i));
8467             this.insertRow(this.store, rowIndex + i, false);
8468             return;
8469         }
8470         
8471     },
8472     
8473     
8474     refreshRow : function(record){
8475         var ds = this.store, index;
8476         if(typeof record == 'number'){
8477             index = record;
8478             record = ds.getAt(index);
8479         }else{
8480             index = ds.indexOf(record);
8481             if (index < 0) {
8482                 return; // should not happen - but seems to 
8483             }
8484         }
8485         this.insertRow(ds, index, true);
8486         this.autoSize();
8487         this.onRemove(ds, record, index+1, true);
8488         this.autoSize();
8489         //this.syncRowHeights(index, index);
8490         //this.layout();
8491         this.fireEvent("rowupdated", this, index, record);
8492     },
8493     
8494     insertRow : function(dm, rowIndex, isUpdate){
8495         
8496         if(!isUpdate){
8497             this.fireEvent("beforerowsinserted", this, rowIndex);
8498         }
8499             //var s = this.getScrollState();
8500         var row = this.renderRow(this.cm, this.store, rowIndex);
8501         // insert before rowIndex..
8502         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8503         
8504         var _this = this;
8505                 
8506         if(row.cellObjects.length){
8507             Roo.each(row.cellObjects, function(r){
8508                 _this.renderCellObject(r);
8509             })
8510         }
8511             
8512         if(!isUpdate){
8513             this.fireEvent("rowsinserted", this, rowIndex);
8514             //this.syncRowHeights(firstRow, lastRow);
8515             //this.stripeRows(firstRow);
8516             //this.layout();
8517         }
8518         
8519     },
8520     
8521     
8522     getRowDom : function(rowIndex)
8523     {
8524         var rows = this.el.select('tbody > tr', true).elements;
8525         
8526         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8527         
8528     },
8529     // returns the object tree for a tr..
8530   
8531     
8532     renderRow : function(cm, ds, rowIndex) 
8533     {
8534         var d = ds.getAt(rowIndex);
8535         
8536         var row = {
8537             tag : 'tr',
8538             cls : 'x-row-' + rowIndex,
8539             cn : []
8540         };
8541             
8542         var cellObjects = [];
8543         
8544         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8545             var config = cm.config[i];
8546             
8547             var renderer = cm.getRenderer(i);
8548             var value = '';
8549             var id = false;
8550             
8551             if(typeof(renderer) !== 'undefined'){
8552                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8553             }
8554             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8555             // and are rendered into the cells after the row is rendered - using the id for the element.
8556             
8557             if(typeof(value) === 'object'){
8558                 id = Roo.id();
8559                 cellObjects.push({
8560                     container : id,
8561                     cfg : value 
8562                 })
8563             }
8564             
8565             var rowcfg = {
8566                 record: d,
8567                 rowIndex : rowIndex,
8568                 colIndex : i,
8569                 rowClass : ''
8570             };
8571
8572             this.fireEvent('rowclass', this, rowcfg);
8573             
8574             var td = {
8575                 tag: 'td',
8576                 cls : rowcfg.rowClass + ' x-col-' + i,
8577                 style: '',
8578                 html: (typeof(value) === 'object') ? '' : value
8579             };
8580             
8581             if (id) {
8582                 td.id = id;
8583             }
8584             
8585             if(typeof(config.colspan) != 'undefined'){
8586                 td.colspan = config.colspan;
8587             }
8588             
8589             if(typeof(config.hidden) != 'undefined' && config.hidden){
8590                 td.style += ' display:none;';
8591             }
8592             
8593             if(typeof(config.align) != 'undefined' && config.align.length){
8594                 td.style += ' text-align:' + config.align + ';';
8595             }
8596             if(typeof(config.valign) != 'undefined' && config.valign.length){
8597                 td.style += ' vertical-align:' + config.valign + ';';
8598             }
8599             
8600             if(typeof(config.width) != 'undefined'){
8601                 td.style += ' width:' +  config.width + 'px;';
8602             }
8603             
8604             if(typeof(config.cursor) != 'undefined'){
8605                 td.style += ' cursor:' +  config.cursor + ';';
8606             }
8607             
8608             if(typeof(config.cls) != 'undefined'){
8609                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8610             }
8611             
8612             ['xs','sm','md','lg'].map(function(size){
8613                 
8614                 if(typeof(config[size]) == 'undefined'){
8615                     return;
8616                 }
8617                 
8618                 
8619                   
8620                 if (!config[size]) { // 0 = hidden
8621                     // BS 4 '0' is treated as hide that column and below.
8622                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8623                     return;
8624                 }
8625                 
8626                 td.cls += ' col-' + size + '-' + config[size] + (
8627                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8628                 );
8629                  
8630
8631             });
8632             
8633             row.cn.push(td);
8634            
8635         }
8636         
8637         row.cellObjects = cellObjects;
8638         
8639         return row;
8640           
8641     },
8642     
8643     
8644     
8645     onBeforeLoad : function()
8646     {
8647         
8648     },
8649      /**
8650      * Remove all rows
8651      */
8652     clear : function()
8653     {
8654         this.el.select('tbody', true).first().dom.innerHTML = '';
8655     },
8656     /**
8657      * Show or hide a row.
8658      * @param {Number} rowIndex to show or hide
8659      * @param {Boolean} state hide
8660      */
8661     setRowVisibility : function(rowIndex, state)
8662     {
8663         var bt = this.mainBody.dom;
8664         
8665         var rows = this.el.select('tbody > tr', true).elements;
8666         
8667         if(typeof(rows[rowIndex]) == 'undefined'){
8668             return;
8669         }
8670         rows[rowIndex].dom.style.display = state ? '' : 'none';
8671     },
8672     
8673     
8674     getSelectionModel : function(){
8675         if(!this.selModel){
8676             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8677         }
8678         return this.selModel;
8679     },
8680     /*
8681      * Render the Roo.bootstrap object from renderder
8682      */
8683     renderCellObject : function(r)
8684     {
8685         var _this = this;
8686         
8687         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8688         
8689         var t = r.cfg.render(r.container);
8690         
8691         if(r.cfg.cn){
8692             Roo.each(r.cfg.cn, function(c){
8693                 var child = {
8694                     container: t.getChildContainer(),
8695                     cfg: c
8696                 };
8697                 _this.renderCellObject(child);
8698             })
8699         }
8700     },
8701     
8702     getRowIndex : function(row)
8703     {
8704         var rowIndex = -1;
8705         
8706         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8707             if(el != row){
8708                 return;
8709             }
8710             
8711             rowIndex = index;
8712         });
8713         
8714         return rowIndex;
8715     },
8716      /**
8717      * Returns the grid's underlying element = used by panel.Grid
8718      * @return {Element} The element
8719      */
8720     getGridEl : function(){
8721         return this.el;
8722     },
8723      /**
8724      * Forces a resize - used by panel.Grid
8725      * @return {Element} The element
8726      */
8727     autoSize : function()
8728     {
8729         //var ctr = Roo.get(this.container.dom.parentElement);
8730         var ctr = Roo.get(this.el.dom);
8731         
8732         var thd = this.getGridEl().select('thead',true).first();
8733         var tbd = this.getGridEl().select('tbody', true).first();
8734         var tfd = this.getGridEl().select('tfoot', true).first();
8735         
8736         var cw = ctr.getWidth();
8737         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8738         
8739         if (tbd) {
8740             
8741             tbd.setWidth(ctr.getWidth());
8742             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8743             // this needs fixing for various usage - currently only hydra job advers I think..
8744             //tdb.setHeight(
8745             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8746             //); 
8747             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8748             cw -= barsize;
8749         }
8750         cw = Math.max(cw, this.totalWidth);
8751         this.getGridEl().select('tbody tr',true).setWidth(cw);
8752         
8753         // resize 'expandable coloumn?
8754         
8755         return; // we doe not have a view in this design..
8756         
8757     },
8758     onBodyScroll: function()
8759     {
8760         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8761         if(this.mainHead){
8762             this.mainHead.setStyle({
8763                 'position' : 'relative',
8764                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8765             });
8766         }
8767         
8768         if(this.lazyLoad){
8769             
8770             var scrollHeight = this.mainBody.dom.scrollHeight;
8771             
8772             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8773             
8774             var height = this.mainBody.getHeight();
8775             
8776             if(scrollHeight - height == scrollTop) {
8777                 
8778                 var total = this.ds.getTotalCount();
8779                 
8780                 if(this.footer.cursor + this.footer.pageSize < total){
8781                     
8782                     this.footer.ds.load({
8783                         params : {
8784                             start : this.footer.cursor + this.footer.pageSize,
8785                             limit : this.footer.pageSize
8786                         },
8787                         add : true
8788                     });
8789                 }
8790             }
8791             
8792         }
8793     },
8794     
8795     onHeaderChange : function()
8796     {
8797         var header = this.renderHeader();
8798         var table = this.el.select('table', true).first();
8799         
8800         this.mainHead.remove();
8801         this.mainHead = table.createChild(header, this.mainBody, false);
8802     },
8803     
8804     onHiddenChange : function(colModel, colIndex, hidden)
8805     {
8806         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8807         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8808         
8809         this.CSS.updateRule(thSelector, "display", "");
8810         this.CSS.updateRule(tdSelector, "display", "");
8811         
8812         if(hidden){
8813             this.CSS.updateRule(thSelector, "display", "none");
8814             this.CSS.updateRule(tdSelector, "display", "none");
8815         }
8816         
8817         this.onHeaderChange();
8818         this.onLoad();
8819     },
8820     
8821     setColumnWidth: function(col_index, width)
8822     {
8823         // width = "md-2 xs-2..."
8824         if(!this.colModel.config[col_index]) {
8825             return;
8826         }
8827         
8828         var w = width.split(" ");
8829         
8830         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8831         
8832         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8833         
8834         
8835         for(var j = 0; j < w.length; j++) {
8836             
8837             if(!w[j]) {
8838                 continue;
8839             }
8840             
8841             var size_cls = w[j].split("-");
8842             
8843             if(!Number.isInteger(size_cls[1] * 1)) {
8844                 continue;
8845             }
8846             
8847             if(!this.colModel.config[col_index][size_cls[0]]) {
8848                 continue;
8849             }
8850             
8851             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8852                 continue;
8853             }
8854             
8855             h_row[0].classList.replace(
8856                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8857                 "col-"+size_cls[0]+"-"+size_cls[1]
8858             );
8859             
8860             for(var i = 0; i < rows.length; i++) {
8861                 
8862                 var size_cls = w[j].split("-");
8863                 
8864                 if(!Number.isInteger(size_cls[1] * 1)) {
8865                     continue;
8866                 }
8867                 
8868                 if(!this.colModel.config[col_index][size_cls[0]]) {
8869                     continue;
8870                 }
8871                 
8872                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8873                     continue;
8874                 }
8875                 
8876                 rows[i].classList.replace(
8877                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8878                     "col-"+size_cls[0]+"-"+size_cls[1]
8879                 );
8880             }
8881             
8882             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8883         }
8884     }
8885 });
8886
8887  
8888
8889  /*
8890  * - LGPL
8891  *
8892  * table cell
8893  * 
8894  */
8895
8896 /**
8897  * @class Roo.bootstrap.TableCell
8898  * @extends Roo.bootstrap.Component
8899  * Bootstrap TableCell class
8900  * @cfg {String} html cell contain text
8901  * @cfg {String} cls cell class
8902  * @cfg {String} tag cell tag (td|th) default td
8903  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8904  * @cfg {String} align Aligns the content in a cell
8905  * @cfg {String} axis Categorizes cells
8906  * @cfg {String} bgcolor Specifies the background color of a cell
8907  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8908  * @cfg {Number} colspan Specifies the number of columns a cell should span
8909  * @cfg {String} headers Specifies one or more header cells a cell is related to
8910  * @cfg {Number} height Sets the height of a cell
8911  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8912  * @cfg {Number} rowspan Sets the number of rows a cell should span
8913  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8914  * @cfg {String} valign Vertical aligns the content in a cell
8915  * @cfg {Number} width Specifies the width of a cell
8916  * 
8917  * @constructor
8918  * Create a new TableCell
8919  * @param {Object} config The config object
8920  */
8921
8922 Roo.bootstrap.TableCell = function(config){
8923     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8924 };
8925
8926 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8927     
8928     html: false,
8929     cls: false,
8930     tag: false,
8931     abbr: false,
8932     align: false,
8933     axis: false,
8934     bgcolor: false,
8935     charoff: false,
8936     colspan: false,
8937     headers: false,
8938     height: false,
8939     nowrap: false,
8940     rowspan: false,
8941     scope: false,
8942     valign: false,
8943     width: false,
8944     
8945     
8946     getAutoCreate : function(){
8947         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8948         
8949         cfg = {
8950             tag: 'td'
8951         };
8952         
8953         if(this.tag){
8954             cfg.tag = this.tag;
8955         }
8956         
8957         if (this.html) {
8958             cfg.html=this.html
8959         }
8960         if (this.cls) {
8961             cfg.cls=this.cls
8962         }
8963         if (this.abbr) {
8964             cfg.abbr=this.abbr
8965         }
8966         if (this.align) {
8967             cfg.align=this.align
8968         }
8969         if (this.axis) {
8970             cfg.axis=this.axis
8971         }
8972         if (this.bgcolor) {
8973             cfg.bgcolor=this.bgcolor
8974         }
8975         if (this.charoff) {
8976             cfg.charoff=this.charoff
8977         }
8978         if (this.colspan) {
8979             cfg.colspan=this.colspan
8980         }
8981         if (this.headers) {
8982             cfg.headers=this.headers
8983         }
8984         if (this.height) {
8985             cfg.height=this.height
8986         }
8987         if (this.nowrap) {
8988             cfg.nowrap=this.nowrap
8989         }
8990         if (this.rowspan) {
8991             cfg.rowspan=this.rowspan
8992         }
8993         if (this.scope) {
8994             cfg.scope=this.scope
8995         }
8996         if (this.valign) {
8997             cfg.valign=this.valign
8998         }
8999         if (this.width) {
9000             cfg.width=this.width
9001         }
9002         
9003         
9004         return cfg;
9005     }
9006    
9007 });
9008
9009  
9010
9011  /*
9012  * - LGPL
9013  *
9014  * table row
9015  * 
9016  */
9017
9018 /**
9019  * @class Roo.bootstrap.TableRow
9020  * @extends Roo.bootstrap.Component
9021  * Bootstrap TableRow class
9022  * @cfg {String} cls row class
9023  * @cfg {String} align Aligns the content in a table row
9024  * @cfg {String} bgcolor Specifies a background color for a table row
9025  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9026  * @cfg {String} valign Vertical aligns the content in a table row
9027  * 
9028  * @constructor
9029  * Create a new TableRow
9030  * @param {Object} config The config object
9031  */
9032
9033 Roo.bootstrap.TableRow = function(config){
9034     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9035 };
9036
9037 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9038     
9039     cls: false,
9040     align: false,
9041     bgcolor: false,
9042     charoff: false,
9043     valign: false,
9044     
9045     getAutoCreate : function(){
9046         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9047         
9048         cfg = {
9049             tag: 'tr'
9050         };
9051             
9052         if(this.cls){
9053             cfg.cls = this.cls;
9054         }
9055         if(this.align){
9056             cfg.align = this.align;
9057         }
9058         if(this.bgcolor){
9059             cfg.bgcolor = this.bgcolor;
9060         }
9061         if(this.charoff){
9062             cfg.charoff = this.charoff;
9063         }
9064         if(this.valign){
9065             cfg.valign = this.valign;
9066         }
9067         
9068         return cfg;
9069     }
9070    
9071 });
9072
9073  
9074
9075  /*
9076  * - LGPL
9077  *
9078  * table body
9079  * 
9080  */
9081
9082 /**
9083  * @class Roo.bootstrap.TableBody
9084  * @extends Roo.bootstrap.Component
9085  * Bootstrap TableBody class
9086  * @cfg {String} cls element class
9087  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9088  * @cfg {String} align Aligns the content inside the element
9089  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9090  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9091  * 
9092  * @constructor
9093  * Create a new TableBody
9094  * @param {Object} config The config object
9095  */
9096
9097 Roo.bootstrap.TableBody = function(config){
9098     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9099 };
9100
9101 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9102     
9103     cls: false,
9104     tag: false,
9105     align: false,
9106     charoff: false,
9107     valign: false,
9108     
9109     getAutoCreate : function(){
9110         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9111         
9112         cfg = {
9113             tag: 'tbody'
9114         };
9115             
9116         if (this.cls) {
9117             cfg.cls=this.cls
9118         }
9119         if(this.tag){
9120             cfg.tag = this.tag;
9121         }
9122         
9123         if(this.align){
9124             cfg.align = this.align;
9125         }
9126         if(this.charoff){
9127             cfg.charoff = this.charoff;
9128         }
9129         if(this.valign){
9130             cfg.valign = this.valign;
9131         }
9132         
9133         return cfg;
9134     }
9135     
9136     
9137 //    initEvents : function()
9138 //    {
9139 //        
9140 //        if(!this.store){
9141 //            return;
9142 //        }
9143 //        
9144 //        this.store = Roo.factory(this.store, Roo.data);
9145 //        this.store.on('load', this.onLoad, this);
9146 //        
9147 //        this.store.load();
9148 //        
9149 //    },
9150 //    
9151 //    onLoad: function () 
9152 //    {   
9153 //        this.fireEvent('load', this);
9154 //    }
9155 //    
9156 //   
9157 });
9158
9159  
9160
9161  /*
9162  * Based on:
9163  * Ext JS Library 1.1.1
9164  * Copyright(c) 2006-2007, Ext JS, LLC.
9165  *
9166  * Originally Released Under LGPL - original licence link has changed is not relivant.
9167  *
9168  * Fork - LGPL
9169  * <script type="text/javascript">
9170  */
9171
9172 // as we use this in bootstrap.
9173 Roo.namespace('Roo.form');
9174  /**
9175  * @class Roo.form.Action
9176  * Internal Class used to handle form actions
9177  * @constructor
9178  * @param {Roo.form.BasicForm} el The form element or its id
9179  * @param {Object} config Configuration options
9180  */
9181
9182  
9183  
9184 // define the action interface
9185 Roo.form.Action = function(form, options){
9186     this.form = form;
9187     this.options = options || {};
9188 };
9189 /**
9190  * Client Validation Failed
9191  * @const 
9192  */
9193 Roo.form.Action.CLIENT_INVALID = 'client';
9194 /**
9195  * Server Validation Failed
9196  * @const 
9197  */
9198 Roo.form.Action.SERVER_INVALID = 'server';
9199  /**
9200  * Connect to Server Failed
9201  * @const 
9202  */
9203 Roo.form.Action.CONNECT_FAILURE = 'connect';
9204 /**
9205  * Reading Data from Server Failed
9206  * @const 
9207  */
9208 Roo.form.Action.LOAD_FAILURE = 'load';
9209
9210 Roo.form.Action.prototype = {
9211     type : 'default',
9212     failureType : undefined,
9213     response : undefined,
9214     result : undefined,
9215
9216     // interface method
9217     run : function(options){
9218
9219     },
9220
9221     // interface method
9222     success : function(response){
9223
9224     },
9225
9226     // interface method
9227     handleResponse : function(response){
9228
9229     },
9230
9231     // default connection failure
9232     failure : function(response){
9233         
9234         this.response = response;
9235         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9236         this.form.afterAction(this, false);
9237     },
9238
9239     processResponse : function(response){
9240         this.response = response;
9241         if(!response.responseText){
9242             return true;
9243         }
9244         this.result = this.handleResponse(response);
9245         return this.result;
9246     },
9247
9248     // utility functions used internally
9249     getUrl : function(appendParams){
9250         var url = this.options.url || this.form.url || this.form.el.dom.action;
9251         if(appendParams){
9252             var p = this.getParams();
9253             if(p){
9254                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9255             }
9256         }
9257         return url;
9258     },
9259
9260     getMethod : function(){
9261         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9262     },
9263
9264     getParams : function(){
9265         var bp = this.form.baseParams;
9266         var p = this.options.params;
9267         if(p){
9268             if(typeof p == "object"){
9269                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9270             }else if(typeof p == 'string' && bp){
9271                 p += '&' + Roo.urlEncode(bp);
9272             }
9273         }else if(bp){
9274             p = Roo.urlEncode(bp);
9275         }
9276         return p;
9277     },
9278
9279     createCallback : function(){
9280         return {
9281             success: this.success,
9282             failure: this.failure,
9283             scope: this,
9284             timeout: (this.form.timeout*1000),
9285             upload: this.form.fileUpload ? this.success : undefined
9286         };
9287     }
9288 };
9289
9290 Roo.form.Action.Submit = function(form, options){
9291     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9292 };
9293
9294 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9295     type : 'submit',
9296
9297     haveProgress : false,
9298     uploadComplete : false,
9299     
9300     // uploadProgress indicator.
9301     uploadProgress : function()
9302     {
9303         if (!this.form.progressUrl) {
9304             return;
9305         }
9306         
9307         if (!this.haveProgress) {
9308             Roo.MessageBox.progress("Uploading", "Uploading");
9309         }
9310         if (this.uploadComplete) {
9311            Roo.MessageBox.hide();
9312            return;
9313         }
9314         
9315         this.haveProgress = true;
9316    
9317         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9318         
9319         var c = new Roo.data.Connection();
9320         c.request({
9321             url : this.form.progressUrl,
9322             params: {
9323                 id : uid
9324             },
9325             method: 'GET',
9326             success : function(req){
9327                //console.log(data);
9328                 var rdata = false;
9329                 var edata;
9330                 try  {
9331                    rdata = Roo.decode(req.responseText)
9332                 } catch (e) {
9333                     Roo.log("Invalid data from server..");
9334                     Roo.log(edata);
9335                     return;
9336                 }
9337                 if (!rdata || !rdata.success) {
9338                     Roo.log(rdata);
9339                     Roo.MessageBox.alert(Roo.encode(rdata));
9340                     return;
9341                 }
9342                 var data = rdata.data;
9343                 
9344                 if (this.uploadComplete) {
9345                    Roo.MessageBox.hide();
9346                    return;
9347                 }
9348                    
9349                 if (data){
9350                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9351                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9352                     );
9353                 }
9354                 this.uploadProgress.defer(2000,this);
9355             },
9356        
9357             failure: function(data) {
9358                 Roo.log('progress url failed ');
9359                 Roo.log(data);
9360             },
9361             scope : this
9362         });
9363            
9364     },
9365     
9366     
9367     run : function()
9368     {
9369         // run get Values on the form, so it syncs any secondary forms.
9370         this.form.getValues();
9371         
9372         var o = this.options;
9373         var method = this.getMethod();
9374         var isPost = method == 'POST';
9375         if(o.clientValidation === false || this.form.isValid()){
9376             
9377             if (this.form.progressUrl) {
9378                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9379                     (new Date() * 1) + '' + Math.random());
9380                     
9381             } 
9382             
9383             
9384             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9385                 form:this.form.el.dom,
9386                 url:this.getUrl(!isPost),
9387                 method: method,
9388                 params:isPost ? this.getParams() : null,
9389                 isUpload: this.form.fileUpload,
9390                 formData : this.form.formData
9391             }));
9392             
9393             this.uploadProgress();
9394
9395         }else if (o.clientValidation !== false){ // client validation failed
9396             this.failureType = Roo.form.Action.CLIENT_INVALID;
9397             this.form.afterAction(this, false);
9398         }
9399     },
9400
9401     success : function(response)
9402     {
9403         this.uploadComplete= true;
9404         if (this.haveProgress) {
9405             Roo.MessageBox.hide();
9406         }
9407         
9408         
9409         var result = this.processResponse(response);
9410         if(result === true || result.success){
9411             this.form.afterAction(this, true);
9412             return;
9413         }
9414         if(result.errors){
9415             this.form.markInvalid(result.errors);
9416             this.failureType = Roo.form.Action.SERVER_INVALID;
9417         }
9418         this.form.afterAction(this, false);
9419     },
9420     failure : function(response)
9421     {
9422         this.uploadComplete= true;
9423         if (this.haveProgress) {
9424             Roo.MessageBox.hide();
9425         }
9426         
9427         this.response = response;
9428         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9429         this.form.afterAction(this, false);
9430     },
9431     
9432     handleResponse : function(response){
9433         if(this.form.errorReader){
9434             var rs = this.form.errorReader.read(response);
9435             var errors = [];
9436             if(rs.records){
9437                 for(var i = 0, len = rs.records.length; i < len; i++) {
9438                     var r = rs.records[i];
9439                     errors[i] = r.data;
9440                 }
9441             }
9442             if(errors.length < 1){
9443                 errors = null;
9444             }
9445             return {
9446                 success : rs.success,
9447                 errors : errors
9448             };
9449         }
9450         var ret = false;
9451         try {
9452             ret = Roo.decode(response.responseText);
9453         } catch (e) {
9454             ret = {
9455                 success: false,
9456                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9457                 errors : []
9458             };
9459         }
9460         return ret;
9461         
9462     }
9463 });
9464
9465
9466 Roo.form.Action.Load = function(form, options){
9467     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9468     this.reader = this.form.reader;
9469 };
9470
9471 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9472     type : 'load',
9473
9474     run : function(){
9475         
9476         Roo.Ajax.request(Roo.apply(
9477                 this.createCallback(), {
9478                     method:this.getMethod(),
9479                     url:this.getUrl(false),
9480                     params:this.getParams()
9481         }));
9482     },
9483
9484     success : function(response){
9485         
9486         var result = this.processResponse(response);
9487         if(result === true || !result.success || !result.data){
9488             this.failureType = Roo.form.Action.LOAD_FAILURE;
9489             this.form.afterAction(this, false);
9490             return;
9491         }
9492         this.form.clearInvalid();
9493         this.form.setValues(result.data);
9494         this.form.afterAction(this, true);
9495     },
9496
9497     handleResponse : function(response){
9498         if(this.form.reader){
9499             var rs = this.form.reader.read(response);
9500             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9501             return {
9502                 success : rs.success,
9503                 data : data
9504             };
9505         }
9506         return Roo.decode(response.responseText);
9507     }
9508 });
9509
9510 Roo.form.Action.ACTION_TYPES = {
9511     'load' : Roo.form.Action.Load,
9512     'submit' : Roo.form.Action.Submit
9513 };/*
9514  * - LGPL
9515  *
9516  * form
9517  *
9518  */
9519
9520 /**
9521  * @class Roo.bootstrap.Form
9522  * @extends Roo.bootstrap.Component
9523  * Bootstrap Form class
9524  * @cfg {String} method  GET | POST (default POST)
9525  * @cfg {String} labelAlign top | left (default top)
9526  * @cfg {String} align left  | right - for navbars
9527  * @cfg {Boolean} loadMask load mask when submit (default true)
9528
9529  *
9530  * @constructor
9531  * Create a new Form
9532  * @param {Object} config The config object
9533  */
9534
9535
9536 Roo.bootstrap.Form = function(config){
9537     
9538     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9539     
9540     Roo.bootstrap.Form.popover.apply();
9541     
9542     this.addEvents({
9543         /**
9544          * @event clientvalidation
9545          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9546          * @param {Form} this
9547          * @param {Boolean} valid true if the form has passed client-side validation
9548          */
9549         clientvalidation: true,
9550         /**
9551          * @event beforeaction
9552          * Fires before any action is performed. Return false to cancel the action.
9553          * @param {Form} this
9554          * @param {Action} action The action to be performed
9555          */
9556         beforeaction: true,
9557         /**
9558          * @event actionfailed
9559          * Fires when an action fails.
9560          * @param {Form} this
9561          * @param {Action} action The action that failed
9562          */
9563         actionfailed : true,
9564         /**
9565          * @event actioncomplete
9566          * Fires when an action is completed.
9567          * @param {Form} this
9568          * @param {Action} action The action that completed
9569          */
9570         actioncomplete : true
9571     });
9572 };
9573
9574 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9575
9576      /**
9577      * @cfg {String} method
9578      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9579      */
9580     method : 'POST',
9581     /**
9582      * @cfg {String} url
9583      * The URL to use for form actions if one isn't supplied in the action options.
9584      */
9585     /**
9586      * @cfg {Boolean} fileUpload
9587      * Set to true if this form is a file upload.
9588      */
9589
9590     /**
9591      * @cfg {Object} baseParams
9592      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9593      */
9594
9595     /**
9596      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9597      */
9598     timeout: 30,
9599     /**
9600      * @cfg {Sting} align (left|right) for navbar forms
9601      */
9602     align : 'left',
9603
9604     // private
9605     activeAction : null,
9606
9607     /**
9608      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9609      * element by passing it or its id or mask the form itself by passing in true.
9610      * @type Mixed
9611      */
9612     waitMsgTarget : false,
9613
9614     loadMask : true,
9615     
9616     /**
9617      * @cfg {Boolean} errorMask (true|false) default false
9618      */
9619     errorMask : false,
9620     
9621     /**
9622      * @cfg {Number} maskOffset Default 100
9623      */
9624     maskOffset : 100,
9625     
9626     /**
9627      * @cfg {Boolean} maskBody
9628      */
9629     maskBody : false,
9630
9631     getAutoCreate : function(){
9632
9633         var cfg = {
9634             tag: 'form',
9635             method : this.method || 'POST',
9636             id : this.id || Roo.id(),
9637             cls : ''
9638         };
9639         if (this.parent().xtype.match(/^Nav/)) {
9640             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9641
9642         }
9643
9644         if (this.labelAlign == 'left' ) {
9645             cfg.cls += ' form-horizontal';
9646         }
9647
9648
9649         return cfg;
9650     },
9651     initEvents : function()
9652     {
9653         this.el.on('submit', this.onSubmit, this);
9654         // this was added as random key presses on the form where triggering form submit.
9655         this.el.on('keypress', function(e) {
9656             if (e.getCharCode() != 13) {
9657                 return true;
9658             }
9659             // we might need to allow it for textareas.. and some other items.
9660             // check e.getTarget().
9661
9662             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9663                 return true;
9664             }
9665
9666             Roo.log("keypress blocked");
9667
9668             e.preventDefault();
9669             return false;
9670         });
9671         
9672     },
9673     // private
9674     onSubmit : function(e){
9675         e.stopEvent();
9676     },
9677
9678      /**
9679      * Returns true if client-side validation on the form is successful.
9680      * @return Boolean
9681      */
9682     isValid : function(){
9683         var items = this.getItems();
9684         var valid = true;
9685         var target = false;
9686         
9687         items.each(function(f){
9688             
9689             if(f.validate()){
9690                 return;
9691             }
9692             
9693             Roo.log('invalid field: ' + f.name);
9694             
9695             valid = false;
9696
9697             if(!target && f.el.isVisible(true)){
9698                 target = f;
9699             }
9700            
9701         });
9702         
9703         if(this.errorMask && !valid){
9704             Roo.bootstrap.Form.popover.mask(this, target);
9705         }
9706         
9707         return valid;
9708     },
9709     
9710     /**
9711      * Returns true if any fields in this form have changed since their original load.
9712      * @return Boolean
9713      */
9714     isDirty : function(){
9715         var dirty = false;
9716         var items = this.getItems();
9717         items.each(function(f){
9718            if(f.isDirty()){
9719                dirty = true;
9720                return false;
9721            }
9722            return true;
9723         });
9724         return dirty;
9725     },
9726      /**
9727      * Performs a predefined action (submit or load) or custom actions you define on this form.
9728      * @param {String} actionName The name of the action type
9729      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9730      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9731      * accept other config options):
9732      * <pre>
9733 Property          Type             Description
9734 ----------------  ---------------  ----------------------------------------------------------------------------------
9735 url               String           The url for the action (defaults to the form's url)
9736 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9737 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9738 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9739                                    validate the form on the client (defaults to false)
9740      * </pre>
9741      * @return {BasicForm} this
9742      */
9743     doAction : function(action, options){
9744         if(typeof action == 'string'){
9745             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9746         }
9747         if(this.fireEvent('beforeaction', this, action) !== false){
9748             this.beforeAction(action);
9749             action.run.defer(100, action);
9750         }
9751         return this;
9752     },
9753
9754     // private
9755     beforeAction : function(action){
9756         var o = action.options;
9757         
9758         if(this.loadMask){
9759             
9760             if(this.maskBody){
9761                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9762             } else {
9763                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9764             }
9765         }
9766         // not really supported yet.. ??
9767
9768         //if(this.waitMsgTarget === true){
9769         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770         //}else if(this.waitMsgTarget){
9771         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9772         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9773         //}else {
9774         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9775        // }
9776
9777     },
9778
9779     // private
9780     afterAction : function(action, success){
9781         this.activeAction = null;
9782         var o = action.options;
9783
9784         if(this.loadMask){
9785             
9786             if(this.maskBody){
9787                 Roo.get(document.body).unmask();
9788             } else {
9789                 this.el.unmask();
9790             }
9791         }
9792         
9793         //if(this.waitMsgTarget === true){
9794 //            this.el.unmask();
9795         //}else if(this.waitMsgTarget){
9796         //    this.waitMsgTarget.unmask();
9797         //}else{
9798         //    Roo.MessageBox.updateProgress(1);
9799         //    Roo.MessageBox.hide();
9800        // }
9801         //
9802         if(success){
9803             if(o.reset){
9804                 this.reset();
9805             }
9806             Roo.callback(o.success, o.scope, [this, action]);
9807             this.fireEvent('actioncomplete', this, action);
9808
9809         }else{
9810
9811             // failure condition..
9812             // we have a scenario where updates need confirming.
9813             // eg. if a locking scenario exists..
9814             // we look for { errors : { needs_confirm : true }} in the response.
9815             if (
9816                 (typeof(action.result) != 'undefined')  &&
9817                 (typeof(action.result.errors) != 'undefined')  &&
9818                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9819            ){
9820                 var _t = this;
9821                 Roo.log("not supported yet");
9822                  /*
9823
9824                 Roo.MessageBox.confirm(
9825                     "Change requires confirmation",
9826                     action.result.errorMsg,
9827                     function(r) {
9828                         if (r != 'yes') {
9829                             return;
9830                         }
9831                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9832                     }
9833
9834                 );
9835                 */
9836
9837
9838                 return;
9839             }
9840
9841             Roo.callback(o.failure, o.scope, [this, action]);
9842             // show an error message if no failed handler is set..
9843             if (!this.hasListener('actionfailed')) {
9844                 Roo.log("need to add dialog support");
9845                 /*
9846                 Roo.MessageBox.alert("Error",
9847                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9848                         action.result.errorMsg :
9849                         "Saving Failed, please check your entries or try again"
9850                 );
9851                 */
9852             }
9853
9854             this.fireEvent('actionfailed', this, action);
9855         }
9856
9857     },
9858     /**
9859      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9860      * @param {String} id The value to search for
9861      * @return Field
9862      */
9863     findField : function(id){
9864         var items = this.getItems();
9865         var field = items.get(id);
9866         if(!field){
9867              items.each(function(f){
9868                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9869                     field = f;
9870                     return false;
9871                 }
9872                 return true;
9873             });
9874         }
9875         return field || null;
9876     },
9877      /**
9878      * Mark fields in this form invalid in bulk.
9879      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9880      * @return {BasicForm} this
9881      */
9882     markInvalid : function(errors){
9883         if(errors instanceof Array){
9884             for(var i = 0, len = errors.length; i < len; i++){
9885                 var fieldError = errors[i];
9886                 var f = this.findField(fieldError.id);
9887                 if(f){
9888                     f.markInvalid(fieldError.msg);
9889                 }
9890             }
9891         }else{
9892             var field, id;
9893             for(id in errors){
9894                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9895                     field.markInvalid(errors[id]);
9896                 }
9897             }
9898         }
9899         //Roo.each(this.childForms || [], function (f) {
9900         //    f.markInvalid(errors);
9901         //});
9902
9903         return this;
9904     },
9905
9906     /**
9907      * Set values for fields in this form in bulk.
9908      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9909      * @return {BasicForm} this
9910      */
9911     setValues : function(values){
9912         if(values instanceof Array){ // array of objects
9913             for(var i = 0, len = values.length; i < len; i++){
9914                 var v = values[i];
9915                 var f = this.findField(v.id);
9916                 if(f){
9917                     f.setValue(v.value);
9918                     if(this.trackResetOnLoad){
9919                         f.originalValue = f.getValue();
9920                     }
9921                 }
9922             }
9923         }else{ // object hash
9924             var field, id;
9925             for(id in values){
9926                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9927
9928                     if (field.setFromData &&
9929                         field.valueField &&
9930                         field.displayField &&
9931                         // combos' with local stores can
9932                         // be queried via setValue()
9933                         // to set their value..
9934                         (field.store && !field.store.isLocal)
9935                         ) {
9936                         // it's a combo
9937                         var sd = { };
9938                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9939                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9940                         field.setFromData(sd);
9941
9942                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9943                         
9944                         field.setFromData(values);
9945                         
9946                     } else {
9947                         field.setValue(values[id]);
9948                     }
9949
9950
9951                     if(this.trackResetOnLoad){
9952                         field.originalValue = field.getValue();
9953                     }
9954                 }
9955             }
9956         }
9957
9958         //Roo.each(this.childForms || [], function (f) {
9959         //    f.setValues(values);
9960         //});
9961
9962         return this;
9963     },
9964
9965     /**
9966      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9967      * they are returned as an array.
9968      * @param {Boolean} asString
9969      * @return {Object}
9970      */
9971     getValues : function(asString){
9972         //if (this.childForms) {
9973             // copy values from the child forms
9974         //    Roo.each(this.childForms, function (f) {
9975         //        this.setValues(f.getValues());
9976         //    }, this);
9977         //}
9978
9979
9980
9981         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9982         if(asString === true){
9983             return fs;
9984         }
9985         return Roo.urlDecode(fs);
9986     },
9987
9988     /**
9989      * Returns the fields in this form as an object with key/value pairs.
9990      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9991      * @return {Object}
9992      */
9993     getFieldValues : function(with_hidden)
9994     {
9995         var items = this.getItems();
9996         var ret = {};
9997         items.each(function(f){
9998             
9999             if (!f.getName()) {
10000                 return;
10001             }
10002             
10003             var v = f.getValue();
10004             
10005             if (f.inputType =='radio') {
10006                 if (typeof(ret[f.getName()]) == 'undefined') {
10007                     ret[f.getName()] = ''; // empty..
10008                 }
10009
10010                 if (!f.el.dom.checked) {
10011                     return;
10012
10013                 }
10014                 v = f.el.dom.value;
10015
10016             }
10017             
10018             if(f.xtype == 'MoneyField'){
10019                 ret[f.currencyName] = f.getCurrency();
10020             }
10021
10022             // not sure if this supported any more..
10023             if ((typeof(v) == 'object') && f.getRawValue) {
10024                 v = f.getRawValue() ; // dates..
10025             }
10026             // combo boxes where name != hiddenName...
10027             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10028                 ret[f.name] = f.getRawValue();
10029             }
10030             ret[f.getName()] = v;
10031         });
10032
10033         return ret;
10034     },
10035
10036     /**
10037      * Clears all invalid messages in this form.
10038      * @return {BasicForm} this
10039      */
10040     clearInvalid : function(){
10041         var items = this.getItems();
10042
10043         items.each(function(f){
10044            f.clearInvalid();
10045         });
10046
10047         return this;
10048     },
10049
10050     /**
10051      * Resets this form.
10052      * @return {BasicForm} this
10053      */
10054     reset : function(){
10055         var items = this.getItems();
10056         items.each(function(f){
10057             f.reset();
10058         });
10059
10060         Roo.each(this.childForms || [], function (f) {
10061             f.reset();
10062         });
10063
10064
10065         return this;
10066     },
10067     
10068     getItems : function()
10069     {
10070         var r=new Roo.util.MixedCollection(false, function(o){
10071             return o.id || (o.id = Roo.id());
10072         });
10073         var iter = function(el) {
10074             if (el.inputEl) {
10075                 r.add(el);
10076             }
10077             if (!el.items) {
10078                 return;
10079             }
10080             Roo.each(el.items,function(e) {
10081                 iter(e);
10082             });
10083         };
10084
10085         iter(this);
10086         return r;
10087     },
10088     
10089     hideFields : function(items)
10090     {
10091         Roo.each(items, function(i){
10092             
10093             var f = this.findField(i);
10094             
10095             if(!f){
10096                 return;
10097             }
10098             
10099             f.hide();
10100             
10101         }, this);
10102     },
10103     
10104     showFields : function(items)
10105     {
10106         Roo.each(items, function(i){
10107             
10108             var f = this.findField(i);
10109             
10110             if(!f){
10111                 return;
10112             }
10113             
10114             f.show();
10115             
10116         }, this);
10117     }
10118
10119 });
10120
10121 Roo.apply(Roo.bootstrap.Form, {
10122     
10123     popover : {
10124         
10125         padding : 5,
10126         
10127         isApplied : false,
10128         
10129         isMasked : false,
10130         
10131         form : false,
10132         
10133         target : false,
10134         
10135         toolTip : false,
10136         
10137         intervalID : false,
10138         
10139         maskEl : false,
10140         
10141         apply : function()
10142         {
10143             if(this.isApplied){
10144                 return;
10145             }
10146             
10147             this.maskEl = {
10148                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10149                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10150                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10151                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10152             };
10153             
10154             this.maskEl.top.enableDisplayMode("block");
10155             this.maskEl.left.enableDisplayMode("block");
10156             this.maskEl.bottom.enableDisplayMode("block");
10157             this.maskEl.right.enableDisplayMode("block");
10158             
10159             this.toolTip = new Roo.bootstrap.Tooltip({
10160                 cls : 'roo-form-error-popover',
10161                 alignment : {
10162                     'left' : ['r-l', [-2,0], 'right'],
10163                     'right' : ['l-r', [2,0], 'left'],
10164                     'bottom' : ['tl-bl', [0,2], 'top'],
10165                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10166                 }
10167             });
10168             
10169             this.toolTip.render(Roo.get(document.body));
10170
10171             this.toolTip.el.enableDisplayMode("block");
10172             
10173             Roo.get(document.body).on('click', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             Roo.get(document.body).on('touchstart', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             this.isApplied = true
10182         },
10183         
10184         mask : function(form, target)
10185         {
10186             this.form = form;
10187             
10188             this.target = target;
10189             
10190             if(!this.form.errorMask || !target.el){
10191                 return;
10192             }
10193             
10194             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10195             
10196             Roo.log(scrollable);
10197             
10198             var ot = this.target.el.calcOffsetsTo(scrollable);
10199             
10200             var scrollTo = ot[1] - this.form.maskOffset;
10201             
10202             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10203             
10204             scrollable.scrollTo('top', scrollTo);
10205             
10206             var box = this.target.el.getBox();
10207             Roo.log(box);
10208             var zIndex = Roo.bootstrap.Modal.zIndex++;
10209
10210             
10211             this.maskEl.top.setStyle('position', 'absolute');
10212             this.maskEl.top.setStyle('z-index', zIndex);
10213             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10214             this.maskEl.top.setLeft(0);
10215             this.maskEl.top.setTop(0);
10216             this.maskEl.top.show();
10217             
10218             this.maskEl.left.setStyle('position', 'absolute');
10219             this.maskEl.left.setStyle('z-index', zIndex);
10220             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10221             this.maskEl.left.setLeft(0);
10222             this.maskEl.left.setTop(box.y - this.padding);
10223             this.maskEl.left.show();
10224
10225             this.maskEl.bottom.setStyle('position', 'absolute');
10226             this.maskEl.bottom.setStyle('z-index', zIndex);
10227             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10228             this.maskEl.bottom.setLeft(0);
10229             this.maskEl.bottom.setTop(box.bottom + this.padding);
10230             this.maskEl.bottom.show();
10231
10232             this.maskEl.right.setStyle('position', 'absolute');
10233             this.maskEl.right.setStyle('z-index', zIndex);
10234             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10235             this.maskEl.right.setLeft(box.right + this.padding);
10236             this.maskEl.right.setTop(box.y - this.padding);
10237             this.maskEl.right.show();
10238
10239             this.toolTip.bindEl = this.target.el;
10240
10241             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10242
10243             var tip = this.target.blankText;
10244
10245             if(this.target.getValue() !== '' ) {
10246                 
10247                 if (this.target.invalidText.length) {
10248                     tip = this.target.invalidText;
10249                 } else if (this.target.regexText.length){
10250                     tip = this.target.regexText;
10251                 }
10252             }
10253
10254             this.toolTip.show(tip);
10255
10256             this.intervalID = window.setInterval(function() {
10257                 Roo.bootstrap.Form.popover.unmask();
10258             }, 10000);
10259
10260             window.onwheel = function(){ return false;};
10261             
10262             (function(){ this.isMasked = true; }).defer(500, this);
10263             
10264         },
10265         
10266         unmask : function()
10267         {
10268             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10269                 return;
10270             }
10271             
10272             this.maskEl.top.setStyle('position', 'absolute');
10273             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.top.hide();
10275
10276             this.maskEl.left.setStyle('position', 'absolute');
10277             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.left.hide();
10279
10280             this.maskEl.bottom.setStyle('position', 'absolute');
10281             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.bottom.hide();
10283
10284             this.maskEl.right.setStyle('position', 'absolute');
10285             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.right.hide();
10287             
10288             this.toolTip.hide();
10289             
10290             this.toolTip.el.hide();
10291             
10292             window.onwheel = function(){ return true;};
10293             
10294             if(this.intervalID){
10295                 window.clearInterval(this.intervalID);
10296                 this.intervalID = false;
10297             }
10298             
10299             this.isMasked = false;
10300             
10301         }
10302         
10303     }
10304     
10305 });
10306
10307 /*
10308  * Based on:
10309  * Ext JS Library 1.1.1
10310  * Copyright(c) 2006-2007, Ext JS, LLC.
10311  *
10312  * Originally Released Under LGPL - original licence link has changed is not relivant.
10313  *
10314  * Fork - LGPL
10315  * <script type="text/javascript">
10316  */
10317 /**
10318  * @class Roo.form.VTypes
10319  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10320  * @singleton
10321  */
10322 Roo.form.VTypes = function(){
10323     // closure these in so they are only created once.
10324     var alpha = /^[a-zA-Z_]+$/;
10325     var alphanum = /^[a-zA-Z0-9_]+$/;
10326     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10327     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10328
10329     // All these messages and functions are configurable
10330     return {
10331         /**
10332          * The function used to validate email addresses
10333          * @param {String} value The email address
10334          */
10335         'email' : function(v){
10336             return email.test(v);
10337         },
10338         /**
10339          * The error text to display when the email validation function returns false
10340          * @type String
10341          */
10342         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10343         /**
10344          * The keystroke filter mask to be applied on email input
10345          * @type RegExp
10346          */
10347         'emailMask' : /[a-z0-9_\.\-@]/i,
10348
10349         /**
10350          * The function used to validate URLs
10351          * @param {String} value The URL
10352          */
10353         'url' : function(v){
10354             return url.test(v);
10355         },
10356         /**
10357          * The error text to display when the url validation function returns false
10358          * @type String
10359          */
10360         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10361         
10362         /**
10363          * The function used to validate alpha values
10364          * @param {String} value The value
10365          */
10366         'alpha' : function(v){
10367             return alpha.test(v);
10368         },
10369         /**
10370          * The error text to display when the alpha validation function returns false
10371          * @type String
10372          */
10373         'alphaText' : 'This field should only contain letters and _',
10374         /**
10375          * The keystroke filter mask to be applied on alpha input
10376          * @type RegExp
10377          */
10378         'alphaMask' : /[a-z_]/i,
10379
10380         /**
10381          * The function used to validate alphanumeric values
10382          * @param {String} value The value
10383          */
10384         'alphanum' : function(v){
10385             return alphanum.test(v);
10386         },
10387         /**
10388          * The error text to display when the alphanumeric validation function returns false
10389          * @type String
10390          */
10391         'alphanumText' : 'This field should only contain letters, numbers and _',
10392         /**
10393          * The keystroke filter mask to be applied on alphanumeric input
10394          * @type RegExp
10395          */
10396         'alphanumMask' : /[a-z0-9_]/i
10397     };
10398 }();/*
10399  * - LGPL
10400  *
10401  * Input
10402  * 
10403  */
10404
10405 /**
10406  * @class Roo.bootstrap.Input
10407  * @extends Roo.bootstrap.Component
10408  * Bootstrap Input class
10409  * @cfg {Boolean} disabled is it disabled
10410  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10411  * @cfg {String} name name of the input
10412  * @cfg {string} fieldLabel - the label associated
10413  * @cfg {string} placeholder - placeholder to put in text.
10414  * @cfg {string}  before - input group add on before
10415  * @cfg {string} after - input group add on after
10416  * @cfg {string} size - (lg|sm) or leave empty..
10417  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10418  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10419  * @cfg {Number} md colspan out of 12 for computer-sized screens
10420  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10421  * @cfg {string} value default value of the input
10422  * @cfg {Number} labelWidth set the width of label 
10423  * @cfg {Number} labellg set the width of label (1-12)
10424  * @cfg {Number} labelmd set the width of label (1-12)
10425  * @cfg {Number} labelsm set the width of label (1-12)
10426  * @cfg {Number} labelxs set the width of label (1-12)
10427  * @cfg {String} labelAlign (top|left)
10428  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10429  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10430  * @cfg {String} indicatorpos (left|right) default left
10431  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10432  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10433  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10434
10435  * @cfg {String} align (left|center|right) Default left
10436  * @cfg {Boolean} forceFeedback (true|false) Default false
10437  * 
10438  * @constructor
10439  * Create a new Input
10440  * @param {Object} config The config object
10441  */
10442
10443 Roo.bootstrap.Input = function(config){
10444     
10445     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10446     
10447     this.addEvents({
10448         /**
10449          * @event focus
10450          * Fires when this field receives input focus.
10451          * @param {Roo.form.Field} this
10452          */
10453         focus : true,
10454         /**
10455          * @event blur
10456          * Fires when this field loses input focus.
10457          * @param {Roo.form.Field} this
10458          */
10459         blur : true,
10460         /**
10461          * @event specialkey
10462          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10463          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10464          * @param {Roo.form.Field} this
10465          * @param {Roo.EventObject} e The event object
10466          */
10467         specialkey : true,
10468         /**
10469          * @event change
10470          * Fires just before the field blurs if the field value has changed.
10471          * @param {Roo.form.Field} this
10472          * @param {Mixed} newValue The new value
10473          * @param {Mixed} oldValue The original value
10474          */
10475         change : true,
10476         /**
10477          * @event invalid
10478          * Fires after the field has been marked as invalid.
10479          * @param {Roo.form.Field} this
10480          * @param {String} msg The validation message
10481          */
10482         invalid : true,
10483         /**
10484          * @event valid
10485          * Fires after the field has been validated with no errors.
10486          * @param {Roo.form.Field} this
10487          */
10488         valid : true,
10489          /**
10490          * @event keyup
10491          * Fires after the key up
10492          * @param {Roo.form.Field} this
10493          * @param {Roo.EventObject}  e The event Object
10494          */
10495         keyup : true
10496     });
10497 };
10498
10499 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10500      /**
10501      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10502       automatic validation (defaults to "keyup").
10503      */
10504     validationEvent : "keyup",
10505      /**
10506      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10507      */
10508     validateOnBlur : true,
10509     /**
10510      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10511      */
10512     validationDelay : 250,
10513      /**
10514      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10515      */
10516     focusClass : "x-form-focus",  // not needed???
10517     
10518        
10519     /**
10520      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10521      */
10522     invalidClass : "has-warning",
10523     
10524     /**
10525      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10526      */
10527     validClass : "has-success",
10528     
10529     /**
10530      * @cfg {Boolean} hasFeedback (true|false) default true
10531      */
10532     hasFeedback : true,
10533     
10534     /**
10535      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10536      */
10537     invalidFeedbackClass : "glyphicon-warning-sign",
10538     
10539     /**
10540      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10541      */
10542     validFeedbackClass : "glyphicon-ok",
10543     
10544     /**
10545      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10546      */
10547     selectOnFocus : false,
10548     
10549      /**
10550      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10551      */
10552     maskRe : null,
10553        /**
10554      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10555      */
10556     vtype : null,
10557     
10558       /**
10559      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10560      */
10561     disableKeyFilter : false,
10562     
10563        /**
10564      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10565      */
10566     disabled : false,
10567      /**
10568      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10569      */
10570     allowBlank : true,
10571     /**
10572      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10573      */
10574     blankText : "Please complete this mandatory field",
10575     
10576      /**
10577      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10578      */
10579     minLength : 0,
10580     /**
10581      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10582      */
10583     maxLength : Number.MAX_VALUE,
10584     /**
10585      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10586      */
10587     minLengthText : "The minimum length for this field is {0}",
10588     /**
10589      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10590      */
10591     maxLengthText : "The maximum length for this field is {0}",
10592   
10593     
10594     /**
10595      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10596      * If available, this function will be called only after the basic validators all return true, and will be passed the
10597      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10598      */
10599     validator : null,
10600     /**
10601      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10602      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10603      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10604      */
10605     regex : null,
10606     /**
10607      * @cfg {String} regexText -- Depricated - use Invalid Text
10608      */
10609     regexText : "",
10610     
10611     /**
10612      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10613      */
10614     invalidText : "",
10615     
10616     
10617     
10618     autocomplete: false,
10619     
10620     
10621     fieldLabel : '',
10622     inputType : 'text',
10623     
10624     name : false,
10625     placeholder: false,
10626     before : false,
10627     after : false,
10628     size : false,
10629     hasFocus : false,
10630     preventMark: false,
10631     isFormField : true,
10632     value : '',
10633     labelWidth : 2,
10634     labelAlign : false,
10635     readOnly : false,
10636     align : false,
10637     formatedValue : false,
10638     forceFeedback : false,
10639     
10640     indicatorpos : 'left',
10641     
10642     labellg : 0,
10643     labelmd : 0,
10644     labelsm : 0,
10645     labelxs : 0,
10646     
10647     capture : '',
10648     accept : '',
10649     
10650     parentLabelAlign : function()
10651     {
10652         var parent = this;
10653         while (parent.parent()) {
10654             parent = parent.parent();
10655             if (typeof(parent.labelAlign) !='undefined') {
10656                 return parent.labelAlign;
10657             }
10658         }
10659         return 'left';
10660         
10661     },
10662     
10663     getAutoCreate : function()
10664     {
10665         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10666         
10667         var id = Roo.id();
10668         
10669         var cfg = {};
10670         
10671         if(this.inputType != 'hidden'){
10672             cfg.cls = 'form-group' //input-group
10673         }
10674         
10675         var input =  {
10676             tag: 'input',
10677             id : id,
10678             type : this.inputType,
10679             value : this.value,
10680             cls : 'form-control',
10681             placeholder : this.placeholder || '',
10682             autocomplete : this.autocomplete || 'new-password'
10683         };
10684         if (this.inputType == 'file') {
10685             input.style = 'overflow:hidden'; // why not in CSS?
10686         }
10687         
10688         if(this.capture.length){
10689             input.capture = this.capture;
10690         }
10691         
10692         if(this.accept.length){
10693             input.accept = this.accept + "/*";
10694         }
10695         
10696         if(this.align){
10697             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10698         }
10699         
10700         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10701             input.maxLength = this.maxLength;
10702         }
10703         
10704         if (this.disabled) {
10705             input.disabled=true;
10706         }
10707         
10708         if (this.readOnly) {
10709             input.readonly=true;
10710         }
10711         
10712         if (this.name) {
10713             input.name = this.name;
10714         }
10715         
10716         if (this.size) {
10717             input.cls += ' input-' + this.size;
10718         }
10719         
10720         var settings=this;
10721         ['xs','sm','md','lg'].map(function(size){
10722             if (settings[size]) {
10723                 cfg.cls += ' col-' + size + '-' + settings[size];
10724             }
10725         });
10726         
10727         var inputblock = input;
10728         
10729         var feedback = {
10730             tag: 'span',
10731             cls: 'glyphicon form-control-feedback'
10732         };
10733             
10734         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10735             
10736             inputblock = {
10737                 cls : 'has-feedback',
10738                 cn :  [
10739                     input,
10740                     feedback
10741                 ] 
10742             };  
10743         }
10744         
10745         if (this.before || this.after) {
10746             
10747             inputblock = {
10748                 cls : 'input-group',
10749                 cn :  [] 
10750             };
10751             
10752             if (this.before && typeof(this.before) == 'string') {
10753                 
10754                 inputblock.cn.push({
10755                     tag :'span',
10756                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10757                     html : this.before
10758                 });
10759             }
10760             if (this.before && typeof(this.before) == 'object') {
10761                 this.before = Roo.factory(this.before);
10762                 
10763                 inputblock.cn.push({
10764                     tag :'span',
10765                     cls : 'roo-input-before input-group-prepend   input-group-' +
10766                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10767                 });
10768             }
10769             
10770             inputblock.cn.push(input);
10771             
10772             if (this.after && typeof(this.after) == 'string') {
10773                 inputblock.cn.push({
10774                     tag :'span',
10775                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10776                     html : this.after
10777                 });
10778             }
10779             if (this.after && typeof(this.after) == 'object') {
10780                 this.after = Roo.factory(this.after);
10781                 
10782                 inputblock.cn.push({
10783                     tag :'span',
10784                     cls : 'roo-input-after input-group-append  input-group-' +
10785                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10786                 });
10787             }
10788             
10789             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10790                 inputblock.cls += ' has-feedback';
10791                 inputblock.cn.push(feedback);
10792             }
10793         };
10794         var indicator = {
10795             tag : 'i',
10796             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10797             tooltip : 'This field is required'
10798         };
10799         if (this.allowBlank ) {
10800             indicator.style = this.allowBlank ? ' display:none' : '';
10801         }
10802         if (align ==='left' && this.fieldLabel.length) {
10803             
10804             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10805             
10806             cfg.cn = [
10807                 indicator,
10808                 {
10809                     tag: 'label',
10810                     'for' :  id,
10811                     cls : 'control-label col-form-label',
10812                     html : this.fieldLabel
10813
10814                 },
10815                 {
10816                     cls : "", 
10817                     cn: [
10818                         inputblock
10819                     ]
10820                 }
10821             ];
10822             
10823             var labelCfg = cfg.cn[1];
10824             var contentCfg = cfg.cn[2];
10825             
10826             if(this.indicatorpos == 'right'){
10827                 cfg.cn = [
10828                     {
10829                         tag: 'label',
10830                         'for' :  id,
10831                         cls : 'control-label col-form-label',
10832                         cn : [
10833                             {
10834                                 tag : 'span',
10835                                 html : this.fieldLabel
10836                             },
10837                             indicator
10838                         ]
10839                     },
10840                     {
10841                         cls : "",
10842                         cn: [
10843                             inputblock
10844                         ]
10845                     }
10846
10847                 ];
10848                 
10849                 labelCfg = cfg.cn[0];
10850                 contentCfg = cfg.cn[1];
10851             
10852             }
10853             
10854             if(this.labelWidth > 12){
10855                 labelCfg.style = "width: " + this.labelWidth + 'px';
10856             }
10857             
10858             if(this.labelWidth < 13 && this.labelmd == 0){
10859                 this.labelmd = this.labelWidth;
10860             }
10861             
10862             if(this.labellg > 0){
10863                 labelCfg.cls += ' col-lg-' + this.labellg;
10864                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10865             }
10866             
10867             if(this.labelmd > 0){
10868                 labelCfg.cls += ' col-md-' + this.labelmd;
10869                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10870             }
10871             
10872             if(this.labelsm > 0){
10873                 labelCfg.cls += ' col-sm-' + this.labelsm;
10874                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10875             }
10876             
10877             if(this.labelxs > 0){
10878                 labelCfg.cls += ' col-xs-' + this.labelxs;
10879                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10880             }
10881             
10882             
10883         } else if ( this.fieldLabel.length) {
10884                 
10885             
10886             
10887             cfg.cn = [
10888                 {
10889                     tag : 'i',
10890                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10891                     tooltip : 'This field is required',
10892                     style : this.allowBlank ? ' display:none' : '' 
10893                 },
10894                 {
10895                     tag: 'label',
10896                    //cls : 'input-group-addon',
10897                     html : this.fieldLabel
10898
10899                 },
10900
10901                inputblock
10902
10903            ];
10904            
10905            if(this.indicatorpos == 'right'){
10906        
10907                 cfg.cn = [
10908                     {
10909                         tag: 'label',
10910                        //cls : 'input-group-addon',
10911                         html : this.fieldLabel
10912
10913                     },
10914                     {
10915                         tag : 'i',
10916                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10917                         tooltip : 'This field is required',
10918                         style : this.allowBlank ? ' display:none' : '' 
10919                     },
10920
10921                    inputblock
10922
10923                ];
10924
10925             }
10926
10927         } else {
10928             
10929             cfg.cn = [
10930
10931                     inputblock
10932
10933             ];
10934                 
10935                 
10936         };
10937         
10938         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10939            cfg.cls += ' navbar-form';
10940         }
10941         
10942         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10943             // on BS4 we do this only if not form 
10944             cfg.cls += ' navbar-form';
10945             cfg.tag = 'li';
10946         }
10947         
10948         return cfg;
10949         
10950     },
10951     /**
10952      * return the real input element.
10953      */
10954     inputEl: function ()
10955     {
10956         return this.el.select('input.form-control',true).first();
10957     },
10958     
10959     tooltipEl : function()
10960     {
10961         return this.inputEl();
10962     },
10963     
10964     indicatorEl : function()
10965     {
10966         if (Roo.bootstrap.version == 4) {
10967             return false; // not enabled in v4 yet.
10968         }
10969         
10970         var indicator = this.el.select('i.roo-required-indicator',true).first();
10971         
10972         if(!indicator){
10973             return false;
10974         }
10975         
10976         return indicator;
10977         
10978     },
10979     
10980     setDisabled : function(v)
10981     {
10982         var i  = this.inputEl().dom;
10983         if (!v) {
10984             i.removeAttribute('disabled');
10985             return;
10986             
10987         }
10988         i.setAttribute('disabled','true');
10989     },
10990     initEvents : function()
10991     {
10992           
10993         this.inputEl().on("keydown" , this.fireKey,  this);
10994         this.inputEl().on("focus", this.onFocus,  this);
10995         this.inputEl().on("blur", this.onBlur,  this);
10996         
10997         this.inputEl().relayEvent('keyup', this);
10998         
10999         this.indicator = this.indicatorEl();
11000         
11001         if(this.indicator){
11002             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11003         }
11004  
11005         // reference to original value for reset
11006         this.originalValue = this.getValue();
11007         //Roo.form.TextField.superclass.initEvents.call(this);
11008         if(this.validationEvent == 'keyup'){
11009             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11010             this.inputEl().on('keyup', this.filterValidation, this);
11011         }
11012         else if(this.validationEvent !== false){
11013             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11014         }
11015         
11016         if(this.selectOnFocus){
11017             this.on("focus", this.preFocus, this);
11018             
11019         }
11020         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11021             this.inputEl().on("keypress", this.filterKeys, this);
11022         } else {
11023             this.inputEl().relayEvent('keypress', this);
11024         }
11025        /* if(this.grow){
11026             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11027             this.el.on("click", this.autoSize,  this);
11028         }
11029         */
11030         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11031             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11032         }
11033         
11034         if (typeof(this.before) == 'object') {
11035             this.before.render(this.el.select('.roo-input-before',true).first());
11036         }
11037         if (typeof(this.after) == 'object') {
11038             this.after.render(this.el.select('.roo-input-after',true).first());
11039         }
11040         
11041         this.inputEl().on('change', this.onChange, this);
11042         
11043     },
11044     filterValidation : function(e){
11045         if(!e.isNavKeyPress()){
11046             this.validationTask.delay(this.validationDelay);
11047         }
11048     },
11049      /**
11050      * Validates the field value
11051      * @return {Boolean} True if the value is valid, else false
11052      */
11053     validate : function(){
11054         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11055         if(this.disabled || this.validateValue(this.getRawValue())){
11056             this.markValid();
11057             return true;
11058         }
11059         
11060         this.markInvalid();
11061         return false;
11062     },
11063     
11064     
11065     /**
11066      * Validates a value according to the field's validation rules and marks the field as invalid
11067      * if the validation fails
11068      * @param {Mixed} value The value to validate
11069      * @return {Boolean} True if the value is valid, else false
11070      */
11071     validateValue : function(value)
11072     {
11073         if(this.getVisibilityEl().hasClass('hidden')){
11074             return true;
11075         }
11076         
11077         if(value.length < 1)  { // if it's blank
11078             if(this.allowBlank){
11079                 return true;
11080             }
11081             return false;
11082         }
11083         
11084         if(value.length < this.minLength){
11085             return false;
11086         }
11087         if(value.length > this.maxLength){
11088             return false;
11089         }
11090         if(this.vtype){
11091             var vt = Roo.form.VTypes;
11092             if(!vt[this.vtype](value, this)){
11093                 return false;
11094             }
11095         }
11096         if(typeof this.validator == "function"){
11097             var msg = this.validator(value);
11098             if(msg !== true){
11099                 return false;
11100             }
11101             if (typeof(msg) == 'string') {
11102                 this.invalidText = msg;
11103             }
11104         }
11105         
11106         if(this.regex && !this.regex.test(value)){
11107             return false;
11108         }
11109         
11110         return true;
11111     },
11112     
11113      // private
11114     fireKey : function(e){
11115         //Roo.log('field ' + e.getKey());
11116         if(e.isNavKeyPress()){
11117             this.fireEvent("specialkey", this, e);
11118         }
11119     },
11120     focus : function (selectText){
11121         if(this.rendered){
11122             this.inputEl().focus();
11123             if(selectText === true){
11124                 this.inputEl().dom.select();
11125             }
11126         }
11127         return this;
11128     } ,
11129     
11130     onFocus : function(){
11131         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11132            // this.el.addClass(this.focusClass);
11133         }
11134         if(!this.hasFocus){
11135             this.hasFocus = true;
11136             this.startValue = this.getValue();
11137             this.fireEvent("focus", this);
11138         }
11139     },
11140     
11141     beforeBlur : Roo.emptyFn,
11142
11143     
11144     // private
11145     onBlur : function(){
11146         this.beforeBlur();
11147         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11148             //this.el.removeClass(this.focusClass);
11149         }
11150         this.hasFocus = false;
11151         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11152             this.validate();
11153         }
11154         var v = this.getValue();
11155         if(String(v) !== String(this.startValue)){
11156             this.fireEvent('change', this, v, this.startValue);
11157         }
11158         this.fireEvent("blur", this);
11159     },
11160     
11161     onChange : function(e)
11162     {
11163         var v = this.getValue();
11164         if(String(v) !== String(this.startValue)){
11165             this.fireEvent('change', this, v, this.startValue);
11166         }
11167         
11168     },
11169     
11170     /**
11171      * Resets the current field value to the originally loaded value and clears any validation messages
11172      */
11173     reset : function(){
11174         this.setValue(this.originalValue);
11175         this.validate();
11176     },
11177      /**
11178      * Returns the name of the field
11179      * @return {Mixed} name The name field
11180      */
11181     getName: function(){
11182         return this.name;
11183     },
11184      /**
11185      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11186      * @return {Mixed} value The field value
11187      */
11188     getValue : function(){
11189         
11190         var v = this.inputEl().getValue();
11191         
11192         return v;
11193     },
11194     /**
11195      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11196      * @return {Mixed} value The field value
11197      */
11198     getRawValue : function(){
11199         var v = this.inputEl().getValue();
11200         
11201         return v;
11202     },
11203     
11204     /**
11205      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11206      * @param {Mixed} value The value to set
11207      */
11208     setRawValue : function(v){
11209         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11210     },
11211     
11212     selectText : function(start, end){
11213         var v = this.getRawValue();
11214         if(v.length > 0){
11215             start = start === undefined ? 0 : start;
11216             end = end === undefined ? v.length : end;
11217             var d = this.inputEl().dom;
11218             if(d.setSelectionRange){
11219                 d.setSelectionRange(start, end);
11220             }else if(d.createTextRange){
11221                 var range = d.createTextRange();
11222                 range.moveStart("character", start);
11223                 range.moveEnd("character", v.length-end);
11224                 range.select();
11225             }
11226         }
11227     },
11228     
11229     /**
11230      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11231      * @param {Mixed} value The value to set
11232      */
11233     setValue : function(v){
11234         this.value = v;
11235         if(this.rendered){
11236             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11237             this.validate();
11238         }
11239     },
11240     
11241     /*
11242     processValue : function(value){
11243         if(this.stripCharsRe){
11244             var newValue = value.replace(this.stripCharsRe, '');
11245             if(newValue !== value){
11246                 this.setRawValue(newValue);
11247                 return newValue;
11248             }
11249         }
11250         return value;
11251     },
11252   */
11253     preFocus : function(){
11254         
11255         if(this.selectOnFocus){
11256             this.inputEl().dom.select();
11257         }
11258     },
11259     filterKeys : function(e){
11260         var k = e.getKey();
11261         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11262             return;
11263         }
11264         var c = e.getCharCode(), cc = String.fromCharCode(c);
11265         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11266             return;
11267         }
11268         if(!this.maskRe.test(cc)){
11269             e.stopEvent();
11270         }
11271     },
11272      /**
11273      * Clear any invalid styles/messages for this field
11274      */
11275     clearInvalid : function(){
11276         
11277         if(!this.el || this.preventMark){ // not rendered
11278             return;
11279         }
11280         
11281         
11282         this.el.removeClass([this.invalidClass, 'is-invalid']);
11283         
11284         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11285             
11286             var feedback = this.el.select('.form-control-feedback', true).first();
11287             
11288             if(feedback){
11289                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11290             }
11291             
11292         }
11293         
11294         if(this.indicator){
11295             this.indicator.removeClass('visible');
11296             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11297         }
11298         
11299         this.fireEvent('valid', this);
11300     },
11301     
11302      /**
11303      * Mark this field as valid
11304      */
11305     markValid : function()
11306     {
11307         if(!this.el  || this.preventMark){ // not rendered...
11308             return;
11309         }
11310         
11311         this.el.removeClass([this.invalidClass, this.validClass]);
11312         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11313
11314         var feedback = this.el.select('.form-control-feedback', true).first();
11315             
11316         if(feedback){
11317             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11318         }
11319         
11320         if(this.indicator){
11321             this.indicator.removeClass('visible');
11322             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11323         }
11324         
11325         if(this.disabled){
11326             return;
11327         }
11328         
11329            
11330         if(this.allowBlank && !this.getRawValue().length){
11331             return;
11332         }
11333         if (Roo.bootstrap.version == 3) {
11334             this.el.addClass(this.validClass);
11335         } else {
11336             this.inputEl().addClass('is-valid');
11337         }
11338
11339         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11340             
11341             var feedback = this.el.select('.form-control-feedback', true).first();
11342             
11343             if(feedback){
11344                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11345                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11346             }
11347             
11348         }
11349         
11350         this.fireEvent('valid', this);
11351     },
11352     
11353      /**
11354      * Mark this field as invalid
11355      * @param {String} msg The validation message
11356      */
11357     markInvalid : function(msg)
11358     {
11359         if(!this.el  || this.preventMark){ // not rendered
11360             return;
11361         }
11362         
11363         this.el.removeClass([this.invalidClass, this.validClass]);
11364         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11365         
11366         var feedback = this.el.select('.form-control-feedback', true).first();
11367             
11368         if(feedback){
11369             this.el.select('.form-control-feedback', true).first().removeClass(
11370                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11371         }
11372
11373         if(this.disabled){
11374             return;
11375         }
11376         
11377         if(this.allowBlank && !this.getRawValue().length){
11378             return;
11379         }
11380         
11381         if(this.indicator){
11382             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11383             this.indicator.addClass('visible');
11384         }
11385         if (Roo.bootstrap.version == 3) {
11386             this.el.addClass(this.invalidClass);
11387         } else {
11388             this.inputEl().addClass('is-invalid');
11389         }
11390         
11391         
11392         
11393         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11394             
11395             var feedback = this.el.select('.form-control-feedback', true).first();
11396             
11397             if(feedback){
11398                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11399                 
11400                 if(this.getValue().length || this.forceFeedback){
11401                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11402                 }
11403                 
11404             }
11405             
11406         }
11407         
11408         this.fireEvent('invalid', this, msg);
11409     },
11410     // private
11411     SafariOnKeyDown : function(event)
11412     {
11413         // this is a workaround for a password hang bug on chrome/ webkit.
11414         if (this.inputEl().dom.type != 'password') {
11415             return;
11416         }
11417         
11418         var isSelectAll = false;
11419         
11420         if(this.inputEl().dom.selectionEnd > 0){
11421             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11422         }
11423         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11424             event.preventDefault();
11425             this.setValue('');
11426             return;
11427         }
11428         
11429         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11430             
11431             event.preventDefault();
11432             // this is very hacky as keydown always get's upper case.
11433             //
11434             var cc = String.fromCharCode(event.getCharCode());
11435             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11436             
11437         }
11438     },
11439     adjustWidth : function(tag, w){
11440         tag = tag.toLowerCase();
11441         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11442             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11443                 if(tag == 'input'){
11444                     return w + 2;
11445                 }
11446                 if(tag == 'textarea'){
11447                     return w-2;
11448                 }
11449             }else if(Roo.isOpera){
11450                 if(tag == 'input'){
11451                     return w + 2;
11452                 }
11453                 if(tag == 'textarea'){
11454                     return w-2;
11455                 }
11456             }
11457         }
11458         return w;
11459     },
11460     
11461     setFieldLabel : function(v)
11462     {
11463         if(!this.rendered){
11464             return;
11465         }
11466         
11467         if(this.indicatorEl()){
11468             var ar = this.el.select('label > span',true);
11469             
11470             if (ar.elements.length) {
11471                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11472                 this.fieldLabel = v;
11473                 return;
11474             }
11475             
11476             var br = this.el.select('label',true);
11477             
11478             if(br.elements.length) {
11479                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11480                 this.fieldLabel = v;
11481                 return;
11482             }
11483             
11484             Roo.log('Cannot Found any of label > span || label in input');
11485             return;
11486         }
11487         
11488         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11489         this.fieldLabel = v;
11490         
11491         
11492     }
11493 });
11494
11495  
11496 /*
11497  * - LGPL
11498  *
11499  * Input
11500  * 
11501  */
11502
11503 /**
11504  * @class Roo.bootstrap.TextArea
11505  * @extends Roo.bootstrap.Input
11506  * Bootstrap TextArea class
11507  * @cfg {Number} cols Specifies the visible width of a text area
11508  * @cfg {Number} rows Specifies the visible number of lines in a text area
11509  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11510  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11511  * @cfg {string} html text
11512  * 
11513  * @constructor
11514  * Create a new TextArea
11515  * @param {Object} config The config object
11516  */
11517
11518 Roo.bootstrap.TextArea = function(config){
11519     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11520    
11521 };
11522
11523 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11524      
11525     cols : false,
11526     rows : 5,
11527     readOnly : false,
11528     warp : 'soft',
11529     resize : false,
11530     value: false,
11531     html: false,
11532     
11533     getAutoCreate : function(){
11534         
11535         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11536         
11537         var id = Roo.id();
11538         
11539         var cfg = {};
11540         
11541         if(this.inputType != 'hidden'){
11542             cfg.cls = 'form-group' //input-group
11543         }
11544         
11545         var input =  {
11546             tag: 'textarea',
11547             id : id,
11548             warp : this.warp,
11549             rows : this.rows,
11550             value : this.value || '',
11551             html: this.html || '',
11552             cls : 'form-control',
11553             placeholder : this.placeholder || '' 
11554             
11555         };
11556         
11557         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11558             input.maxLength = this.maxLength;
11559         }
11560         
11561         if(this.resize){
11562             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11563         }
11564         
11565         if(this.cols){
11566             input.cols = this.cols;
11567         }
11568         
11569         if (this.readOnly) {
11570             input.readonly = true;
11571         }
11572         
11573         if (this.name) {
11574             input.name = this.name;
11575         }
11576         
11577         if (this.size) {
11578             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11579         }
11580         
11581         var settings=this;
11582         ['xs','sm','md','lg'].map(function(size){
11583             if (settings[size]) {
11584                 cfg.cls += ' col-' + size + '-' + settings[size];
11585             }
11586         });
11587         
11588         var inputblock = input;
11589         
11590         if(this.hasFeedback && !this.allowBlank){
11591             
11592             var feedback = {
11593                 tag: 'span',
11594                 cls: 'glyphicon form-control-feedback'
11595             };
11596
11597             inputblock = {
11598                 cls : 'has-feedback',
11599                 cn :  [
11600                     input,
11601                     feedback
11602                 ] 
11603             };  
11604         }
11605         
11606         
11607         if (this.before || this.after) {
11608             
11609             inputblock = {
11610                 cls : 'input-group',
11611                 cn :  [] 
11612             };
11613             if (this.before) {
11614                 inputblock.cn.push({
11615                     tag :'span',
11616                     cls : 'input-group-addon',
11617                     html : this.before
11618                 });
11619             }
11620             
11621             inputblock.cn.push(input);
11622             
11623             if(this.hasFeedback && !this.allowBlank){
11624                 inputblock.cls += ' has-feedback';
11625                 inputblock.cn.push(feedback);
11626             }
11627             
11628             if (this.after) {
11629                 inputblock.cn.push({
11630                     tag :'span',
11631                     cls : 'input-group-addon',
11632                     html : this.after
11633                 });
11634             }
11635             
11636         }
11637         
11638         if (align ==='left' && this.fieldLabel.length) {
11639             cfg.cn = [
11640                 {
11641                     tag: 'label',
11642                     'for' :  id,
11643                     cls : 'control-label',
11644                     html : this.fieldLabel
11645                 },
11646                 {
11647                     cls : "",
11648                     cn: [
11649                         inputblock
11650                     ]
11651                 }
11652
11653             ];
11654             
11655             if(this.labelWidth > 12){
11656                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11657             }
11658
11659             if(this.labelWidth < 13 && this.labelmd == 0){
11660                 this.labelmd = this.labelWidth;
11661             }
11662
11663             if(this.labellg > 0){
11664                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11665                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11666             }
11667
11668             if(this.labelmd > 0){
11669                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11670                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11671             }
11672
11673             if(this.labelsm > 0){
11674                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11675                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11676             }
11677
11678             if(this.labelxs > 0){
11679                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11680                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11681             }
11682             
11683         } else if ( this.fieldLabel.length) {
11684             cfg.cn = [
11685
11686                {
11687                    tag: 'label',
11688                    //cls : 'input-group-addon',
11689                    html : this.fieldLabel
11690
11691                },
11692
11693                inputblock
11694
11695            ];
11696
11697         } else {
11698
11699             cfg.cn = [
11700
11701                 inputblock
11702
11703             ];
11704                 
11705         }
11706         
11707         if (this.disabled) {
11708             input.disabled=true;
11709         }
11710         
11711         return cfg;
11712         
11713     },
11714     /**
11715      * return the real textarea element.
11716      */
11717     inputEl: function ()
11718     {
11719         return this.el.select('textarea.form-control',true).first();
11720     },
11721     
11722     /**
11723      * Clear any invalid styles/messages for this field
11724      */
11725     clearInvalid : function()
11726     {
11727         
11728         if(!this.el || this.preventMark){ // not rendered
11729             return;
11730         }
11731         
11732         var label = this.el.select('label', true).first();
11733         var icon = this.el.select('i.fa-star', true).first();
11734         
11735         if(label && icon){
11736             icon.remove();
11737         }
11738         this.el.removeClass( this.validClass);
11739         this.inputEl().removeClass('is-invalid');
11740          
11741         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11742             
11743             var feedback = this.el.select('.form-control-feedback', true).first();
11744             
11745             if(feedback){
11746                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11747             }
11748             
11749         }
11750         
11751         this.fireEvent('valid', this);
11752     },
11753     
11754      /**
11755      * Mark this field as valid
11756      */
11757     markValid : function()
11758     {
11759         if(!this.el  || this.preventMark){ // not rendered
11760             return;
11761         }
11762         
11763         this.el.removeClass([this.invalidClass, this.validClass]);
11764         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11765         
11766         var feedback = this.el.select('.form-control-feedback', true).first();
11767             
11768         if(feedback){
11769             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11770         }
11771
11772         if(this.disabled || this.allowBlank){
11773             return;
11774         }
11775         
11776         var label = this.el.select('label', true).first();
11777         var icon = this.el.select('i.fa-star', true).first();
11778         
11779         if(label && icon){
11780             icon.remove();
11781         }
11782         if (Roo.bootstrap.version == 3) {
11783             this.el.addClass(this.validClass);
11784         } else {
11785             this.inputEl().addClass('is-valid');
11786         }
11787         
11788         
11789         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11790             
11791             var feedback = this.el.select('.form-control-feedback', true).first();
11792             
11793             if(feedback){
11794                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11795                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11796             }
11797             
11798         }
11799         
11800         this.fireEvent('valid', this);
11801     },
11802     
11803      /**
11804      * Mark this field as invalid
11805      * @param {String} msg The validation message
11806      */
11807     markInvalid : function(msg)
11808     {
11809         if(!this.el  || this.preventMark){ // not rendered
11810             return;
11811         }
11812         
11813         this.el.removeClass([this.invalidClass, this.validClass]);
11814         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11815         
11816         var feedback = this.el.select('.form-control-feedback', true).first();
11817             
11818         if(feedback){
11819             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11820         }
11821
11822         if(this.disabled || this.allowBlank){
11823             return;
11824         }
11825         
11826         var label = this.el.select('label', true).first();
11827         var icon = this.el.select('i.fa-star', true).first();
11828         
11829         if(!this.getValue().length && label && !icon){
11830             this.el.createChild({
11831                 tag : 'i',
11832                 cls : 'text-danger fa fa-lg fa-star',
11833                 tooltip : 'This field is required',
11834                 style : 'margin-right:5px;'
11835             }, label, true);
11836         }
11837         
11838         if (Roo.bootstrap.version == 3) {
11839             this.el.addClass(this.invalidClass);
11840         } else {
11841             this.inputEl().addClass('is-invalid');
11842         }
11843         
11844         // fixme ... this may be depricated need to test..
11845         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11846             
11847             var feedback = this.el.select('.form-control-feedback', true).first();
11848             
11849             if(feedback){
11850                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11851                 
11852                 if(this.getValue().length || this.forceFeedback){
11853                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11854                 }
11855                 
11856             }
11857             
11858         }
11859         
11860         this.fireEvent('invalid', this, msg);
11861     }
11862 });
11863
11864  
11865 /*
11866  * - LGPL
11867  *
11868  * trigger field - base class for combo..
11869  * 
11870  */
11871  
11872 /**
11873  * @class Roo.bootstrap.TriggerField
11874  * @extends Roo.bootstrap.Input
11875  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11876  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11877  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11878  * for which you can provide a custom implementation.  For example:
11879  * <pre><code>
11880 var trigger = new Roo.bootstrap.TriggerField();
11881 trigger.onTriggerClick = myTriggerFn;
11882 trigger.applyTo('my-field');
11883 </code></pre>
11884  *
11885  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11886  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11887  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11888  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11889  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11890
11891  * @constructor
11892  * Create a new TriggerField.
11893  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11894  * to the base TextField)
11895  */
11896 Roo.bootstrap.TriggerField = function(config){
11897     this.mimicing = false;
11898     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11899 };
11900
11901 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11902     /**
11903      * @cfg {String} triggerClass A CSS class to apply to the trigger
11904      */
11905      /**
11906      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11907      */
11908     hideTrigger:false,
11909
11910     /**
11911      * @cfg {Boolean} removable (true|false) special filter default false
11912      */
11913     removable : false,
11914     
11915     /** @cfg {Boolean} grow @hide */
11916     /** @cfg {Number} growMin @hide */
11917     /** @cfg {Number} growMax @hide */
11918
11919     /**
11920      * @hide 
11921      * @method
11922      */
11923     autoSize: Roo.emptyFn,
11924     // private
11925     monitorTab : true,
11926     // private
11927     deferHeight : true,
11928
11929     
11930     actionMode : 'wrap',
11931     
11932     caret : false,
11933     
11934     
11935     getAutoCreate : function(){
11936        
11937         var align = this.labelAlign || this.parentLabelAlign();
11938         
11939         var id = Roo.id();
11940         
11941         var cfg = {
11942             cls: 'form-group' //input-group
11943         };
11944         
11945         
11946         var input =  {
11947             tag: 'input',
11948             id : id,
11949             type : this.inputType,
11950             cls : 'form-control',
11951             autocomplete: 'new-password',
11952             placeholder : this.placeholder || '' 
11953             
11954         };
11955         if (this.name) {
11956             input.name = this.name;
11957         }
11958         if (this.size) {
11959             input.cls += ' input-' + this.size;
11960         }
11961         
11962         if (this.disabled) {
11963             input.disabled=true;
11964         }
11965         
11966         var inputblock = input;
11967         
11968         if(this.hasFeedback && !this.allowBlank){
11969             
11970             var feedback = {
11971                 tag: 'span',
11972                 cls: 'glyphicon form-control-feedback'
11973             };
11974             
11975             if(this.removable && !this.editable  ){
11976                 inputblock = {
11977                     cls : 'has-feedback',
11978                     cn :  [
11979                         inputblock,
11980                         {
11981                             tag: 'button',
11982                             html : 'x',
11983                             cls : 'roo-combo-removable-btn close'
11984                         },
11985                         feedback
11986                     ] 
11987                 };
11988             } else {
11989                 inputblock = {
11990                     cls : 'has-feedback',
11991                     cn :  [
11992                         inputblock,
11993                         feedback
11994                     ] 
11995                 };
11996             }
11997
11998         } else {
11999             if(this.removable && !this.editable ){
12000                 inputblock = {
12001                     cls : 'roo-removable',
12002                     cn :  [
12003                         inputblock,
12004                         {
12005                             tag: 'button',
12006                             html : 'x',
12007                             cls : 'roo-combo-removable-btn close'
12008                         }
12009                     ] 
12010                 };
12011             }
12012         }
12013         
12014         if (this.before || this.after) {
12015             
12016             inputblock = {
12017                 cls : 'input-group',
12018                 cn :  [] 
12019             };
12020             if (this.before) {
12021                 inputblock.cn.push({
12022                     tag :'span',
12023                     cls : 'input-group-addon input-group-prepend input-group-text',
12024                     html : this.before
12025                 });
12026             }
12027             
12028             inputblock.cn.push(input);
12029             
12030             if(this.hasFeedback && !this.allowBlank){
12031                 inputblock.cls += ' has-feedback';
12032                 inputblock.cn.push(feedback);
12033             }
12034             
12035             if (this.after) {
12036                 inputblock.cn.push({
12037                     tag :'span',
12038                     cls : 'input-group-addon input-group-append input-group-text',
12039                     html : this.after
12040                 });
12041             }
12042             
12043         };
12044         
12045       
12046         
12047         var ibwrap = inputblock;
12048         
12049         if(this.multiple){
12050             ibwrap = {
12051                 tag: 'ul',
12052                 cls: 'roo-select2-choices',
12053                 cn:[
12054                     {
12055                         tag: 'li',
12056                         cls: 'roo-select2-search-field',
12057                         cn: [
12058
12059                             inputblock
12060                         ]
12061                     }
12062                 ]
12063             };
12064                 
12065         }
12066         
12067         var combobox = {
12068             cls: 'roo-select2-container input-group',
12069             cn: [
12070                  {
12071                     tag: 'input',
12072                     type : 'hidden',
12073                     cls: 'form-hidden-field'
12074                 },
12075                 ibwrap
12076             ]
12077         };
12078         
12079         if(!this.multiple && this.showToggleBtn){
12080             
12081             var caret = {
12082                         tag: 'span',
12083                         cls: 'caret'
12084              };
12085             if (this.caret != false) {
12086                 caret = {
12087                      tag: 'i',
12088                      cls: 'fa fa-' + this.caret
12089                 };
12090                 
12091             }
12092             
12093             combobox.cn.push({
12094                 tag :'span',
12095                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12096                 cn : [
12097                     Roo.bootstrap.version == 3 ? caret : '',
12098                     {
12099                         tag: 'span',
12100                         cls: 'combobox-clear',
12101                         cn  : [
12102                             {
12103                                 tag : 'i',
12104                                 cls: 'icon-remove'
12105                             }
12106                         ]
12107                     }
12108                 ]
12109
12110             })
12111         }
12112         
12113         if(this.multiple){
12114             combobox.cls += ' roo-select2-container-multi';
12115         }
12116          var indicator = {
12117             tag : 'i',
12118             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12119             tooltip : 'This field is required'
12120         };
12121         if (Roo.bootstrap.version == 4) {
12122             indicator = {
12123                 tag : 'i',
12124                 style : 'display:none'
12125             };
12126         }
12127         
12128         
12129         if (align ==='left' && this.fieldLabel.length) {
12130             
12131             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12132
12133             cfg.cn = [
12134                 indicator,
12135                 {
12136                     tag: 'label',
12137                     'for' :  id,
12138                     cls : 'control-label',
12139                     html : this.fieldLabel
12140
12141                 },
12142                 {
12143                     cls : "", 
12144                     cn: [
12145                         combobox
12146                     ]
12147                 }
12148
12149             ];
12150             
12151             var labelCfg = cfg.cn[1];
12152             var contentCfg = cfg.cn[2];
12153             
12154             if(this.indicatorpos == 'right'){
12155                 cfg.cn = [
12156                     {
12157                         tag: 'label',
12158                         'for' :  id,
12159                         cls : 'control-label',
12160                         cn : [
12161                             {
12162                                 tag : 'span',
12163                                 html : this.fieldLabel
12164                             },
12165                             indicator
12166                         ]
12167                     },
12168                     {
12169                         cls : "", 
12170                         cn: [
12171                             combobox
12172                         ]
12173                     }
12174
12175                 ];
12176                 
12177                 labelCfg = cfg.cn[0];
12178                 contentCfg = cfg.cn[1];
12179             }
12180             
12181             if(this.labelWidth > 12){
12182                 labelCfg.style = "width: " + this.labelWidth + 'px';
12183             }
12184             
12185             if(this.labelWidth < 13 && this.labelmd == 0){
12186                 this.labelmd = this.labelWidth;
12187             }
12188             
12189             if(this.labellg > 0){
12190                 labelCfg.cls += ' col-lg-' + this.labellg;
12191                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12192             }
12193             
12194             if(this.labelmd > 0){
12195                 labelCfg.cls += ' col-md-' + this.labelmd;
12196                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12197             }
12198             
12199             if(this.labelsm > 0){
12200                 labelCfg.cls += ' col-sm-' + this.labelsm;
12201                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12202             }
12203             
12204             if(this.labelxs > 0){
12205                 labelCfg.cls += ' col-xs-' + this.labelxs;
12206                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12207             }
12208             
12209         } else if ( this.fieldLabel.length) {
12210 //                Roo.log(" label");
12211             cfg.cn = [
12212                 indicator,
12213                {
12214                    tag: 'label',
12215                    //cls : 'input-group-addon',
12216                    html : this.fieldLabel
12217
12218                },
12219
12220                combobox
12221
12222             ];
12223             
12224             if(this.indicatorpos == 'right'){
12225                 
12226                 cfg.cn = [
12227                     {
12228                        tag: 'label',
12229                        cn : [
12230                            {
12231                                tag : 'span',
12232                                html : this.fieldLabel
12233                            },
12234                            indicator
12235                        ]
12236
12237                     },
12238                     combobox
12239
12240                 ];
12241
12242             }
12243
12244         } else {
12245             
12246 //                Roo.log(" no label && no align");
12247                 cfg = combobox
12248                      
12249                 
12250         }
12251         
12252         var settings=this;
12253         ['xs','sm','md','lg'].map(function(size){
12254             if (settings[size]) {
12255                 cfg.cls += ' col-' + size + '-' + settings[size];
12256             }
12257         });
12258         
12259         return cfg;
12260         
12261     },
12262     
12263     
12264     
12265     // private
12266     onResize : function(w, h){
12267 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12268 //        if(typeof w == 'number'){
12269 //            var x = w - this.trigger.getWidth();
12270 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12271 //            this.trigger.setStyle('left', x+'px');
12272 //        }
12273     },
12274
12275     // private
12276     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12277
12278     // private
12279     getResizeEl : function(){
12280         return this.inputEl();
12281     },
12282
12283     // private
12284     getPositionEl : function(){
12285         return this.inputEl();
12286     },
12287
12288     // private
12289     alignErrorIcon : function(){
12290         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12291     },
12292
12293     // private
12294     initEvents : function(){
12295         
12296         this.createList();
12297         
12298         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12299         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12300         if(!this.multiple && this.showToggleBtn){
12301             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12302             if(this.hideTrigger){
12303                 this.trigger.setDisplayed(false);
12304             }
12305             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.multiple){
12309             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12310         }
12311         
12312         if(this.removable && !this.editable && !this.tickable){
12313             var close = this.closeTriggerEl();
12314             
12315             if(close){
12316                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12317                 close.on('click', this.removeBtnClick, this, close);
12318             }
12319         }
12320         
12321         //this.trigger.addClassOnOver('x-form-trigger-over');
12322         //this.trigger.addClassOnClick('x-form-trigger-click');
12323         
12324         //if(!this.width){
12325         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12326         //}
12327     },
12328     
12329     closeTriggerEl : function()
12330     {
12331         var close = this.el.select('.roo-combo-removable-btn', true).first();
12332         return close ? close : false;
12333     },
12334     
12335     removeBtnClick : function(e, h, el)
12336     {
12337         e.preventDefault();
12338         
12339         if(this.fireEvent("remove", this) !== false){
12340             this.reset();
12341             this.fireEvent("afterremove", this)
12342         }
12343     },
12344     
12345     createList : function()
12346     {
12347         this.list = Roo.get(document.body).createChild({
12348             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12349             cls: 'typeahead typeahead-long dropdown-menu shadow',
12350             style: 'display:none'
12351         });
12352         
12353         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12354         
12355     },
12356
12357     // private
12358     initTrigger : function(){
12359        
12360     },
12361
12362     // private
12363     onDestroy : function(){
12364         if(this.trigger){
12365             this.trigger.removeAllListeners();
12366           //  this.trigger.remove();
12367         }
12368         //if(this.wrap){
12369         //    this.wrap.remove();
12370         //}
12371         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12372     },
12373
12374     // private
12375     onFocus : function(){
12376         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12377         /*
12378         if(!this.mimicing){
12379             this.wrap.addClass('x-trigger-wrap-focus');
12380             this.mimicing = true;
12381             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12382             if(this.monitorTab){
12383                 this.el.on("keydown", this.checkTab, this);
12384             }
12385         }
12386         */
12387     },
12388
12389     // private
12390     checkTab : function(e){
12391         if(e.getKey() == e.TAB){
12392             this.triggerBlur();
12393         }
12394     },
12395
12396     // private
12397     onBlur : function(){
12398         // do nothing
12399     },
12400
12401     // private
12402     mimicBlur : function(e, t){
12403         /*
12404         if(!this.wrap.contains(t) && this.validateBlur()){
12405             this.triggerBlur();
12406         }
12407         */
12408     },
12409
12410     // private
12411     triggerBlur : function(){
12412         this.mimicing = false;
12413         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12414         if(this.monitorTab){
12415             this.el.un("keydown", this.checkTab, this);
12416         }
12417         //this.wrap.removeClass('x-trigger-wrap-focus');
12418         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12419     },
12420
12421     // private
12422     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12423     validateBlur : function(e, t){
12424         return true;
12425     },
12426
12427     // private
12428     onDisable : function(){
12429         this.inputEl().dom.disabled = true;
12430         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12431         //if(this.wrap){
12432         //    this.wrap.addClass('x-item-disabled');
12433         //}
12434     },
12435
12436     // private
12437     onEnable : function(){
12438         this.inputEl().dom.disabled = false;
12439         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12440         //if(this.wrap){
12441         //    this.el.removeClass('x-item-disabled');
12442         //}
12443     },
12444
12445     // private
12446     onShow : function(){
12447         var ae = this.getActionEl();
12448         
12449         if(ae){
12450             ae.dom.style.display = '';
12451             ae.dom.style.visibility = 'visible';
12452         }
12453     },
12454
12455     // private
12456     
12457     onHide : function(){
12458         var ae = this.getActionEl();
12459         ae.dom.style.display = 'none';
12460     },
12461
12462     /**
12463      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12464      * by an implementing function.
12465      * @method
12466      * @param {EventObject} e
12467      */
12468     onTriggerClick : Roo.emptyFn
12469 });
12470  
12471 /*
12472 * Licence: LGPL
12473 */
12474
12475 /**
12476  * @class Roo.bootstrap.CardUploader
12477  * @extends Roo.bootstrap.Button
12478  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12479  * @cfg {Number} errorTimeout default 3000
12480  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12481  * @cfg {Array}  html The button text.
12482
12483  *
12484  * @constructor
12485  * Create a new CardUploader
12486  * @param {Object} config The config object
12487  */
12488
12489 Roo.bootstrap.CardUploader = function(config){
12490     
12491  
12492     
12493     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12494     
12495     
12496     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12497         return r.data.id
12498         });
12499     
12500     
12501 };
12502
12503 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12504     
12505      
12506     errorTimeout : 3000,
12507      
12508     images : false,
12509    
12510     fileCollection : false,
12511     allowBlank : true,
12512     
12513     getAutoCreate : function()
12514     {
12515         
12516         var cfg =  {
12517             cls :'form-group' ,
12518             cn : [
12519                
12520                 {
12521                     tag: 'label',
12522                    //cls : 'input-group-addon',
12523                     html : this.fieldLabel
12524
12525                 },
12526
12527                 {
12528                     tag: 'input',
12529                     type : 'hidden',
12530                     value : this.value,
12531                     cls : 'd-none  form-control'
12532                 },
12533                 
12534                 {
12535                     tag: 'input',
12536                     multiple : 'multiple',
12537                     type : 'file',
12538                     cls : 'd-none  roo-card-upload-selector'
12539                 },
12540                 
12541                 {
12542                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12543                 },
12544                 {
12545                     cls : 'card-columns roo-card-uploader-container'
12546                 }
12547
12548             ]
12549         };
12550            
12551          
12552         return cfg;
12553     },
12554     
12555     getChildContainer : function() /// what children are added to.
12556     {
12557         return this.containerEl;
12558     },
12559    
12560     getButtonContainer : function() /// what children are added to.
12561     {
12562         return this.el.select(".roo-card-uploader-button-container").first();
12563     },
12564    
12565     initEvents : function()
12566     {
12567         
12568         Roo.bootstrap.Input.prototype.initEvents.call(this);
12569         
12570         var t = this;
12571         this.addxtype({
12572             xns: Roo.bootstrap,
12573
12574             xtype : 'Button',
12575             container_method : 'getButtonContainer' ,            
12576             html :  this.html, // fix changable?
12577             cls : 'w-100 ',
12578             listeners : {
12579                 'click' : function(btn, e) {
12580                     t.onClick(e);
12581                 }
12582             }
12583         });
12584         
12585         
12586         
12587         
12588         this.urlAPI = (window.createObjectURL && window) || 
12589                                 (window.URL && URL.revokeObjectURL && URL) || 
12590                                 (window.webkitURL && webkitURL);
12591                         
12592          
12593          
12594          
12595         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12596         
12597         this.selectorEl.on('change', this.onFileSelected, this);
12598         if (this.images) {
12599             var t = this;
12600             this.images.forEach(function(img) {
12601                 t.addCard(img)
12602             });
12603             this.images = false;
12604         }
12605         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12606          
12607        
12608     },
12609     
12610    
12611     onClick : function(e)
12612     {
12613         e.preventDefault();
12614          
12615         this.selectorEl.dom.click();
12616          
12617     },
12618     
12619     onFileSelected : function(e)
12620     {
12621         e.preventDefault();
12622         
12623         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12624             return;
12625         }
12626         
12627         Roo.each(this.selectorEl.dom.files, function(file){    
12628             this.addFile(file);
12629         }, this);
12630          
12631     },
12632     
12633       
12634     
12635       
12636     
12637     addFile : function(file)
12638     {
12639            
12640         if(typeof(file) === 'string'){
12641             throw "Add file by name?"; // should not happen
12642             return;
12643         }
12644         
12645         if(!file || !this.urlAPI){
12646             return;
12647         }
12648         
12649         // file;
12650         // file.type;
12651         
12652         var _this = this;
12653         
12654         
12655         var url = _this.urlAPI.createObjectURL( file);
12656            
12657         this.addCard({
12658             id : Roo.bootstrap.CardUploader.ID--,
12659             is_uploaded : false,
12660             src : url,
12661             title : file.name,
12662             mimetype : file.type,
12663             preview : false,
12664             is_deleted : 0
12665         })
12666         
12667     },
12668     
12669     addCard : function (data)
12670     {
12671         // hidden input element?
12672         // if the file is not an image...
12673         //then we need to use something other that and header_image
12674         var t = this;
12675         //   remove.....
12676         var footer = [
12677             {
12678                 xns : Roo.bootstrap,
12679                 xtype : 'CardFooter',
12680                 items: [
12681                     {
12682                         xns : Roo.bootstrap,
12683                         xtype : 'Element',
12684                         cls : 'd-flex',
12685                         items : [
12686                             
12687                             {
12688                                 xns : Roo.bootstrap,
12689                                 xtype : 'Button',
12690                                 html : String.format("<small>{0}</small>", data.title),
12691                                 cls : 'col-11 text-left',
12692                                 size: 'sm',
12693                                 weight: 'link',
12694                                 fa : 'download',
12695                                 listeners : {
12696                                     click : function() {
12697                                         this.downloadCard(data.id)
12698                                     }
12699                                 }
12700                             },
12701                           
12702                             {
12703                                 xns : Roo.bootstrap,
12704                                 xtype : 'Button',
12705                                 
12706                                 size : 'sm',
12707                                 weight: 'danger',
12708                                 cls : 'col-1',
12709                                 fa : 'times',
12710                                 listeners : {
12711                                     click : function() {
12712                                         t.removeCard(data.id)
12713                                     }
12714                                 }
12715                             }
12716                         ]
12717                     }
12718                     
12719                 ] 
12720             }
12721             
12722         ];
12723
12724         var cn = this.addxtype(
12725             {
12726                  
12727                 xns : Roo.bootstrap,
12728                 xtype : 'Card',
12729                 closeable : true,
12730                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12731                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12732                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12733                 data : data,
12734                 html : false,
12735                  
12736                 items : footer,
12737                 initEvents : function() {
12738                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12739                     this.imgEl = this.el.select('.card-img-top').first();
12740                     if (this.imgEl) {
12741                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12742                         this.imgEl.set({ 'pointer' : 'cursor' });
12743                                   
12744                     }
12745                     
12746                   
12747                 }
12748                 
12749             }
12750         );
12751         // dont' really need ot update items.
12752         // this.items.push(cn);
12753         this.fileCollection.add(cn);
12754         this.updateInput();
12755         
12756     },
12757     removeCard : function(id)
12758     {
12759         
12760         var card  = this.fileCollection.get(id);
12761         card.data.is_deleted = 1;
12762         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12763         this.fileCollection.remove(card);
12764         //this.items = this.items.filter(function(e) { return e != card });
12765         // dont' really need ot update items.
12766         card.el.dom.parentNode.removeChild(card.el.dom);
12767         
12768     },
12769     reset: function()
12770     {
12771         this.fileCollection.each(function(card) {
12772             card.el.dom.parentNode.removeChild(card.el.dom);    
12773         });
12774         this.fileCollection.clear();
12775         this.updateInput();
12776     },
12777     
12778     updateInput : function()
12779     {
12780         var data = [];
12781         this.fileCollection.each(function(e) {
12782             data.push(e.data);
12783         });
12784         
12785         this.inputEl().dom.value = JSON.stringify(data);
12786     }
12787     
12788     
12789 });
12790
12791
12792 Roo.bootstrap.CardUploader.ID = -1;/*
12793  * Based on:
12794  * Ext JS Library 1.1.1
12795  * Copyright(c) 2006-2007, Ext JS, LLC.
12796  *
12797  * Originally Released Under LGPL - original licence link has changed is not relivant.
12798  *
12799  * Fork - LGPL
12800  * <script type="text/javascript">
12801  */
12802
12803
12804 /**
12805  * @class Roo.data.SortTypes
12806  * @singleton
12807  * Defines the default sorting (casting?) comparison functions used when sorting data.
12808  */
12809 Roo.data.SortTypes = {
12810     /**
12811      * Default sort that does nothing
12812      * @param {Mixed} s The value being converted
12813      * @return {Mixed} The comparison value
12814      */
12815     none : function(s){
12816         return s;
12817     },
12818     
12819     /**
12820      * The regular expression used to strip tags
12821      * @type {RegExp}
12822      * @property
12823      */
12824     stripTagsRE : /<\/?[^>]+>/gi,
12825     
12826     /**
12827      * Strips all HTML tags to sort on text only
12828      * @param {Mixed} s The value being converted
12829      * @return {String} The comparison value
12830      */
12831     asText : function(s){
12832         return String(s).replace(this.stripTagsRE, "");
12833     },
12834     
12835     /**
12836      * Strips all HTML tags to sort on text only - Case insensitive
12837      * @param {Mixed} s The value being converted
12838      * @return {String} The comparison value
12839      */
12840     asUCText : function(s){
12841         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12842     },
12843     
12844     /**
12845      * Case insensitive string
12846      * @param {Mixed} s The value being converted
12847      * @return {String} The comparison value
12848      */
12849     asUCString : function(s) {
12850         return String(s).toUpperCase();
12851     },
12852     
12853     /**
12854      * Date sorting
12855      * @param {Mixed} s The value being converted
12856      * @return {Number} The comparison value
12857      */
12858     asDate : function(s) {
12859         if(!s){
12860             return 0;
12861         }
12862         if(s instanceof Date){
12863             return s.getTime();
12864         }
12865         return Date.parse(String(s));
12866     },
12867     
12868     /**
12869      * Float sorting
12870      * @param {Mixed} s The value being converted
12871      * @return {Float} The comparison value
12872      */
12873     asFloat : function(s) {
12874         var val = parseFloat(String(s).replace(/,/g, ""));
12875         if(isNaN(val)) {
12876             val = 0;
12877         }
12878         return val;
12879     },
12880     
12881     /**
12882      * Integer sorting
12883      * @param {Mixed} s The value being converted
12884      * @return {Number} The comparison value
12885      */
12886     asInt : function(s) {
12887         var val = parseInt(String(s).replace(/,/g, ""));
12888         if(isNaN(val)) {
12889             val = 0;
12890         }
12891         return val;
12892     }
12893 };/*
12894  * Based on:
12895  * Ext JS Library 1.1.1
12896  * Copyright(c) 2006-2007, Ext JS, LLC.
12897  *
12898  * Originally Released Under LGPL - original licence link has changed is not relivant.
12899  *
12900  * Fork - LGPL
12901  * <script type="text/javascript">
12902  */
12903
12904 /**
12905 * @class Roo.data.Record
12906  * Instances of this class encapsulate both record <em>definition</em> information, and record
12907  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12908  * to access Records cached in an {@link Roo.data.Store} object.<br>
12909  * <p>
12910  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12911  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12912  * objects.<br>
12913  * <p>
12914  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12915  * @constructor
12916  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12917  * {@link #create}. The parameters are the same.
12918  * @param {Array} data An associative Array of data values keyed by the field name.
12919  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12920  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12921  * not specified an integer id is generated.
12922  */
12923 Roo.data.Record = function(data, id){
12924     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12925     this.data = data;
12926 };
12927
12928 /**
12929  * Generate a constructor for a specific record layout.
12930  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12931  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12932  * Each field definition object may contain the following properties: <ul>
12933  * <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,
12934  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12935  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12936  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12937  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12938  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12939  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12940  * this may be omitted.</p></li>
12941  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12942  * <ul><li>auto (Default, implies no conversion)</li>
12943  * <li>string</li>
12944  * <li>int</li>
12945  * <li>float</li>
12946  * <li>boolean</li>
12947  * <li>date</li></ul></p></li>
12948  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12949  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12950  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12951  * by the Reader into an object that will be stored in the Record. It is passed the
12952  * following parameters:<ul>
12953  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12954  * </ul></p></li>
12955  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12956  * </ul>
12957  * <br>usage:<br><pre><code>
12958 var TopicRecord = Roo.data.Record.create(
12959     {name: 'title', mapping: 'topic_title'},
12960     {name: 'author', mapping: 'username'},
12961     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12962     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12963     {name: 'lastPoster', mapping: 'user2'},
12964     {name: 'excerpt', mapping: 'post_text'}
12965 );
12966
12967 var myNewRecord = new TopicRecord({
12968     title: 'Do my job please',
12969     author: 'noobie',
12970     totalPosts: 1,
12971     lastPost: new Date(),
12972     lastPoster: 'Animal',
12973     excerpt: 'No way dude!'
12974 });
12975 myStore.add(myNewRecord);
12976 </code></pre>
12977  * @method create
12978  * @static
12979  */
12980 Roo.data.Record.create = function(o){
12981     var f = function(){
12982         f.superclass.constructor.apply(this, arguments);
12983     };
12984     Roo.extend(f, Roo.data.Record);
12985     var p = f.prototype;
12986     p.fields = new Roo.util.MixedCollection(false, function(field){
12987         return field.name;
12988     });
12989     for(var i = 0, len = o.length; i < len; i++){
12990         p.fields.add(new Roo.data.Field(o[i]));
12991     }
12992     f.getField = function(name){
12993         return p.fields.get(name);  
12994     };
12995     return f;
12996 };
12997
12998 Roo.data.Record.AUTO_ID = 1000;
12999 Roo.data.Record.EDIT = 'edit';
13000 Roo.data.Record.REJECT = 'reject';
13001 Roo.data.Record.COMMIT = 'commit';
13002
13003 Roo.data.Record.prototype = {
13004     /**
13005      * Readonly flag - true if this record has been modified.
13006      * @type Boolean
13007      */
13008     dirty : false,
13009     editing : false,
13010     error: null,
13011     modified: null,
13012
13013     // private
13014     join : function(store){
13015         this.store = store;
13016     },
13017
13018     /**
13019      * Set the named field to the specified value.
13020      * @param {String} name The name of the field to set.
13021      * @param {Object} value The value to set the field to.
13022      */
13023     set : function(name, value){
13024         if(this.data[name] == value){
13025             return;
13026         }
13027         this.dirty = true;
13028         if(!this.modified){
13029             this.modified = {};
13030         }
13031         if(typeof this.modified[name] == 'undefined'){
13032             this.modified[name] = this.data[name];
13033         }
13034         this.data[name] = value;
13035         if(!this.editing && this.store){
13036             this.store.afterEdit(this);
13037         }       
13038     },
13039
13040     /**
13041      * Get the value of the named field.
13042      * @param {String} name The name of the field to get the value of.
13043      * @return {Object} The value of the field.
13044      */
13045     get : function(name){
13046         return this.data[name]; 
13047     },
13048
13049     // private
13050     beginEdit : function(){
13051         this.editing = true;
13052         this.modified = {}; 
13053     },
13054
13055     // private
13056     cancelEdit : function(){
13057         this.editing = false;
13058         delete this.modified;
13059     },
13060
13061     // private
13062     endEdit : function(){
13063         this.editing = false;
13064         if(this.dirty && this.store){
13065             this.store.afterEdit(this);
13066         }
13067     },
13068
13069     /**
13070      * Usually called by the {@link Roo.data.Store} which owns the Record.
13071      * Rejects all changes made to the Record since either creation, or the last commit operation.
13072      * Modified fields are reverted to their original values.
13073      * <p>
13074      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13075      * of reject operations.
13076      */
13077     reject : function(){
13078         var m = this.modified;
13079         for(var n in m){
13080             if(typeof m[n] != "function"){
13081                 this.data[n] = m[n];
13082             }
13083         }
13084         this.dirty = false;
13085         delete this.modified;
13086         this.editing = false;
13087         if(this.store){
13088             this.store.afterReject(this);
13089         }
13090     },
13091
13092     /**
13093      * Usually called by the {@link Roo.data.Store} which owns the Record.
13094      * Commits all changes made to the Record since either creation, or the last commit operation.
13095      * <p>
13096      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13097      * of commit operations.
13098      */
13099     commit : function(){
13100         this.dirty = false;
13101         delete this.modified;
13102         this.editing = false;
13103         if(this.store){
13104             this.store.afterCommit(this);
13105         }
13106     },
13107
13108     // private
13109     hasError : function(){
13110         return this.error != null;
13111     },
13112
13113     // private
13114     clearError : function(){
13115         this.error = null;
13116     },
13117
13118     /**
13119      * Creates a copy of this record.
13120      * @param {String} id (optional) A new record id if you don't want to use this record's id
13121      * @return {Record}
13122      */
13123     copy : function(newId) {
13124         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13125     }
13126 };/*
13127  * Based on:
13128  * Ext JS Library 1.1.1
13129  * Copyright(c) 2006-2007, Ext JS, LLC.
13130  *
13131  * Originally Released Under LGPL - original licence link has changed is not relivant.
13132  *
13133  * Fork - LGPL
13134  * <script type="text/javascript">
13135  */
13136
13137
13138
13139 /**
13140  * @class Roo.data.Store
13141  * @extends Roo.util.Observable
13142  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13143  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13144  * <p>
13145  * 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
13146  * has no knowledge of the format of the data returned by the Proxy.<br>
13147  * <p>
13148  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13149  * instances from the data object. These records are cached and made available through accessor functions.
13150  * @constructor
13151  * Creates a new Store.
13152  * @param {Object} config A config object containing the objects needed for the Store to access data,
13153  * and read the data into Records.
13154  */
13155 Roo.data.Store = function(config){
13156     this.data = new Roo.util.MixedCollection(false);
13157     this.data.getKey = function(o){
13158         return o.id;
13159     };
13160     this.baseParams = {};
13161     // private
13162     this.paramNames = {
13163         "start" : "start",
13164         "limit" : "limit",
13165         "sort" : "sort",
13166         "dir" : "dir",
13167         "multisort" : "_multisort"
13168     };
13169
13170     if(config && config.data){
13171         this.inlineData = config.data;
13172         delete config.data;
13173     }
13174
13175     Roo.apply(this, config);
13176     
13177     if(this.reader){ // reader passed
13178         this.reader = Roo.factory(this.reader, Roo.data);
13179         this.reader.xmodule = this.xmodule || false;
13180         if(!this.recordType){
13181             this.recordType = this.reader.recordType;
13182         }
13183         if(this.reader.onMetaChange){
13184             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13185         }
13186     }
13187
13188     if(this.recordType){
13189         this.fields = this.recordType.prototype.fields;
13190     }
13191     this.modified = [];
13192
13193     this.addEvents({
13194         /**
13195          * @event datachanged
13196          * Fires when the data cache has changed, and a widget which is using this Store
13197          * as a Record cache should refresh its view.
13198          * @param {Store} this
13199          */
13200         datachanged : true,
13201         /**
13202          * @event metachange
13203          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13204          * @param {Store} this
13205          * @param {Object} meta The JSON metadata
13206          */
13207         metachange : true,
13208         /**
13209          * @event add
13210          * Fires when Records have been added to the Store
13211          * @param {Store} this
13212          * @param {Roo.data.Record[]} records The array of Records added
13213          * @param {Number} index The index at which the record(s) were added
13214          */
13215         add : true,
13216         /**
13217          * @event remove
13218          * Fires when a Record has been removed from the Store
13219          * @param {Store} this
13220          * @param {Roo.data.Record} record The Record that was removed
13221          * @param {Number} index The index at which the record was removed
13222          */
13223         remove : true,
13224         /**
13225          * @event update
13226          * Fires when a Record has been updated
13227          * @param {Store} this
13228          * @param {Roo.data.Record} record The Record that was updated
13229          * @param {String} operation The update operation being performed.  Value may be one of:
13230          * <pre><code>
13231  Roo.data.Record.EDIT
13232  Roo.data.Record.REJECT
13233  Roo.data.Record.COMMIT
13234          * </code></pre>
13235          */
13236         update : true,
13237         /**
13238          * @event clear
13239          * Fires when the data cache has been cleared.
13240          * @param {Store} this
13241          */
13242         clear : true,
13243         /**
13244          * @event beforeload
13245          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13246          * the load action will be canceled.
13247          * @param {Store} this
13248          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13249          */
13250         beforeload : true,
13251         /**
13252          * @event beforeloadadd
13253          * Fires after a new set of Records has been loaded.
13254          * @param {Store} this
13255          * @param {Roo.data.Record[]} records The Records that were loaded
13256          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13257          */
13258         beforeloadadd : true,
13259         /**
13260          * @event load
13261          * Fires after a new set of Records has been loaded, before they are added to the store.
13262          * @param {Store} this
13263          * @param {Roo.data.Record[]} records The Records that were loaded
13264          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13265          * @params {Object} return from reader
13266          */
13267         load : true,
13268         /**
13269          * @event loadexception
13270          * Fires if an exception occurs in the Proxy during loading.
13271          * Called with the signature of the Proxy's "loadexception" event.
13272          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13273          * 
13274          * @param {Proxy} 
13275          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13276          * @param {Object} load options 
13277          * @param {Object} jsonData from your request (normally this contains the Exception)
13278          */
13279         loadexception : true
13280     });
13281     
13282     if(this.proxy){
13283         this.proxy = Roo.factory(this.proxy, Roo.data);
13284         this.proxy.xmodule = this.xmodule || false;
13285         this.relayEvents(this.proxy,  ["loadexception"]);
13286     }
13287     this.sortToggle = {};
13288     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13289
13290     Roo.data.Store.superclass.constructor.call(this);
13291
13292     if(this.inlineData){
13293         this.loadData(this.inlineData);
13294         delete this.inlineData;
13295     }
13296 };
13297
13298 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13299      /**
13300     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13301     * without a remote query - used by combo/forms at present.
13302     */
13303     
13304     /**
13305     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13306     */
13307     /**
13308     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13309     */
13310     /**
13311     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13312     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13313     */
13314     /**
13315     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13316     * on any HTTP request
13317     */
13318     /**
13319     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13320     */
13321     /**
13322     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13323     */
13324     multiSort: false,
13325     /**
13326     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13327     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13328     */
13329     remoteSort : false,
13330
13331     /**
13332     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13333      * loaded or when a record is removed. (defaults to false).
13334     */
13335     pruneModifiedRecords : false,
13336
13337     // private
13338     lastOptions : null,
13339
13340     /**
13341      * Add Records to the Store and fires the add event.
13342      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13343      */
13344     add : function(records){
13345         records = [].concat(records);
13346         for(var i = 0, len = records.length; i < len; i++){
13347             records[i].join(this);
13348         }
13349         var index = this.data.length;
13350         this.data.addAll(records);
13351         this.fireEvent("add", this, records, index);
13352     },
13353
13354     /**
13355      * Remove a Record from the Store and fires the remove event.
13356      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13357      */
13358     remove : function(record){
13359         var index = this.data.indexOf(record);
13360         this.data.removeAt(index);
13361  
13362         if(this.pruneModifiedRecords){
13363             this.modified.remove(record);
13364         }
13365         this.fireEvent("remove", this, record, index);
13366     },
13367
13368     /**
13369      * Remove all Records from the Store and fires the clear event.
13370      */
13371     removeAll : function(){
13372         this.data.clear();
13373         if(this.pruneModifiedRecords){
13374             this.modified = [];
13375         }
13376         this.fireEvent("clear", this);
13377     },
13378
13379     /**
13380      * Inserts Records to the Store at the given index and fires the add event.
13381      * @param {Number} index The start index at which to insert the passed Records.
13382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13383      */
13384     insert : function(index, records){
13385         records = [].concat(records);
13386         for(var i = 0, len = records.length; i < len; i++){
13387             this.data.insert(index, records[i]);
13388             records[i].join(this);
13389         }
13390         this.fireEvent("add", this, records, index);
13391     },
13392
13393     /**
13394      * Get the index within the cache of the passed Record.
13395      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13396      * @return {Number} The index of the passed Record. Returns -1 if not found.
13397      */
13398     indexOf : function(record){
13399         return this.data.indexOf(record);
13400     },
13401
13402     /**
13403      * Get the index within the cache of the Record with the passed id.
13404      * @param {String} id The id of the Record to find.
13405      * @return {Number} The index of the Record. Returns -1 if not found.
13406      */
13407     indexOfId : function(id){
13408         return this.data.indexOfKey(id);
13409     },
13410
13411     /**
13412      * Get the Record with the specified id.
13413      * @param {String} id The id of the Record to find.
13414      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13415      */
13416     getById : function(id){
13417         return this.data.key(id);
13418     },
13419
13420     /**
13421      * Get the Record at the specified index.
13422      * @param {Number} index The index of the Record to find.
13423      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13424      */
13425     getAt : function(index){
13426         return this.data.itemAt(index);
13427     },
13428
13429     /**
13430      * Returns a range of Records between specified indices.
13431      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13432      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13433      * @return {Roo.data.Record[]} An array of Records
13434      */
13435     getRange : function(start, end){
13436         return this.data.getRange(start, end);
13437     },
13438
13439     // private
13440     storeOptions : function(o){
13441         o = Roo.apply({}, o);
13442         delete o.callback;
13443         delete o.scope;
13444         this.lastOptions = o;
13445     },
13446
13447     /**
13448      * Loads the Record cache from the configured Proxy using the configured Reader.
13449      * <p>
13450      * If using remote paging, then the first load call must specify the <em>start</em>
13451      * and <em>limit</em> properties in the options.params property to establish the initial
13452      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13453      * <p>
13454      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13455      * and this call will return before the new data has been loaded. Perform any post-processing
13456      * in a callback function, or in a "load" event handler.</strong>
13457      * <p>
13458      * @param {Object} options An object containing properties which control loading options:<ul>
13459      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13460      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13461      * passed the following arguments:<ul>
13462      * <li>r : Roo.data.Record[]</li>
13463      * <li>options: Options object from the load call</li>
13464      * <li>success: Boolean success indicator</li></ul></li>
13465      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13466      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13467      * </ul>
13468      */
13469     load : function(options){
13470         options = options || {};
13471         if(this.fireEvent("beforeload", this, options) !== false){
13472             this.storeOptions(options);
13473             var p = Roo.apply(options.params || {}, this.baseParams);
13474             // if meta was not loaded from remote source.. try requesting it.
13475             if (!this.reader.metaFromRemote) {
13476                 p._requestMeta = 1;
13477             }
13478             if(this.sortInfo && this.remoteSort){
13479                 var pn = this.paramNames;
13480                 p[pn["sort"]] = this.sortInfo.field;
13481                 p[pn["dir"]] = this.sortInfo.direction;
13482             }
13483             if (this.multiSort) {
13484                 var pn = this.paramNames;
13485                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13486             }
13487             
13488             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13489         }
13490     },
13491
13492     /**
13493      * Reloads the Record cache from the configured Proxy using the configured Reader and
13494      * the options from the last load operation performed.
13495      * @param {Object} options (optional) An object containing properties which may override the options
13496      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13497      * the most recently used options are reused).
13498      */
13499     reload : function(options){
13500         this.load(Roo.applyIf(options||{}, this.lastOptions));
13501     },
13502
13503     // private
13504     // Called as a callback by the Reader during a load operation.
13505     loadRecords : function(o, options, success){
13506         if(!o || success === false){
13507             if(success !== false){
13508                 this.fireEvent("load", this, [], options, o);
13509             }
13510             if(options.callback){
13511                 options.callback.call(options.scope || this, [], options, false);
13512             }
13513             return;
13514         }
13515         // if data returned failure - throw an exception.
13516         if (o.success === false) {
13517             // show a message if no listener is registered.
13518             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13519                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13520             }
13521             // loadmask wil be hooked into this..
13522             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13523             return;
13524         }
13525         var r = o.records, t = o.totalRecords || r.length;
13526         
13527         this.fireEvent("beforeloadadd", this, r, options, o);
13528         
13529         if(!options || options.add !== true){
13530             if(this.pruneModifiedRecords){
13531                 this.modified = [];
13532             }
13533             for(var i = 0, len = r.length; i < len; i++){
13534                 r[i].join(this);
13535             }
13536             if(this.snapshot){
13537                 this.data = this.snapshot;
13538                 delete this.snapshot;
13539             }
13540             this.data.clear();
13541             this.data.addAll(r);
13542             this.totalLength = t;
13543             this.applySort();
13544             this.fireEvent("datachanged", this);
13545         }else{
13546             this.totalLength = Math.max(t, this.data.length+r.length);
13547             this.add(r);
13548         }
13549         
13550         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13551                 
13552             var e = new Roo.data.Record({});
13553
13554             e.set(this.parent.displayField, this.parent.emptyTitle);
13555             e.set(this.parent.valueField, '');
13556
13557             this.insert(0, e);
13558         }
13559             
13560         this.fireEvent("load", this, r, options, o);
13561         if(options.callback){
13562             options.callback.call(options.scope || this, r, options, true);
13563         }
13564     },
13565
13566
13567     /**
13568      * Loads data from a passed data block. A Reader which understands the format of the data
13569      * must have been configured in the constructor.
13570      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13571      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13572      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13573      */
13574     loadData : function(o, append){
13575         var r = this.reader.readRecords(o);
13576         this.loadRecords(r, {add: append}, true);
13577     },
13578     
13579      /**
13580      * using 'cn' the nested child reader read the child array into it's child stores.
13581      * @param {Object} rec The record with a 'children array
13582      */
13583     loadDataFromChildren : function(rec)
13584     {
13585         this.loadData(this.reader.toLoadData(rec));
13586     },
13587     
13588
13589     /**
13590      * Gets the number of cached records.
13591      * <p>
13592      * <em>If using paging, this may not be the total size of the dataset. If the data object
13593      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13594      * the data set size</em>
13595      */
13596     getCount : function(){
13597         return this.data.length || 0;
13598     },
13599
13600     /**
13601      * Gets the total number of records in the dataset as returned by the server.
13602      * <p>
13603      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13604      * the dataset size</em>
13605      */
13606     getTotalCount : function(){
13607         return this.totalLength || 0;
13608     },
13609
13610     /**
13611      * Returns the sort state of the Store as an object with two properties:
13612      * <pre><code>
13613  field {String} The name of the field by which the Records are sorted
13614  direction {String} The sort order, "ASC" or "DESC"
13615      * </code></pre>
13616      */
13617     getSortState : function(){
13618         return this.sortInfo;
13619     },
13620
13621     // private
13622     applySort : function(){
13623         if(this.sortInfo && !this.remoteSort){
13624             var s = this.sortInfo, f = s.field;
13625             var st = this.fields.get(f).sortType;
13626             var fn = function(r1, r2){
13627                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13628                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13629             };
13630             this.data.sort(s.direction, fn);
13631             if(this.snapshot && this.snapshot != this.data){
13632                 this.snapshot.sort(s.direction, fn);
13633             }
13634         }
13635     },
13636
13637     /**
13638      * Sets the default sort column and order to be used by the next load operation.
13639      * @param {String} fieldName The name of the field to sort by.
13640      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13641      */
13642     setDefaultSort : function(field, dir){
13643         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13644     },
13645
13646     /**
13647      * Sort the Records.
13648      * If remote sorting is used, the sort is performed on the server, and the cache is
13649      * reloaded. If local sorting is used, the cache is sorted internally.
13650      * @param {String} fieldName The name of the field to sort by.
13651      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13652      */
13653     sort : function(fieldName, dir){
13654         var f = this.fields.get(fieldName);
13655         if(!dir){
13656             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13657             
13658             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13659                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13660             }else{
13661                 dir = f.sortDir;
13662             }
13663         }
13664         this.sortToggle[f.name] = dir;
13665         this.sortInfo = {field: f.name, direction: dir};
13666         if(!this.remoteSort){
13667             this.applySort();
13668             this.fireEvent("datachanged", this);
13669         }else{
13670             this.load(this.lastOptions);
13671         }
13672     },
13673
13674     /**
13675      * Calls the specified function for each of the Records in the cache.
13676      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13677      * Returning <em>false</em> aborts and exits the iteration.
13678      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13679      */
13680     each : function(fn, scope){
13681         this.data.each(fn, scope);
13682     },
13683
13684     /**
13685      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13686      * (e.g., during paging).
13687      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13688      */
13689     getModifiedRecords : function(){
13690         return this.modified;
13691     },
13692
13693     // private
13694     createFilterFn : function(property, value, anyMatch){
13695         if(!value.exec){ // not a regex
13696             value = String(value);
13697             if(value.length == 0){
13698                 return false;
13699             }
13700             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13701         }
13702         return function(r){
13703             return value.test(r.data[property]);
13704         };
13705     },
13706
13707     /**
13708      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13709      * @param {String} property A field on your records
13710      * @param {Number} start The record index to start at (defaults to 0)
13711      * @param {Number} end The last record index to include (defaults to length - 1)
13712      * @return {Number} The sum
13713      */
13714     sum : function(property, start, end){
13715         var rs = this.data.items, v = 0;
13716         start = start || 0;
13717         end = (end || end === 0) ? end : rs.length-1;
13718
13719         for(var i = start; i <= end; i++){
13720             v += (rs[i].data[property] || 0);
13721         }
13722         return v;
13723     },
13724
13725     /**
13726      * Filter the records by a specified property.
13727      * @param {String} field A field on your records
13728      * @param {String/RegExp} value Either a string that the field
13729      * should start with or a RegExp to test against the field
13730      * @param {Boolean} anyMatch True to match any part not just the beginning
13731      */
13732     filter : function(property, value, anyMatch){
13733         var fn = this.createFilterFn(property, value, anyMatch);
13734         return fn ? this.filterBy(fn) : this.clearFilter();
13735     },
13736
13737     /**
13738      * Filter by a function. The specified function will be called with each
13739      * record in this data source. If the function returns true the record is included,
13740      * otherwise it is filtered.
13741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13742      * @param {Object} scope (optional) The scope of the function (defaults to this)
13743      */
13744     filterBy : function(fn, scope){
13745         this.snapshot = this.snapshot || this.data;
13746         this.data = this.queryBy(fn, scope||this);
13747         this.fireEvent("datachanged", this);
13748     },
13749
13750     /**
13751      * Query the records by a specified property.
13752      * @param {String} field A field on your records
13753      * @param {String/RegExp} value Either a string that the field
13754      * should start with or a RegExp to test against the field
13755      * @param {Boolean} anyMatch True to match any part not just the beginning
13756      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13757      */
13758     query : function(property, value, anyMatch){
13759         var fn = this.createFilterFn(property, value, anyMatch);
13760         return fn ? this.queryBy(fn) : this.data.clone();
13761     },
13762
13763     /**
13764      * Query by a function. The specified function will be called with each
13765      * record in this data source. If the function returns true the record is included
13766      * in the results.
13767      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13768      * @param {Object} scope (optional) The scope of the function (defaults to this)
13769       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13770      **/
13771     queryBy : function(fn, scope){
13772         var data = this.snapshot || this.data;
13773         return data.filterBy(fn, scope||this);
13774     },
13775
13776     /**
13777      * Collects unique values for a particular dataIndex from this store.
13778      * @param {String} dataIndex The property to collect
13779      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13780      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13781      * @return {Array} An array of the unique values
13782      **/
13783     collect : function(dataIndex, allowNull, bypassFilter){
13784         var d = (bypassFilter === true && this.snapshot) ?
13785                 this.snapshot.items : this.data.items;
13786         var v, sv, r = [], l = {};
13787         for(var i = 0, len = d.length; i < len; i++){
13788             v = d[i].data[dataIndex];
13789             sv = String(v);
13790             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13791                 l[sv] = true;
13792                 r[r.length] = v;
13793             }
13794         }
13795         return r;
13796     },
13797
13798     /**
13799      * Revert to a view of the Record cache with no filtering applied.
13800      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13801      */
13802     clearFilter : function(suppressEvent){
13803         if(this.snapshot && this.snapshot != this.data){
13804             this.data = this.snapshot;
13805             delete this.snapshot;
13806             if(suppressEvent !== true){
13807                 this.fireEvent("datachanged", this);
13808             }
13809         }
13810     },
13811
13812     // private
13813     afterEdit : function(record){
13814         if(this.modified.indexOf(record) == -1){
13815             this.modified.push(record);
13816         }
13817         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13818     },
13819     
13820     // private
13821     afterReject : function(record){
13822         this.modified.remove(record);
13823         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13824     },
13825
13826     // private
13827     afterCommit : function(record){
13828         this.modified.remove(record);
13829         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13830     },
13831
13832     /**
13833      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13834      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13835      */
13836     commitChanges : function(){
13837         var m = this.modified.slice(0);
13838         this.modified = [];
13839         for(var i = 0, len = m.length; i < len; i++){
13840             m[i].commit();
13841         }
13842     },
13843
13844     /**
13845      * Cancel outstanding changes on all changed records.
13846      */
13847     rejectChanges : function(){
13848         var m = this.modified.slice(0);
13849         this.modified = [];
13850         for(var i = 0, len = m.length; i < len; i++){
13851             m[i].reject();
13852         }
13853     },
13854
13855     onMetaChange : function(meta, rtype, o){
13856         this.recordType = rtype;
13857         this.fields = rtype.prototype.fields;
13858         delete this.snapshot;
13859         this.sortInfo = meta.sortInfo || this.sortInfo;
13860         this.modified = [];
13861         this.fireEvent('metachange', this, this.reader.meta);
13862     },
13863     
13864     moveIndex : function(data, type)
13865     {
13866         var index = this.indexOf(data);
13867         
13868         var newIndex = index + type;
13869         
13870         this.remove(data);
13871         
13872         this.insert(newIndex, data);
13873         
13874     }
13875 });/*
13876  * Based on:
13877  * Ext JS Library 1.1.1
13878  * Copyright(c) 2006-2007, Ext JS, LLC.
13879  *
13880  * Originally Released Under LGPL - original licence link has changed is not relivant.
13881  *
13882  * Fork - LGPL
13883  * <script type="text/javascript">
13884  */
13885
13886 /**
13887  * @class Roo.data.SimpleStore
13888  * @extends Roo.data.Store
13889  * Small helper class to make creating Stores from Array data easier.
13890  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13891  * @cfg {Array} fields An array of field definition objects, or field name strings.
13892  * @cfg {Object} an existing reader (eg. copied from another store)
13893  * @cfg {Array} data The multi-dimensional array of data
13894  * @constructor
13895  * @param {Object} config
13896  */
13897 Roo.data.SimpleStore = function(config)
13898 {
13899     Roo.data.SimpleStore.superclass.constructor.call(this, {
13900         isLocal : true,
13901         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13902                 id: config.id
13903             },
13904             Roo.data.Record.create(config.fields)
13905         ),
13906         proxy : new Roo.data.MemoryProxy(config.data)
13907     });
13908     this.load();
13909 };
13910 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13911  * Based on:
13912  * Ext JS Library 1.1.1
13913  * Copyright(c) 2006-2007, Ext JS, LLC.
13914  *
13915  * Originally Released Under LGPL - original licence link has changed is not relivant.
13916  *
13917  * Fork - LGPL
13918  * <script type="text/javascript">
13919  */
13920
13921 /**
13922 /**
13923  * @extends Roo.data.Store
13924  * @class Roo.data.JsonStore
13925  * Small helper class to make creating Stores for JSON data easier. <br/>
13926 <pre><code>
13927 var store = new Roo.data.JsonStore({
13928     url: 'get-images.php',
13929     root: 'images',
13930     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13931 });
13932 </code></pre>
13933  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13934  * JsonReader and HttpProxy (unless inline data is provided).</b>
13935  * @cfg {Array} fields An array of field definition objects, or field name strings.
13936  * @constructor
13937  * @param {Object} config
13938  */
13939 Roo.data.JsonStore = function(c){
13940     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13941         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13942         reader: new Roo.data.JsonReader(c, c.fields)
13943     }));
13944 };
13945 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956  
13957 Roo.data.Field = function(config){
13958     if(typeof config == "string"){
13959         config = {name: config};
13960     }
13961     Roo.apply(this, config);
13962     
13963     if(!this.type){
13964         this.type = "auto";
13965     }
13966     
13967     var st = Roo.data.SortTypes;
13968     // named sortTypes are supported, here we look them up
13969     if(typeof this.sortType == "string"){
13970         this.sortType = st[this.sortType];
13971     }
13972     
13973     // set default sortType for strings and dates
13974     if(!this.sortType){
13975         switch(this.type){
13976             case "string":
13977                 this.sortType = st.asUCString;
13978                 break;
13979             case "date":
13980                 this.sortType = st.asDate;
13981                 break;
13982             default:
13983                 this.sortType = st.none;
13984         }
13985     }
13986
13987     // define once
13988     var stripRe = /[\$,%]/g;
13989
13990     // prebuilt conversion function for this field, instead of
13991     // switching every time we're reading a value
13992     if(!this.convert){
13993         var cv, dateFormat = this.dateFormat;
13994         switch(this.type){
13995             case "":
13996             case "auto":
13997             case undefined:
13998                 cv = function(v){ return v; };
13999                 break;
14000             case "string":
14001                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14002                 break;
14003             case "int":
14004                 cv = function(v){
14005                     return v !== undefined && v !== null && v !== '' ?
14006                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14007                     };
14008                 break;
14009             case "float":
14010                 cv = function(v){
14011                     return v !== undefined && v !== null && v !== '' ?
14012                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14013                     };
14014                 break;
14015             case "bool":
14016             case "boolean":
14017                 cv = function(v){ return v === true || v === "true" || v == 1; };
14018                 break;
14019             case "date":
14020                 cv = function(v){
14021                     if(!v){
14022                         return '';
14023                     }
14024                     if(v instanceof Date){
14025                         return v;
14026                     }
14027                     if(dateFormat){
14028                         if(dateFormat == "timestamp"){
14029                             return new Date(v*1000);
14030                         }
14031                         return Date.parseDate(v, dateFormat);
14032                     }
14033                     var parsed = Date.parse(v);
14034                     return parsed ? new Date(parsed) : null;
14035                 };
14036              break;
14037             
14038         }
14039         this.convert = cv;
14040     }
14041 };
14042
14043 Roo.data.Field.prototype = {
14044     dateFormat: null,
14045     defaultValue: "",
14046     mapping: null,
14047     sortType : null,
14048     sortDir : "ASC"
14049 };/*
14050  * Based on:
14051  * Ext JS Library 1.1.1
14052  * Copyright(c) 2006-2007, Ext JS, LLC.
14053  *
14054  * Originally Released Under LGPL - original licence link has changed is not relivant.
14055  *
14056  * Fork - LGPL
14057  * <script type="text/javascript">
14058  */
14059  
14060 // Base class for reading structured data from a data source.  This class is intended to be
14061 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14062
14063 /**
14064  * @class Roo.data.DataReader
14065  * Base class for reading structured data from a data source.  This class is intended to be
14066  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14067  */
14068
14069 Roo.data.DataReader = function(meta, recordType){
14070     
14071     this.meta = meta;
14072     
14073     this.recordType = recordType instanceof Array ? 
14074         Roo.data.Record.create(recordType) : recordType;
14075 };
14076
14077 Roo.data.DataReader.prototype = {
14078     
14079     
14080     readerType : 'Data',
14081      /**
14082      * Create an empty record
14083      * @param {Object} data (optional) - overlay some values
14084      * @return {Roo.data.Record} record created.
14085      */
14086     newRow :  function(d) {
14087         var da =  {};
14088         this.recordType.prototype.fields.each(function(c) {
14089             switch( c.type) {
14090                 case 'int' : da[c.name] = 0; break;
14091                 case 'date' : da[c.name] = new Date(); break;
14092                 case 'float' : da[c.name] = 0.0; break;
14093                 case 'boolean' : da[c.name] = false; break;
14094                 default : da[c.name] = ""; break;
14095             }
14096             
14097         });
14098         return new this.recordType(Roo.apply(da, d));
14099     }
14100     
14101     
14102 };/*
14103  * Based on:
14104  * Ext JS Library 1.1.1
14105  * Copyright(c) 2006-2007, Ext JS, LLC.
14106  *
14107  * Originally Released Under LGPL - original licence link has changed is not relivant.
14108  *
14109  * Fork - LGPL
14110  * <script type="text/javascript">
14111  */
14112
14113 /**
14114  * @class Roo.data.DataProxy
14115  * @extends Roo.data.Observable
14116  * This class is an abstract base class for implementations which provide retrieval of
14117  * unformatted data objects.<br>
14118  * <p>
14119  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14120  * (of the appropriate type which knows how to parse the data object) to provide a block of
14121  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14122  * <p>
14123  * Custom implementations must implement the load method as described in
14124  * {@link Roo.data.HttpProxy#load}.
14125  */
14126 Roo.data.DataProxy = function(){
14127     this.addEvents({
14128         /**
14129          * @event beforeload
14130          * Fires before a network request is made to retrieve a data object.
14131          * @param {Object} This DataProxy object.
14132          * @param {Object} params The params parameter to the load function.
14133          */
14134         beforeload : true,
14135         /**
14136          * @event load
14137          * Fires before the load method's callback is called.
14138          * @param {Object} This DataProxy object.
14139          * @param {Object} o The data object.
14140          * @param {Object} arg The callback argument object passed to the load function.
14141          */
14142         load : true,
14143         /**
14144          * @event loadexception
14145          * Fires if an Exception occurs during data retrieval.
14146          * @param {Object} This DataProxy object.
14147          * @param {Object} o The data object.
14148          * @param {Object} arg The callback argument object passed to the load function.
14149          * @param {Object} e The Exception.
14150          */
14151         loadexception : true
14152     });
14153     Roo.data.DataProxy.superclass.constructor.call(this);
14154 };
14155
14156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14157
14158     /**
14159      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14160      */
14161 /*
14162  * Based on:
14163  * Ext JS Library 1.1.1
14164  * Copyright(c) 2006-2007, Ext JS, LLC.
14165  *
14166  * Originally Released Under LGPL - original licence link has changed is not relivant.
14167  *
14168  * Fork - LGPL
14169  * <script type="text/javascript">
14170  */
14171 /**
14172  * @class Roo.data.MemoryProxy
14173  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14174  * to the Reader when its load method is called.
14175  * @constructor
14176  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14177  */
14178 Roo.data.MemoryProxy = function(data){
14179     if (data.data) {
14180         data = data.data;
14181     }
14182     Roo.data.MemoryProxy.superclass.constructor.call(this);
14183     this.data = data;
14184 };
14185
14186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14187     
14188     /**
14189      * Load data from the requested source (in this case an in-memory
14190      * data object passed to the constructor), read the data object into
14191      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14192      * process that block using the passed callback.
14193      * @param {Object} params This parameter is not used by the MemoryProxy class.
14194      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14195      * object into a block of Roo.data.Records.
14196      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14197      * The function must be passed <ul>
14198      * <li>The Record block object</li>
14199      * <li>The "arg" argument from the load function</li>
14200      * <li>A boolean success indicator</li>
14201      * </ul>
14202      * @param {Object} scope The scope in which to call the callback
14203      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14204      */
14205     load : function(params, reader, callback, scope, arg){
14206         params = params || {};
14207         var result;
14208         try {
14209             result = reader.readRecords(params.data ? params.data :this.data);
14210         }catch(e){
14211             this.fireEvent("loadexception", this, arg, null, e);
14212             callback.call(scope, null, arg, false);
14213             return;
14214         }
14215         callback.call(scope, result, arg, true);
14216     },
14217     
14218     // private
14219     update : function(params, records){
14220         
14221     }
14222 });/*
14223  * Based on:
14224  * Ext JS Library 1.1.1
14225  * Copyright(c) 2006-2007, Ext JS, LLC.
14226  *
14227  * Originally Released Under LGPL - original licence link has changed is not relivant.
14228  *
14229  * Fork - LGPL
14230  * <script type="text/javascript">
14231  */
14232 /**
14233  * @class Roo.data.HttpProxy
14234  * @extends Roo.data.DataProxy
14235  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14236  * configured to reference a certain URL.<br><br>
14237  * <p>
14238  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14239  * from which the running page was served.<br><br>
14240  * <p>
14241  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14242  * <p>
14243  * Be aware that to enable the browser to parse an XML document, the server must set
14244  * the Content-Type header in the HTTP response to "text/xml".
14245  * @constructor
14246  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14247  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14248  * will be used to make the request.
14249  */
14250 Roo.data.HttpProxy = function(conn){
14251     Roo.data.HttpProxy.superclass.constructor.call(this);
14252     // is conn a conn config or a real conn?
14253     this.conn = conn;
14254     this.useAjax = !conn || !conn.events;
14255   
14256 };
14257
14258 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14259     // thse are take from connection...
14260     
14261     /**
14262      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14266      * extra parameters to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14270      *  to each request made by this object. (defaults to undefined)
14271      */
14272     /**
14273      * @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)
14274      */
14275     /**
14276      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14277      */
14278      /**
14279      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14280      * @type Boolean
14281      */
14282   
14283
14284     /**
14285      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14286      * @type Boolean
14287      */
14288     /**
14289      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14290      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14291      * a finer-grained basis than the DataProxy events.
14292      */
14293     getConnection : function(){
14294         return this.useAjax ? Roo.Ajax : this.conn;
14295     },
14296
14297     /**
14298      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14299      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14300      * process that block using the passed callback.
14301      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14302      * for the request to the remote server.
14303      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14304      * object into a block of Roo.data.Records.
14305      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14306      * The function must be passed <ul>
14307      * <li>The Record block object</li>
14308      * <li>The "arg" argument from the load function</li>
14309      * <li>A boolean success indicator</li>
14310      * </ul>
14311      * @param {Object} scope The scope in which to call the callback
14312      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14313      */
14314     load : function(params, reader, callback, scope, arg){
14315         if(this.fireEvent("beforeload", this, params) !== false){
14316             var  o = {
14317                 params : params || {},
14318                 request: {
14319                     callback : callback,
14320                     scope : scope,
14321                     arg : arg
14322                 },
14323                 reader: reader,
14324                 callback : this.loadResponse,
14325                 scope: this
14326             };
14327             if(this.useAjax){
14328                 Roo.applyIf(o, this.conn);
14329                 if(this.activeRequest){
14330                     Roo.Ajax.abort(this.activeRequest);
14331                 }
14332                 this.activeRequest = Roo.Ajax.request(o);
14333             }else{
14334                 this.conn.request(o);
14335             }
14336         }else{
14337             callback.call(scope||this, null, arg, false);
14338         }
14339     },
14340
14341     // private
14342     loadResponse : function(o, success, response){
14343         delete this.activeRequest;
14344         if(!success){
14345             this.fireEvent("loadexception", this, o, response);
14346             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14347             return;
14348         }
14349         var result;
14350         try {
14351             result = o.reader.read(response);
14352         }catch(e){
14353             this.fireEvent("loadexception", this, o, response, e);
14354             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14355             return;
14356         }
14357         
14358         this.fireEvent("load", this, o, o.request.arg);
14359         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14360     },
14361
14362     // private
14363     update : function(dataSet){
14364
14365     },
14366
14367     // private
14368     updateResponse : function(dataSet){
14369
14370     }
14371 });/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381
14382 /**
14383  * @class Roo.data.ScriptTagProxy
14384  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14385  * other than the originating domain of the running page.<br><br>
14386  * <p>
14387  * <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
14388  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14389  * <p>
14390  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14391  * source code that is used as the source inside a &lt;script> tag.<br><br>
14392  * <p>
14393  * In order for the browser to process the returned data, the server must wrap the data object
14394  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14395  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14396  * depending on whether the callback name was passed:
14397  * <p>
14398  * <pre><code>
14399 boolean scriptTag = false;
14400 String cb = request.getParameter("callback");
14401 if (cb != null) {
14402     scriptTag = true;
14403     response.setContentType("text/javascript");
14404 } else {
14405     response.setContentType("application/x-json");
14406 }
14407 Writer out = response.getWriter();
14408 if (scriptTag) {
14409     out.write(cb + "(");
14410 }
14411 out.print(dataBlock.toJsonString());
14412 if (scriptTag) {
14413     out.write(");");
14414 }
14415 </pre></code>
14416  *
14417  * @constructor
14418  * @param {Object} config A configuration object.
14419  */
14420 Roo.data.ScriptTagProxy = function(config){
14421     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14422     Roo.apply(this, config);
14423     this.head = document.getElementsByTagName("head")[0];
14424 };
14425
14426 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14427
14428 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14429     /**
14430      * @cfg {String} url The URL from which to request the data object.
14431      */
14432     /**
14433      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14434      */
14435     timeout : 30000,
14436     /**
14437      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14438      * the server the name of the callback function set up by the load call to process the returned data object.
14439      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14440      * javascript output which calls this named function passing the data object as its only parameter.
14441      */
14442     callbackParam : "callback",
14443     /**
14444      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14445      * name to the request.
14446      */
14447     nocache : true,
14448
14449     /**
14450      * Load data from the configured URL, read the data object into
14451      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14452      * process that block using the passed callback.
14453      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14454      * for the request to the remote server.
14455      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14456      * object into a block of Roo.data.Records.
14457      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14458      * The function must be passed <ul>
14459      * <li>The Record block object</li>
14460      * <li>The "arg" argument from the load function</li>
14461      * <li>A boolean success indicator</li>
14462      * </ul>
14463      * @param {Object} scope The scope in which to call the callback
14464      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14465      */
14466     load : function(params, reader, callback, scope, arg){
14467         if(this.fireEvent("beforeload", this, params) !== false){
14468
14469             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14470
14471             var url = this.url;
14472             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14473             if(this.nocache){
14474                 url += "&_dc=" + (new Date().getTime());
14475             }
14476             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14477             var trans = {
14478                 id : transId,
14479                 cb : "stcCallback"+transId,
14480                 scriptId : "stcScript"+transId,
14481                 params : params,
14482                 arg : arg,
14483                 url : url,
14484                 callback : callback,
14485                 scope : scope,
14486                 reader : reader
14487             };
14488             var conn = this;
14489
14490             window[trans.cb] = function(o){
14491                 conn.handleResponse(o, trans);
14492             };
14493
14494             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14495
14496             if(this.autoAbort !== false){
14497                 this.abort();
14498             }
14499
14500             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14501
14502             var script = document.createElement("script");
14503             script.setAttribute("src", url);
14504             script.setAttribute("type", "text/javascript");
14505             script.setAttribute("id", trans.scriptId);
14506             this.head.appendChild(script);
14507
14508             this.trans = trans;
14509         }else{
14510             callback.call(scope||this, null, arg, false);
14511         }
14512     },
14513
14514     // private
14515     isLoading : function(){
14516         return this.trans ? true : false;
14517     },
14518
14519     /**
14520      * Abort the current server request.
14521      */
14522     abort : function(){
14523         if(this.isLoading()){
14524             this.destroyTrans(this.trans);
14525         }
14526     },
14527
14528     // private
14529     destroyTrans : function(trans, isLoaded){
14530         this.head.removeChild(document.getElementById(trans.scriptId));
14531         clearTimeout(trans.timeoutId);
14532         if(isLoaded){
14533             window[trans.cb] = undefined;
14534             try{
14535                 delete window[trans.cb];
14536             }catch(e){}
14537         }else{
14538             // if hasn't been loaded, wait for load to remove it to prevent script error
14539             window[trans.cb] = function(){
14540                 window[trans.cb] = undefined;
14541                 try{
14542                     delete window[trans.cb];
14543                 }catch(e){}
14544             };
14545         }
14546     },
14547
14548     // private
14549     handleResponse : function(o, trans){
14550         this.trans = false;
14551         this.destroyTrans(trans, true);
14552         var result;
14553         try {
14554             result = trans.reader.readRecords(o);
14555         }catch(e){
14556             this.fireEvent("loadexception", this, o, trans.arg, e);
14557             trans.callback.call(trans.scope||window, null, trans.arg, false);
14558             return;
14559         }
14560         this.fireEvent("load", this, o, trans.arg);
14561         trans.callback.call(trans.scope||window, result, trans.arg, true);
14562     },
14563
14564     // private
14565     handleFailure : function(trans){
14566         this.trans = false;
14567         this.destroyTrans(trans, false);
14568         this.fireEvent("loadexception", this, null, trans.arg);
14569         trans.callback.call(trans.scope||window, null, trans.arg, false);
14570     }
14571 });/*
14572  * Based on:
14573  * Ext JS Library 1.1.1
14574  * Copyright(c) 2006-2007, Ext JS, LLC.
14575  *
14576  * Originally Released Under LGPL - original licence link has changed is not relivant.
14577  *
14578  * Fork - LGPL
14579  * <script type="text/javascript">
14580  */
14581
14582 /**
14583  * @class Roo.data.JsonReader
14584  * @extends Roo.data.DataReader
14585  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14586  * based on mappings in a provided Roo.data.Record constructor.
14587  * 
14588  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14589  * in the reply previously. 
14590  * 
14591  * <p>
14592  * Example code:
14593  * <pre><code>
14594 var RecordDef = Roo.data.Record.create([
14595     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14596     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14597 ]);
14598 var myReader = new Roo.data.JsonReader({
14599     totalProperty: "results",    // The property which contains the total dataset size (optional)
14600     root: "rows",                // The property which contains an Array of row objects
14601     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14602 }, RecordDef);
14603 </code></pre>
14604  * <p>
14605  * This would consume a JSON file like this:
14606  * <pre><code>
14607 { 'results': 2, 'rows': [
14608     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14609     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14610 }
14611 </code></pre>
14612  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14613  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14614  * paged from the remote server.
14615  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14616  * @cfg {String} root name of the property which contains the Array of row objects.
14617  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14618  * @cfg {Array} fields Array of field definition objects
14619  * @constructor
14620  * Create a new JsonReader
14621  * @param {Object} meta Metadata configuration options
14622  * @param {Object} recordType Either an Array of field definition objects,
14623  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14624  */
14625 Roo.data.JsonReader = function(meta, recordType){
14626     
14627     meta = meta || {};
14628     // set some defaults:
14629     Roo.applyIf(meta, {
14630         totalProperty: 'total',
14631         successProperty : 'success',
14632         root : 'data',
14633         id : 'id'
14634     });
14635     
14636     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14637 };
14638 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14639     
14640     readerType : 'Json',
14641     
14642     /**
14643      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14644      * Used by Store query builder to append _requestMeta to params.
14645      * 
14646      */
14647     metaFromRemote : false,
14648     /**
14649      * This method is only used by a DataProxy which has retrieved data from a remote server.
14650      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14651      * @return {Object} data A data block which is used by an Roo.data.Store object as
14652      * a cache of Roo.data.Records.
14653      */
14654     read : function(response){
14655         var json = response.responseText;
14656        
14657         var o = /* eval:var:o */ eval("("+json+")");
14658         if(!o) {
14659             throw {message: "JsonReader.read: Json object not found"};
14660         }
14661         
14662         if(o.metaData){
14663             
14664             delete this.ef;
14665             this.metaFromRemote = true;
14666             this.meta = o.metaData;
14667             this.recordType = Roo.data.Record.create(o.metaData.fields);
14668             this.onMetaChange(this.meta, this.recordType, o);
14669         }
14670         return this.readRecords(o);
14671     },
14672
14673     // private function a store will implement
14674     onMetaChange : function(meta, recordType, o){
14675
14676     },
14677
14678     /**
14679          * @ignore
14680          */
14681     simpleAccess: function(obj, subsc) {
14682         return obj[subsc];
14683     },
14684
14685         /**
14686          * @ignore
14687          */
14688     getJsonAccessor: function(){
14689         var re = /[\[\.]/;
14690         return function(expr) {
14691             try {
14692                 return(re.test(expr))
14693                     ? new Function("obj", "return obj." + expr)
14694                     : function(obj){
14695                         return obj[expr];
14696                     };
14697             } catch(e){}
14698             return Roo.emptyFn;
14699         };
14700     }(),
14701
14702     /**
14703      * Create a data block containing Roo.data.Records from an XML document.
14704      * @param {Object} o An object which contains an Array of row objects in the property specified
14705      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14706      * which contains the total size of the dataset.
14707      * @return {Object} data A data block which is used by an Roo.data.Store object as
14708      * a cache of Roo.data.Records.
14709      */
14710     readRecords : function(o){
14711         /**
14712          * After any data loads, the raw JSON data is available for further custom processing.
14713          * @type Object
14714          */
14715         this.o = o;
14716         var s = this.meta, Record = this.recordType,
14717             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14718
14719 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14720         if (!this.ef) {
14721             if(s.totalProperty) {
14722                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14723                 }
14724                 if(s.successProperty) {
14725                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14726                 }
14727                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14728                 if (s.id) {
14729                         var g = this.getJsonAccessor(s.id);
14730                         this.getId = function(rec) {
14731                                 var r = g(rec);  
14732                                 return (r === undefined || r === "") ? null : r;
14733                         };
14734                 } else {
14735                         this.getId = function(){return null;};
14736                 }
14737             this.ef = [];
14738             for(var jj = 0; jj < fl; jj++){
14739                 f = fi[jj];
14740                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14741                 this.ef[jj] = this.getJsonAccessor(map);
14742             }
14743         }
14744
14745         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14746         if(s.totalProperty){
14747             var vt = parseInt(this.getTotal(o), 10);
14748             if(!isNaN(vt)){
14749                 totalRecords = vt;
14750             }
14751         }
14752         if(s.successProperty){
14753             var vs = this.getSuccess(o);
14754             if(vs === false || vs === 'false'){
14755                 success = false;
14756             }
14757         }
14758         var records = [];
14759         for(var i = 0; i < c; i++){
14760                 var n = root[i];
14761             var values = {};
14762             var id = this.getId(n);
14763             for(var j = 0; j < fl; j++){
14764                 f = fi[j];
14765             var v = this.ef[j](n);
14766             if (!f.convert) {
14767                 Roo.log('missing convert for ' + f.name);
14768                 Roo.log(f);
14769                 continue;
14770             }
14771             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14772             }
14773             var record = new Record(values, id);
14774             record.json = n;
14775             records[i] = record;
14776         }
14777         return {
14778             raw : o,
14779             success : success,
14780             records : records,
14781             totalRecords : totalRecords
14782         };
14783     },
14784     // used when loading children.. @see loadDataFromChildren
14785     toLoadData: function(rec)
14786     {
14787         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14788         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14789         return { data : data, total : data.length };
14790         
14791     }
14792 });/*
14793  * Based on:
14794  * Ext JS Library 1.1.1
14795  * Copyright(c) 2006-2007, Ext JS, LLC.
14796  *
14797  * Originally Released Under LGPL - original licence link has changed is not relivant.
14798  *
14799  * Fork - LGPL
14800  * <script type="text/javascript">
14801  */
14802
14803 /**
14804  * @class Roo.data.ArrayReader
14805  * @extends Roo.data.DataReader
14806  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14807  * Each element of that Array represents a row of data fields. The
14808  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14809  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14810  * <p>
14811  * Example code:.
14812  * <pre><code>
14813 var RecordDef = Roo.data.Record.create([
14814     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14815     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14816 ]);
14817 var myReader = new Roo.data.ArrayReader({
14818     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14819 }, RecordDef);
14820 </code></pre>
14821  * <p>
14822  * This would consume an Array like this:
14823  * <pre><code>
14824 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14825   </code></pre>
14826  
14827  * @constructor
14828  * Create a new JsonReader
14829  * @param {Object} meta Metadata configuration options.
14830  * @param {Object|Array} recordType Either an Array of field definition objects
14831  * 
14832  * @cfg {Array} fields Array of field definition objects
14833  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14834  * as specified to {@link Roo.data.Record#create},
14835  * or an {@link Roo.data.Record} object
14836  *
14837  * 
14838  * created using {@link Roo.data.Record#create}.
14839  */
14840 Roo.data.ArrayReader = function(meta, recordType)
14841 {    
14842     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14843 };
14844
14845 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14846     
14847       /**
14848      * Create a data block containing Roo.data.Records from an XML document.
14849      * @param {Object} o An Array of row objects which represents the dataset.
14850      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14851      * a cache of Roo.data.Records.
14852      */
14853     readRecords : function(o)
14854     {
14855         var sid = this.meta ? this.meta.id : null;
14856         var recordType = this.recordType, fields = recordType.prototype.fields;
14857         var records = [];
14858         var root = o;
14859         for(var i = 0; i < root.length; i++){
14860                 var n = root[i];
14861             var values = {};
14862             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14863             for(var j = 0, jlen = fields.length; j < jlen; j++){
14864                 var f = fields.items[j];
14865                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14866                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14867                 v = f.convert(v);
14868                 values[f.name] = v;
14869             }
14870             var record = new recordType(values, id);
14871             record.json = n;
14872             records[records.length] = record;
14873         }
14874         return {
14875             records : records,
14876             totalRecords : records.length
14877         };
14878     },
14879     // used when loading children.. @see loadDataFromChildren
14880     toLoadData: function(rec)
14881     {
14882         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14883         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14884         
14885     }
14886     
14887     
14888 });/*
14889  * - LGPL
14890  * * 
14891  */
14892
14893 /**
14894  * @class Roo.bootstrap.ComboBox
14895  * @extends Roo.bootstrap.TriggerField
14896  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14897  * @cfg {Boolean} append (true|false) default false
14898  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14899  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14900  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14901  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14902  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14903  * @cfg {Boolean} animate default true
14904  * @cfg {Boolean} emptyResultText only for touch device
14905  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14906  * @cfg {String} emptyTitle default ''
14907  * @cfg {Number} width fixed with? experimental
14908  * @constructor
14909  * Create a new ComboBox.
14910  * @param {Object} config Configuration options
14911  */
14912 Roo.bootstrap.ComboBox = function(config){
14913     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14914     this.addEvents({
14915         /**
14916          * @event expand
14917          * Fires when the dropdown list is expanded
14918         * @param {Roo.bootstrap.ComboBox} combo This combo box
14919         */
14920         'expand' : true,
14921         /**
14922          * @event collapse
14923          * Fires when the dropdown list is collapsed
14924         * @param {Roo.bootstrap.ComboBox} combo This combo box
14925         */
14926         'collapse' : true,
14927         /**
14928          * @event beforeselect
14929          * Fires before a list item is selected. Return false to cancel the selection.
14930         * @param {Roo.bootstrap.ComboBox} combo This combo box
14931         * @param {Roo.data.Record} record The data record returned from the underlying store
14932         * @param {Number} index The index of the selected item in the dropdown list
14933         */
14934         'beforeselect' : true,
14935         /**
14936          * @event select
14937          * Fires when a list item is selected
14938         * @param {Roo.bootstrap.ComboBox} combo This combo box
14939         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14940         * @param {Number} index The index of the selected item in the dropdown list
14941         */
14942         'select' : true,
14943         /**
14944          * @event beforequery
14945          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14946          * The event object passed has these properties:
14947         * @param {Roo.bootstrap.ComboBox} combo This combo box
14948         * @param {String} query The query
14949         * @param {Boolean} forceAll true to force "all" query
14950         * @param {Boolean} cancel true to cancel the query
14951         * @param {Object} e The query event object
14952         */
14953         'beforequery': true,
14954          /**
14955          * @event add
14956          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14957         * @param {Roo.bootstrap.ComboBox} combo This combo box
14958         */
14959         'add' : true,
14960         /**
14961          * @event edit
14962          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14963         * @param {Roo.bootstrap.ComboBox} combo This combo box
14964         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14965         */
14966         'edit' : true,
14967         /**
14968          * @event remove
14969          * Fires when the remove value from the combobox array
14970         * @param {Roo.bootstrap.ComboBox} combo This combo box
14971         */
14972         'remove' : true,
14973         /**
14974          * @event afterremove
14975          * Fires when the remove value from the combobox array
14976         * @param {Roo.bootstrap.ComboBox} combo This combo box
14977         */
14978         'afterremove' : true,
14979         /**
14980          * @event specialfilter
14981          * Fires when specialfilter
14982             * @param {Roo.bootstrap.ComboBox} combo This combo box
14983             */
14984         'specialfilter' : true,
14985         /**
14986          * @event tick
14987          * Fires when tick the element
14988             * @param {Roo.bootstrap.ComboBox} combo This combo box
14989             */
14990         'tick' : true,
14991         /**
14992          * @event touchviewdisplay
14993          * Fires when touch view require special display (default is using displayField)
14994             * @param {Roo.bootstrap.ComboBox} combo This combo box
14995             * @param {Object} cfg set html .
14996             */
14997         'touchviewdisplay' : true
14998         
14999     });
15000     
15001     this.item = [];
15002     this.tickItems = [];
15003     
15004     this.selectedIndex = -1;
15005     if(this.mode == 'local'){
15006         if(config.queryDelay === undefined){
15007             this.queryDelay = 10;
15008         }
15009         if(config.minChars === undefined){
15010             this.minChars = 0;
15011         }
15012     }
15013 };
15014
15015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15016      
15017     /**
15018      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15019      * rendering into an Roo.Editor, defaults to false)
15020      */
15021     /**
15022      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15023      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15024      */
15025     /**
15026      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15027      */
15028     /**
15029      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15030      * the dropdown list (defaults to undefined, with no header element)
15031      */
15032
15033      /**
15034      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15035      */
15036      
15037      /**
15038      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15039      */
15040     listWidth: undefined,
15041     /**
15042      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15043      * mode = 'remote' or 'text' if mode = 'local')
15044      */
15045     displayField: undefined,
15046     
15047     /**
15048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15049      * mode = 'remote' or 'value' if mode = 'local'). 
15050      * Note: use of a valueField requires the user make a selection
15051      * in order for a value to be mapped.
15052      */
15053     valueField: undefined,
15054     /**
15055      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15056      */
15057     modalTitle : '',
15058     
15059     /**
15060      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15061      * field's data value (defaults to the underlying DOM element's name)
15062      */
15063     hiddenName: undefined,
15064     /**
15065      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15066      */
15067     listClass: '',
15068     /**
15069      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15070      */
15071     selectedClass: 'active',
15072     
15073     /**
15074      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15075      */
15076     shadow:'sides',
15077     /**
15078      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15079      * anchor positions (defaults to 'tl-bl')
15080      */
15081     listAlign: 'tl-bl?',
15082     /**
15083      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15084      */
15085     maxHeight: 300,
15086     /**
15087      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15088      * query specified by the allQuery config option (defaults to 'query')
15089      */
15090     triggerAction: 'query',
15091     /**
15092      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15093      * (defaults to 4, does not apply if editable = false)
15094      */
15095     minChars : 4,
15096     /**
15097      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15098      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15099      */
15100     typeAhead: false,
15101     /**
15102      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15103      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15104      */
15105     queryDelay: 500,
15106     /**
15107      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15108      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15109      */
15110     pageSize: 0,
15111     /**
15112      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15113      * when editable = true (defaults to false)
15114      */
15115     selectOnFocus:false,
15116     /**
15117      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15118      */
15119     queryParam: 'query',
15120     /**
15121      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15122      * when mode = 'remote' (defaults to 'Loading...')
15123      */
15124     loadingText: 'Loading...',
15125     /**
15126      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15127      */
15128     resizable: false,
15129     /**
15130      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15131      */
15132     handleHeight : 8,
15133     /**
15134      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15135      * traditional select (defaults to true)
15136      */
15137     editable: true,
15138     /**
15139      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15140      */
15141     allQuery: '',
15142     /**
15143      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15144      */
15145     mode: 'remote',
15146     /**
15147      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15148      * listWidth has a higher value)
15149      */
15150     minListWidth : 70,
15151     /**
15152      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15153      * allow the user to set arbitrary text into the field (defaults to false)
15154      */
15155     forceSelection:false,
15156     /**
15157      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15158      * if typeAhead = true (defaults to 250)
15159      */
15160     typeAheadDelay : 250,
15161     /**
15162      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15163      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15164      */
15165     valueNotFoundText : undefined,
15166     /**
15167      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15168      */
15169     blockFocus : false,
15170     
15171     /**
15172      * @cfg {Boolean} disableClear Disable showing of clear button.
15173      */
15174     disableClear : false,
15175     /**
15176      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15177      */
15178     alwaysQuery : false,
15179     
15180     /**
15181      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15182      */
15183     multiple : false,
15184     
15185     /**
15186      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15187      */
15188     invalidClass : "has-warning",
15189     
15190     /**
15191      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15192      */
15193     validClass : "has-success",
15194     
15195     /**
15196      * @cfg {Boolean} specialFilter (true|false) special filter default false
15197      */
15198     specialFilter : false,
15199     
15200     /**
15201      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15202      */
15203     mobileTouchView : true,
15204     
15205     /**
15206      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15207      */
15208     useNativeIOS : false,
15209     
15210     /**
15211      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15212      */
15213     mobile_restrict_height : false,
15214     
15215     ios_options : false,
15216     
15217     //private
15218     addicon : false,
15219     editicon: false,
15220     
15221     page: 0,
15222     hasQuery: false,
15223     append: false,
15224     loadNext: false,
15225     autoFocus : true,
15226     tickable : false,
15227     btnPosition : 'right',
15228     triggerList : true,
15229     showToggleBtn : true,
15230     animate : true,
15231     emptyResultText: 'Empty',
15232     triggerText : 'Select',
15233     emptyTitle : '',
15234     width : false,
15235     
15236     // element that contains real text value.. (when hidden is used..)
15237     
15238     getAutoCreate : function()
15239     {   
15240         var cfg = false;
15241         //render
15242         /*
15243          * Render classic select for iso
15244          */
15245         
15246         if(Roo.isIOS && this.useNativeIOS){
15247             cfg = this.getAutoCreateNativeIOS();
15248             return cfg;
15249         }
15250         
15251         /*
15252          * Touch Devices
15253          */
15254         
15255         if(Roo.isTouch && this.mobileTouchView){
15256             cfg = this.getAutoCreateTouchView();
15257             return cfg;;
15258         }
15259         
15260         /*
15261          *  Normal ComboBox
15262          */
15263         if(!this.tickable){
15264             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15265             return cfg;
15266         }
15267         
15268         /*
15269          *  ComboBox with tickable selections
15270          */
15271              
15272         var align = this.labelAlign || this.parentLabelAlign();
15273         
15274         cfg = {
15275             cls : 'form-group roo-combobox-tickable' //input-group
15276         };
15277         
15278         var btn_text_select = '';
15279         var btn_text_done = '';
15280         var btn_text_cancel = '';
15281         
15282         if (this.btn_text_show) {
15283             btn_text_select = 'Select';
15284             btn_text_done = 'Done';
15285             btn_text_cancel = 'Cancel'; 
15286         }
15287         
15288         var buttons = {
15289             tag : 'div',
15290             cls : 'tickable-buttons',
15291             cn : [
15292                 {
15293                     tag : 'button',
15294                     type : 'button',
15295                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15296                     //html : this.triggerText
15297                     html: btn_text_select
15298                 },
15299                 {
15300                     tag : 'button',
15301                     type : 'button',
15302                     name : 'ok',
15303                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15304                     //html : 'Done'
15305                     html: btn_text_done
15306                 },
15307                 {
15308                     tag : 'button',
15309                     type : 'button',
15310                     name : 'cancel',
15311                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15312                     //html : 'Cancel'
15313                     html: btn_text_cancel
15314                 }
15315             ]
15316         };
15317         
15318         if(this.editable){
15319             buttons.cn.unshift({
15320                 tag: 'input',
15321                 cls: 'roo-select2-search-field-input'
15322             });
15323         }
15324         
15325         var _this = this;
15326         
15327         Roo.each(buttons.cn, function(c){
15328             if (_this.size) {
15329                 c.cls += ' btn-' + _this.size;
15330             }
15331
15332             if (_this.disabled) {
15333                 c.disabled = true;
15334             }
15335         });
15336         
15337         var box = {
15338             tag: 'div',
15339             style : 'display: contents',
15340             cn: [
15341                 {
15342                     tag: 'input',
15343                     type : 'hidden',
15344                     cls: 'form-hidden-field'
15345                 },
15346                 {
15347                     tag: 'ul',
15348                     cls: 'roo-select2-choices',
15349                     cn:[
15350                         {
15351                             tag: 'li',
15352                             cls: 'roo-select2-search-field',
15353                             cn: [
15354                                 buttons
15355                             ]
15356                         }
15357                     ]
15358                 }
15359             ]
15360         };
15361         
15362         var combobox = {
15363             cls: 'roo-select2-container input-group roo-select2-container-multi',
15364             cn: [
15365                 
15366                 box
15367 //                {
15368 //                    tag: 'ul',
15369 //                    cls: 'typeahead typeahead-long dropdown-menu',
15370 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15371 //                }
15372             ]
15373         };
15374         
15375         if(this.hasFeedback && !this.allowBlank){
15376             
15377             var feedback = {
15378                 tag: 'span',
15379                 cls: 'glyphicon form-control-feedback'
15380             };
15381
15382             combobox.cn.push(feedback);
15383         }
15384         
15385         
15386         
15387         var indicator = {
15388             tag : 'i',
15389             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15390             tooltip : 'This field is required'
15391         };
15392         if (Roo.bootstrap.version == 4) {
15393             indicator = {
15394                 tag : 'i',
15395                 style : 'display:none'
15396             };
15397         }
15398         if (align ==='left' && this.fieldLabel.length) {
15399             
15400             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15401             
15402             cfg.cn = [
15403                 indicator,
15404                 {
15405                     tag: 'label',
15406                     'for' :  id,
15407                     cls : 'control-label col-form-label',
15408                     html : this.fieldLabel
15409
15410                 },
15411                 {
15412                     cls : "", 
15413                     cn: [
15414                         combobox
15415                     ]
15416                 }
15417
15418             ];
15419             
15420             var labelCfg = cfg.cn[1];
15421             var contentCfg = cfg.cn[2];
15422             
15423
15424             if(this.indicatorpos == 'right'){
15425                 
15426                 cfg.cn = [
15427                     {
15428                         tag: 'label',
15429                         'for' :  id,
15430                         cls : 'control-label col-form-label',
15431                         cn : [
15432                             {
15433                                 tag : 'span',
15434                                 html : this.fieldLabel
15435                             },
15436                             indicator
15437                         ]
15438                     },
15439                     {
15440                         cls : "",
15441                         cn: [
15442                             combobox
15443                         ]
15444                     }
15445
15446                 ];
15447                 
15448                 
15449                 
15450                 labelCfg = cfg.cn[0];
15451                 contentCfg = cfg.cn[1];
15452             
15453             }
15454             
15455             if(this.labelWidth > 12){
15456                 labelCfg.style = "width: " + this.labelWidth + 'px';
15457             }
15458             if(this.width * 1 > 0){
15459                 contentCfg.style = "width: " + this.width + 'px';
15460             }
15461             if(this.labelWidth < 13 && this.labelmd == 0){
15462                 this.labelmd = this.labelWidth;
15463             }
15464             
15465             if(this.labellg > 0){
15466                 labelCfg.cls += ' col-lg-' + this.labellg;
15467                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15468             }
15469             
15470             if(this.labelmd > 0){
15471                 labelCfg.cls += ' col-md-' + this.labelmd;
15472                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15473             }
15474             
15475             if(this.labelsm > 0){
15476                 labelCfg.cls += ' col-sm-' + this.labelsm;
15477                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15478             }
15479             
15480             if(this.labelxs > 0){
15481                 labelCfg.cls += ' col-xs-' + this.labelxs;
15482                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15483             }
15484                 
15485                 
15486         } else if ( this.fieldLabel.length) {
15487 //                Roo.log(" label");
15488                  cfg.cn = [
15489                    indicator,
15490                     {
15491                         tag: 'label',
15492                         //cls : 'input-group-addon',
15493                         html : this.fieldLabel
15494                     },
15495                     combobox
15496                 ];
15497                 
15498                 if(this.indicatorpos == 'right'){
15499                     cfg.cn = [
15500                         {
15501                             tag: 'label',
15502                             //cls : 'input-group-addon',
15503                             html : this.fieldLabel
15504                         },
15505                         indicator,
15506                         combobox
15507                     ];
15508                     
15509                 }
15510
15511         } else {
15512             
15513 //                Roo.log(" no label && no align");
15514                 cfg = combobox
15515                      
15516                 
15517         }
15518          
15519         var settings=this;
15520         ['xs','sm','md','lg'].map(function(size){
15521             if (settings[size]) {
15522                 cfg.cls += ' col-' + size + '-' + settings[size];
15523             }
15524         });
15525         
15526         return cfg;
15527         
15528     },
15529     
15530     _initEventsCalled : false,
15531     
15532     // private
15533     initEvents: function()
15534     {   
15535         if (this._initEventsCalled) { // as we call render... prevent looping...
15536             return;
15537         }
15538         this._initEventsCalled = true;
15539         
15540         if (!this.store) {
15541             throw "can not find store for combo";
15542         }
15543         
15544         this.indicator = this.indicatorEl();
15545         
15546         this.store = Roo.factory(this.store, Roo.data);
15547         this.store.parent = this;
15548         
15549         // if we are building from html. then this element is so complex, that we can not really
15550         // use the rendered HTML.
15551         // so we have to trash and replace the previous code.
15552         if (Roo.XComponent.build_from_html) {
15553             // remove this element....
15554             var e = this.el.dom, k=0;
15555             while (e ) { e = e.previousSibling;  ++k;}
15556
15557             this.el.remove();
15558             
15559             this.el=false;
15560             this.rendered = false;
15561             
15562             this.render(this.parent().getChildContainer(true), k);
15563         }
15564         
15565         if(Roo.isIOS && this.useNativeIOS){
15566             this.initIOSView();
15567             return;
15568         }
15569         
15570         /*
15571          * Touch Devices
15572          */
15573         
15574         if(Roo.isTouch && this.mobileTouchView){
15575             this.initTouchView();
15576             return;
15577         }
15578         
15579         if(this.tickable){
15580             this.initTickableEvents();
15581             return;
15582         }
15583         
15584         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15585         
15586         if(this.hiddenName){
15587             
15588             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15589             
15590             this.hiddenField.dom.value =
15591                 this.hiddenValue !== undefined ? this.hiddenValue :
15592                 this.value !== undefined ? this.value : '';
15593
15594             // prevent input submission
15595             this.el.dom.removeAttribute('name');
15596             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15597              
15598              
15599         }
15600         //if(Roo.isGecko){
15601         //    this.el.dom.setAttribute('autocomplete', 'off');
15602         //}
15603         
15604         var cls = 'x-combo-list';
15605         
15606         //this.list = new Roo.Layer({
15607         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15608         //});
15609         
15610         var _this = this;
15611         
15612         (function(){
15613             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15614             _this.list.setWidth(lw);
15615         }).defer(100);
15616         
15617         this.list.on('mouseover', this.onViewOver, this);
15618         this.list.on('mousemove', this.onViewMove, this);
15619         this.list.on('scroll', this.onViewScroll, this);
15620         
15621         /*
15622         this.list.swallowEvent('mousewheel');
15623         this.assetHeight = 0;
15624
15625         if(this.title){
15626             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15627             this.assetHeight += this.header.getHeight();
15628         }
15629
15630         this.innerList = this.list.createChild({cls:cls+'-inner'});
15631         this.innerList.on('mouseover', this.onViewOver, this);
15632         this.innerList.on('mousemove', this.onViewMove, this);
15633         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15634         
15635         if(this.allowBlank && !this.pageSize && !this.disableClear){
15636             this.footer = this.list.createChild({cls:cls+'-ft'});
15637             this.pageTb = new Roo.Toolbar(this.footer);
15638            
15639         }
15640         if(this.pageSize){
15641             this.footer = this.list.createChild({cls:cls+'-ft'});
15642             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15643                     {pageSize: this.pageSize});
15644             
15645         }
15646         
15647         if (this.pageTb && this.allowBlank && !this.disableClear) {
15648             var _this = this;
15649             this.pageTb.add(new Roo.Toolbar.Fill(), {
15650                 cls: 'x-btn-icon x-btn-clear',
15651                 text: '&#160;',
15652                 handler: function()
15653                 {
15654                     _this.collapse();
15655                     _this.clearValue();
15656                     _this.onSelect(false, -1);
15657                 }
15658             });
15659         }
15660         if (this.footer) {
15661             this.assetHeight += this.footer.getHeight();
15662         }
15663         */
15664             
15665         if(!this.tpl){
15666             this.tpl = Roo.bootstrap.version == 4 ?
15667                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15668                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15669         }
15670
15671         this.view = new Roo.View(this.list, this.tpl, {
15672             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15673         });
15674         //this.view.wrapEl.setDisplayed(false);
15675         this.view.on('click', this.onViewClick, this);
15676         
15677         
15678         this.store.on('beforeload', this.onBeforeLoad, this);
15679         this.store.on('load', this.onLoad, this);
15680         this.store.on('loadexception', this.onLoadException, this);
15681         /*
15682         if(this.resizable){
15683             this.resizer = new Roo.Resizable(this.list,  {
15684                pinned:true, handles:'se'
15685             });
15686             this.resizer.on('resize', function(r, w, h){
15687                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15688                 this.listWidth = w;
15689                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15690                 this.restrictHeight();
15691             }, this);
15692             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15693         }
15694         */
15695         if(!this.editable){
15696             this.editable = true;
15697             this.setEditable(false);
15698         }
15699         
15700         /*
15701         
15702         if (typeof(this.events.add.listeners) != 'undefined') {
15703             
15704             this.addicon = this.wrap.createChild(
15705                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15706        
15707             this.addicon.on('click', function(e) {
15708                 this.fireEvent('add', this);
15709             }, this);
15710         }
15711         if (typeof(this.events.edit.listeners) != 'undefined') {
15712             
15713             this.editicon = this.wrap.createChild(
15714                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15715             if (this.addicon) {
15716                 this.editicon.setStyle('margin-left', '40px');
15717             }
15718             this.editicon.on('click', function(e) {
15719                 
15720                 // we fire even  if inothing is selected..
15721                 this.fireEvent('edit', this, this.lastData );
15722                 
15723             }, this);
15724         }
15725         */
15726         
15727         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15728             "up" : function(e){
15729                 this.inKeyMode = true;
15730                 this.selectPrev();
15731             },
15732
15733             "down" : function(e){
15734                 if(!this.isExpanded()){
15735                     this.onTriggerClick();
15736                 }else{
15737                     this.inKeyMode = true;
15738                     this.selectNext();
15739                 }
15740             },
15741
15742             "enter" : function(e){
15743 //                this.onViewClick();
15744                 //return true;
15745                 this.collapse();
15746                 
15747                 if(this.fireEvent("specialkey", this, e)){
15748                     this.onViewClick(false);
15749                 }
15750                 
15751                 return true;
15752             },
15753
15754             "esc" : function(e){
15755                 this.collapse();
15756             },
15757
15758             "tab" : function(e){
15759                 this.collapse();
15760                 
15761                 if(this.fireEvent("specialkey", this, e)){
15762                     this.onViewClick(false);
15763                 }
15764                 
15765                 return true;
15766             },
15767
15768             scope : this,
15769
15770             doRelay : function(foo, bar, hname){
15771                 if(hname == 'down' || this.scope.isExpanded()){
15772                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15773                 }
15774                 return true;
15775             },
15776
15777             forceKeyDown: true
15778         });
15779         
15780         
15781         this.queryDelay = Math.max(this.queryDelay || 10,
15782                 this.mode == 'local' ? 10 : 250);
15783         
15784         
15785         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15786         
15787         if(this.typeAhead){
15788             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15789         }
15790         if(this.editable !== false){
15791             this.inputEl().on("keyup", this.onKeyUp, this);
15792         }
15793         if(this.forceSelection){
15794             this.inputEl().on('blur', this.doForce, this);
15795         }
15796         
15797         if(this.multiple){
15798             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15799             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15800         }
15801     },
15802     
15803     initTickableEvents: function()
15804     {   
15805         this.createList();
15806         
15807         if(this.hiddenName){
15808             
15809             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15810             
15811             this.hiddenField.dom.value =
15812                 this.hiddenValue !== undefined ? this.hiddenValue :
15813                 this.value !== undefined ? this.value : '';
15814
15815             // prevent input submission
15816             this.el.dom.removeAttribute('name');
15817             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15818              
15819              
15820         }
15821         
15822 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15823         
15824         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15825         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15826         if(this.triggerList){
15827             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15828         }
15829          
15830         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15831         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15832         
15833         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15834         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15835         
15836         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15837         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15838         
15839         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15840         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         
15843         this.okBtn.hide();
15844         this.cancelBtn.hide();
15845         
15846         var _this = this;
15847         
15848         (function(){
15849             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15850             _this.list.setWidth(lw);
15851         }).defer(100);
15852         
15853         this.list.on('mouseover', this.onViewOver, this);
15854         this.list.on('mousemove', this.onViewMove, this);
15855         
15856         this.list.on('scroll', this.onViewScroll, this);
15857         
15858         if(!this.tpl){
15859             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15860                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15861         }
15862
15863         this.view = new Roo.View(this.list, this.tpl, {
15864             singleSelect:true,
15865             tickable:true,
15866             parent:this,
15867             store: this.store,
15868             selectedClass: this.selectedClass
15869         });
15870         
15871         //this.view.wrapEl.setDisplayed(false);
15872         this.view.on('click', this.onViewClick, this);
15873         
15874         
15875         
15876         this.store.on('beforeload', this.onBeforeLoad, this);
15877         this.store.on('load', this.onLoad, this);
15878         this.store.on('loadexception', this.onLoadException, this);
15879         
15880         if(this.editable){
15881             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15882                 "up" : function(e){
15883                     this.inKeyMode = true;
15884                     this.selectPrev();
15885                 },
15886
15887                 "down" : function(e){
15888                     this.inKeyMode = true;
15889                     this.selectNext();
15890                 },
15891
15892                 "enter" : function(e){
15893                     if(this.fireEvent("specialkey", this, e)){
15894                         this.onViewClick(false);
15895                     }
15896                     
15897                     return true;
15898                 },
15899
15900                 "esc" : function(e){
15901                     this.onTickableFooterButtonClick(e, false, false);
15902                 },
15903
15904                 "tab" : function(e){
15905                     this.fireEvent("specialkey", this, e);
15906                     
15907                     this.onTickableFooterButtonClick(e, false, false);
15908                     
15909                     return true;
15910                 },
15911
15912                 scope : this,
15913
15914                 doRelay : function(e, fn, key){
15915                     if(this.scope.isExpanded()){
15916                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15917                     }
15918                     return true;
15919                 },
15920
15921                 forceKeyDown: true
15922             });
15923         }
15924         
15925         this.queryDelay = Math.max(this.queryDelay || 10,
15926                 this.mode == 'local' ? 10 : 250);
15927         
15928         
15929         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15930         
15931         if(this.typeAhead){
15932             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15933         }
15934         
15935         if(this.editable !== false){
15936             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15937         }
15938         
15939         this.indicator = this.indicatorEl();
15940         
15941         if(this.indicator){
15942             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15943             this.indicator.hide();
15944         }
15945         
15946     },
15947
15948     onDestroy : function(){
15949         if(this.view){
15950             this.view.setStore(null);
15951             this.view.el.removeAllListeners();
15952             this.view.el.remove();
15953             this.view.purgeListeners();
15954         }
15955         if(this.list){
15956             this.list.dom.innerHTML  = '';
15957         }
15958         
15959         if(this.store){
15960             this.store.un('beforeload', this.onBeforeLoad, this);
15961             this.store.un('load', this.onLoad, this);
15962             this.store.un('loadexception', this.onLoadException, this);
15963         }
15964         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15965     },
15966
15967     // private
15968     fireKey : function(e){
15969         if(e.isNavKeyPress() && !this.list.isVisible()){
15970             this.fireEvent("specialkey", this, e);
15971         }
15972     },
15973
15974     // private
15975     onResize: function(w, h)
15976     {
15977         
15978         
15979 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15980 //        
15981 //        if(typeof w != 'number'){
15982 //            // we do not handle it!?!?
15983 //            return;
15984 //        }
15985 //        var tw = this.trigger.getWidth();
15986 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15987 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15988 //        var x = w - tw;
15989 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15990 //            
15991 //        //this.trigger.setStyle('left', x+'px');
15992 //        
15993 //        if(this.list && this.listWidth === undefined){
15994 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15995 //            this.list.setWidth(lw);
15996 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15997 //        }
15998         
15999     
16000         
16001     },
16002
16003     /**
16004      * Allow or prevent the user from directly editing the field text.  If false is passed,
16005      * the user will only be able to select from the items defined in the dropdown list.  This method
16006      * is the runtime equivalent of setting the 'editable' config option at config time.
16007      * @param {Boolean} value True to allow the user to directly edit the field text
16008      */
16009     setEditable : function(value){
16010         if(value == this.editable){
16011             return;
16012         }
16013         this.editable = value;
16014         if(!value){
16015             this.inputEl().dom.setAttribute('readOnly', true);
16016             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().addClass('x-combo-noedit');
16018         }else{
16019             this.inputEl().dom.setAttribute('readOnly', false);
16020             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16021             this.inputEl().removeClass('x-combo-noedit');
16022         }
16023     },
16024
16025     // private
16026     
16027     onBeforeLoad : function(combo,opts){
16028         if(!this.hasFocus){
16029             return;
16030         }
16031          if (!opts.add) {
16032             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16033          }
16034         this.restrictHeight();
16035         this.selectedIndex = -1;
16036     },
16037
16038     // private
16039     onLoad : function(){
16040         
16041         this.hasQuery = false;
16042         
16043         if(!this.hasFocus){
16044             return;
16045         }
16046         
16047         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16048             this.loading.hide();
16049         }
16050         
16051         if(this.store.getCount() > 0){
16052             
16053             this.expand();
16054             this.restrictHeight();
16055             if(this.lastQuery == this.allQuery){
16056                 if(this.editable && !this.tickable){
16057                     this.inputEl().dom.select();
16058                 }
16059                 
16060                 if(
16061                     !this.selectByValue(this.value, true) &&
16062                     this.autoFocus && 
16063                     (
16064                         !this.store.lastOptions ||
16065                         typeof(this.store.lastOptions.add) == 'undefined' || 
16066                         this.store.lastOptions.add != true
16067                     )
16068                 ){
16069                     this.select(0, true);
16070                 }
16071             }else{
16072                 if(this.autoFocus){
16073                     this.selectNext();
16074                 }
16075                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16076                     this.taTask.delay(this.typeAheadDelay);
16077                 }
16078             }
16079         }else{
16080             this.onEmptyResults();
16081         }
16082         
16083         //this.el.focus();
16084     },
16085     // private
16086     onLoadException : function()
16087     {
16088         this.hasQuery = false;
16089         
16090         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16091             this.loading.hide();
16092         }
16093         
16094         if(this.tickable && this.editable){
16095             return;
16096         }
16097         
16098         this.collapse();
16099         // only causes errors at present
16100         //Roo.log(this.store.reader.jsonData);
16101         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16102             // fixme
16103             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16104         //}
16105         
16106         
16107     },
16108     // private
16109     onTypeAhead : function(){
16110         if(this.store.getCount() > 0){
16111             var r = this.store.getAt(0);
16112             var newValue = r.data[this.displayField];
16113             var len = newValue.length;
16114             var selStart = this.getRawValue().length;
16115             
16116             if(selStart != len){
16117                 this.setRawValue(newValue);
16118                 this.selectText(selStart, newValue.length);
16119             }
16120         }
16121     },
16122
16123     // private
16124     onSelect : function(record, index){
16125         
16126         if(this.fireEvent('beforeselect', this, record, index) !== false){
16127         
16128             this.setFromData(index > -1 ? record.data : false);
16129             
16130             this.collapse();
16131             this.fireEvent('select', this, record, index);
16132         }
16133     },
16134
16135     /**
16136      * Returns the currently selected field value or empty string if no value is set.
16137      * @return {String} value The selected value
16138      */
16139     getValue : function()
16140     {
16141         if(Roo.isIOS && this.useNativeIOS){
16142             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16143         }
16144         
16145         if(this.multiple){
16146             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16147         }
16148         
16149         if(this.valueField){
16150             return typeof this.value != 'undefined' ? this.value : '';
16151         }else{
16152             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16153         }
16154     },
16155     
16156     getRawValue : function()
16157     {
16158         if(Roo.isIOS && this.useNativeIOS){
16159             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16160         }
16161         
16162         var v = this.inputEl().getValue();
16163         
16164         return v;
16165     },
16166
16167     /**
16168      * Clears any text/value currently set in the field
16169      */
16170     clearValue : function(){
16171         
16172         if(this.hiddenField){
16173             this.hiddenField.dom.value = '';
16174         }
16175         this.value = '';
16176         this.setRawValue('');
16177         this.lastSelectionText = '';
16178         this.lastData = false;
16179         
16180         var close = this.closeTriggerEl();
16181         
16182         if(close){
16183             close.hide();
16184         }
16185         
16186         this.validate();
16187         
16188     },
16189
16190     /**
16191      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16192      * will be displayed in the field.  If the value does not match the data value of an existing item,
16193      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16194      * Otherwise the field will be blank (although the value will still be set).
16195      * @param {String} value The value to match
16196      */
16197     setValue : function(v)
16198     {
16199         if(Roo.isIOS && this.useNativeIOS){
16200             this.setIOSValue(v);
16201             return;
16202         }
16203         
16204         if(this.multiple){
16205             this.syncValue();
16206             return;
16207         }
16208         
16209         var text = v;
16210         if(this.valueField){
16211             var r = this.findRecord(this.valueField, v);
16212             if(r){
16213                 text = r.data[this.displayField];
16214             }else if(this.valueNotFoundText !== undefined){
16215                 text = this.valueNotFoundText;
16216             }
16217         }
16218         this.lastSelectionText = text;
16219         if(this.hiddenField){
16220             this.hiddenField.dom.value = v;
16221         }
16222         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16223         this.value = v;
16224         
16225         var close = this.closeTriggerEl();
16226         
16227         if(close){
16228             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16229         }
16230         
16231         this.validate();
16232     },
16233     /**
16234      * @property {Object} the last set data for the element
16235      */
16236     
16237     lastData : false,
16238     /**
16239      * Sets the value of the field based on a object which is related to the record format for the store.
16240      * @param {Object} value the value to set as. or false on reset?
16241      */
16242     setFromData : function(o){
16243         
16244         if(this.multiple){
16245             this.addItem(o);
16246             return;
16247         }
16248             
16249         var dv = ''; // display value
16250         var vv = ''; // value value..
16251         this.lastData = o;
16252         if (this.displayField) {
16253             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16254         } else {
16255             // this is an error condition!!!
16256             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16257         }
16258         
16259         if(this.valueField){
16260             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16261         }
16262         
16263         var close = this.closeTriggerEl();
16264         
16265         if(close){
16266             if(dv.length || vv * 1 > 0){
16267                 close.show() ;
16268                 this.blockFocus=true;
16269             } else {
16270                 close.hide();
16271             }             
16272         }
16273         
16274         if(this.hiddenField){
16275             this.hiddenField.dom.value = vv;
16276             
16277             this.lastSelectionText = dv;
16278             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16279             this.value = vv;
16280             return;
16281         }
16282         // no hidden field.. - we store the value in 'value', but still display
16283         // display field!!!!
16284         this.lastSelectionText = dv;
16285         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16286         this.value = vv;
16287         
16288         
16289         
16290     },
16291     // private
16292     reset : function(){
16293         // overridden so that last data is reset..
16294         
16295         if(this.multiple){
16296             this.clearItem();
16297             return;
16298         }
16299         
16300         this.setValue(this.originalValue);
16301         //this.clearInvalid();
16302         this.lastData = false;
16303         if (this.view) {
16304             this.view.clearSelections();
16305         }
16306         
16307         this.validate();
16308     },
16309     // private
16310     findRecord : function(prop, value){
16311         var record;
16312         if(this.store.getCount() > 0){
16313             this.store.each(function(r){
16314                 if(r.data[prop] == value){
16315                     record = r;
16316                     return false;
16317                 }
16318                 return true;
16319             });
16320         }
16321         return record;
16322     },
16323     
16324     getName: function()
16325     {
16326         // returns hidden if it's set..
16327         if (!this.rendered) {return ''};
16328         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16329         
16330     },
16331     // private
16332     onViewMove : function(e, t){
16333         this.inKeyMode = false;
16334     },
16335
16336     // private
16337     onViewOver : function(e, t){
16338         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16339             return;
16340         }
16341         var item = this.view.findItemFromChild(t);
16342         
16343         if(item){
16344             var index = this.view.indexOf(item);
16345             this.select(index, false);
16346         }
16347     },
16348
16349     // private
16350     onViewClick : function(view, doFocus, el, e)
16351     {
16352         var index = this.view.getSelectedIndexes()[0];
16353         
16354         var r = this.store.getAt(index);
16355         
16356         if(this.tickable){
16357             
16358             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16359                 return;
16360             }
16361             
16362             var rm = false;
16363             var _this = this;
16364             
16365             Roo.each(this.tickItems, function(v,k){
16366                 
16367                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16368                     Roo.log(v);
16369                     _this.tickItems.splice(k, 1);
16370                     
16371                     if(typeof(e) == 'undefined' && view == false){
16372                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16373                     }
16374                     
16375                     rm = true;
16376                     return;
16377                 }
16378             });
16379             
16380             if(rm){
16381                 return;
16382             }
16383             
16384             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16385                 this.tickItems.push(r.data);
16386             }
16387             
16388             if(typeof(e) == 'undefined' && view == false){
16389                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16390             }
16391                     
16392             return;
16393         }
16394         
16395         if(r){
16396             this.onSelect(r, index);
16397         }
16398         if(doFocus !== false && !this.blockFocus){
16399             this.inputEl().focus();
16400         }
16401     },
16402
16403     // private
16404     restrictHeight : function(){
16405         //this.innerList.dom.style.height = '';
16406         //var inner = this.innerList.dom;
16407         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16408         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16409         //this.list.beginUpdate();
16410         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16411         this.list.alignTo(this.inputEl(), this.listAlign);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         //this.list.endUpdate();
16414     },
16415
16416     // private
16417     onEmptyResults : function(){
16418         
16419         if(this.tickable && this.editable){
16420             this.hasFocus = false;
16421             this.restrictHeight();
16422             return;
16423         }
16424         
16425         this.collapse();
16426     },
16427
16428     /**
16429      * Returns true if the dropdown list is expanded, else false.
16430      */
16431     isExpanded : function(){
16432         return this.list.isVisible();
16433     },
16434
16435     /**
16436      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16437      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16438      * @param {String} value The data value of the item to select
16439      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16440      * selected item if it is not currently in view (defaults to true)
16441      * @return {Boolean} True if the value matched an item in the list, else false
16442      */
16443     selectByValue : function(v, scrollIntoView){
16444         if(v !== undefined && v !== null){
16445             var r = this.findRecord(this.valueField || this.displayField, v);
16446             if(r){
16447                 this.select(this.store.indexOf(r), scrollIntoView);
16448                 return true;
16449             }
16450         }
16451         return false;
16452     },
16453
16454     /**
16455      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16456      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16457      * @param {Number} index The zero-based index of the list item to select
16458      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16459      * selected item if it is not currently in view (defaults to true)
16460      */
16461     select : function(index, scrollIntoView){
16462         this.selectedIndex = index;
16463         this.view.select(index);
16464         if(scrollIntoView !== false){
16465             var el = this.view.getNode(index);
16466             /*
16467              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16468              */
16469             if(el){
16470                 this.list.scrollChildIntoView(el, false);
16471             }
16472         }
16473     },
16474
16475     // private
16476     selectNext : function(){
16477         var ct = this.store.getCount();
16478         if(ct > 0){
16479             if(this.selectedIndex == -1){
16480                 this.select(0);
16481             }else if(this.selectedIndex < ct-1){
16482                 this.select(this.selectedIndex+1);
16483             }
16484         }
16485     },
16486
16487     // private
16488     selectPrev : function(){
16489         var ct = this.store.getCount();
16490         if(ct > 0){
16491             if(this.selectedIndex == -1){
16492                 this.select(0);
16493             }else if(this.selectedIndex != 0){
16494                 this.select(this.selectedIndex-1);
16495             }
16496         }
16497     },
16498
16499     // private
16500     onKeyUp : function(e){
16501         if(this.editable !== false && !e.isSpecialKey()){
16502             this.lastKey = e.getKey();
16503             this.dqTask.delay(this.queryDelay);
16504         }
16505     },
16506
16507     // private
16508     validateBlur : function(){
16509         return !this.list || !this.list.isVisible();   
16510     },
16511
16512     // private
16513     initQuery : function(){
16514         
16515         var v = this.getRawValue();
16516         
16517         if(this.tickable && this.editable){
16518             v = this.tickableInputEl().getValue();
16519         }
16520         
16521         this.doQuery(v);
16522     },
16523
16524     // private
16525     doForce : function(){
16526         if(this.inputEl().dom.value.length > 0){
16527             this.inputEl().dom.value =
16528                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16529              
16530         }
16531     },
16532
16533     /**
16534      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16535      * query allowing the query action to be canceled if needed.
16536      * @param {String} query The SQL query to execute
16537      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16538      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16539      * saved in the current store (defaults to false)
16540      */
16541     doQuery : function(q, forceAll){
16542         
16543         if(q === undefined || q === null){
16544             q = '';
16545         }
16546         var qe = {
16547             query: q,
16548             forceAll: forceAll,
16549             combo: this,
16550             cancel:false
16551         };
16552         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16553             return false;
16554         }
16555         q = qe.query;
16556         
16557         forceAll = qe.forceAll;
16558         if(forceAll === true || (q.length >= this.minChars)){
16559             
16560             this.hasQuery = true;
16561             
16562             if(this.lastQuery != q || this.alwaysQuery){
16563                 this.lastQuery = q;
16564                 if(this.mode == 'local'){
16565                     this.selectedIndex = -1;
16566                     if(forceAll){
16567                         this.store.clearFilter();
16568                     }else{
16569                         
16570                         if(this.specialFilter){
16571                             this.fireEvent('specialfilter', this);
16572                             this.onLoad();
16573                             return;
16574                         }
16575                         
16576                         this.store.filter(this.displayField, q);
16577                     }
16578                     
16579                     this.store.fireEvent("datachanged", this.store);
16580                     
16581                     this.onLoad();
16582                     
16583                     
16584                 }else{
16585                     
16586                     this.store.baseParams[this.queryParam] = q;
16587                     
16588                     var options = {params : this.getParams(q)};
16589                     
16590                     if(this.loadNext){
16591                         options.add = true;
16592                         options.params.start = this.page * this.pageSize;
16593                     }
16594                     
16595                     this.store.load(options);
16596                     
16597                     /*
16598                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16599                      *  we should expand the list on onLoad
16600                      *  so command out it
16601                      */
16602 //                    this.expand();
16603                 }
16604             }else{
16605                 this.selectedIndex = -1;
16606                 this.onLoad();   
16607             }
16608         }
16609         
16610         this.loadNext = false;
16611     },
16612     
16613     // private
16614     getParams : function(q){
16615         var p = {};
16616         //p[this.queryParam] = q;
16617         
16618         if(this.pageSize){
16619             p.start = 0;
16620             p.limit = this.pageSize;
16621         }
16622         return p;
16623     },
16624
16625     /**
16626      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16627      */
16628     collapse : function(){
16629         if(!this.isExpanded()){
16630             return;
16631         }
16632         
16633         this.list.hide();
16634         
16635         this.hasFocus = false;
16636         
16637         if(this.tickable){
16638             this.okBtn.hide();
16639             this.cancelBtn.hide();
16640             this.trigger.show();
16641             
16642             if(this.editable){
16643                 this.tickableInputEl().dom.value = '';
16644                 this.tickableInputEl().blur();
16645             }
16646             
16647         }
16648         
16649         Roo.get(document).un('mousedown', this.collapseIf, this);
16650         Roo.get(document).un('mousewheel', this.collapseIf, this);
16651         if (!this.editable) {
16652             Roo.get(document).un('keydown', this.listKeyPress, this);
16653         }
16654         this.fireEvent('collapse', this);
16655         
16656         this.validate();
16657     },
16658
16659     // private
16660     collapseIf : function(e){
16661         var in_combo  = e.within(this.el);
16662         var in_list =  e.within(this.list);
16663         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16664         
16665         if (in_combo || in_list || is_list) {
16666             //e.stopPropagation();
16667             return;
16668         }
16669         
16670         if(this.tickable){
16671             this.onTickableFooterButtonClick(e, false, false);
16672         }
16673
16674         this.collapse();
16675         
16676     },
16677
16678     /**
16679      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16680      */
16681     expand : function(){
16682        
16683         if(this.isExpanded() || !this.hasFocus){
16684             return;
16685         }
16686         
16687         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16688         this.list.setWidth(lw);
16689         
16690         Roo.log('expand');
16691         
16692         this.list.show();
16693         
16694         this.restrictHeight();
16695         
16696         if(this.tickable){
16697             
16698             this.tickItems = Roo.apply([], this.item);
16699             
16700             this.okBtn.show();
16701             this.cancelBtn.show();
16702             this.trigger.hide();
16703             
16704             if(this.editable){
16705                 this.tickableInputEl().focus();
16706             }
16707             
16708         }
16709         
16710         Roo.get(document).on('mousedown', this.collapseIf, this);
16711         Roo.get(document).on('mousewheel', this.collapseIf, this);
16712         if (!this.editable) {
16713             Roo.get(document).on('keydown', this.listKeyPress, this);
16714         }
16715         
16716         this.fireEvent('expand', this);
16717     },
16718
16719     // private
16720     // Implements the default empty TriggerField.onTriggerClick function
16721     onTriggerClick : function(e)
16722     {
16723         Roo.log('trigger click');
16724         
16725         if(this.disabled || !this.triggerList){
16726             return;
16727         }
16728         
16729         this.page = 0;
16730         this.loadNext = false;
16731         
16732         if(this.isExpanded()){
16733             this.collapse();
16734             if (!this.blockFocus) {
16735                 this.inputEl().focus();
16736             }
16737             
16738         }else {
16739             this.hasFocus = true;
16740             if(this.triggerAction == 'all') {
16741                 this.doQuery(this.allQuery, true);
16742             } else {
16743                 this.doQuery(this.getRawValue());
16744             }
16745             if (!this.blockFocus) {
16746                 this.inputEl().focus();
16747             }
16748         }
16749     },
16750     
16751     onTickableTriggerClick : function(e)
16752     {
16753         if(this.disabled){
16754             return;
16755         }
16756         
16757         this.page = 0;
16758         this.loadNext = false;
16759         this.hasFocus = true;
16760         
16761         if(this.triggerAction == 'all') {
16762             this.doQuery(this.allQuery, true);
16763         } else {
16764             this.doQuery(this.getRawValue());
16765         }
16766     },
16767     
16768     onSearchFieldClick : function(e)
16769     {
16770         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16771             this.onTickableFooterButtonClick(e, false, false);
16772             return;
16773         }
16774         
16775         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16776             return;
16777         }
16778         
16779         this.page = 0;
16780         this.loadNext = false;
16781         this.hasFocus = true;
16782         
16783         if(this.triggerAction == 'all') {
16784             this.doQuery(this.allQuery, true);
16785         } else {
16786             this.doQuery(this.getRawValue());
16787         }
16788     },
16789     
16790     listKeyPress : function(e)
16791     {
16792         //Roo.log('listkeypress');
16793         // scroll to first matching element based on key pres..
16794         if (e.isSpecialKey()) {
16795             return false;
16796         }
16797         var k = String.fromCharCode(e.getKey()).toUpperCase();
16798         //Roo.log(k);
16799         var match  = false;
16800         var csel = this.view.getSelectedNodes();
16801         var cselitem = false;
16802         if (csel.length) {
16803             var ix = this.view.indexOf(csel[0]);
16804             cselitem  = this.store.getAt(ix);
16805             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16806                 cselitem = false;
16807             }
16808             
16809         }
16810         
16811         this.store.each(function(v) { 
16812             if (cselitem) {
16813                 // start at existing selection.
16814                 if (cselitem.id == v.id) {
16815                     cselitem = false;
16816                 }
16817                 return true;
16818             }
16819                 
16820             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16821                 match = this.store.indexOf(v);
16822                 return false;
16823             }
16824             return true;
16825         }, this);
16826         
16827         if (match === false) {
16828             return true; // no more action?
16829         }
16830         // scroll to?
16831         this.view.select(match);
16832         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16833         sn.scrollIntoView(sn.dom.parentNode, false);
16834     },
16835     
16836     onViewScroll : function(e, t){
16837         
16838         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){
16839             return;
16840         }
16841         
16842         this.hasQuery = true;
16843         
16844         this.loading = this.list.select('.loading', true).first();
16845         
16846         if(this.loading === null){
16847             this.list.createChild({
16848                 tag: 'div',
16849                 cls: 'loading roo-select2-more-results roo-select2-active',
16850                 html: 'Loading more results...'
16851             });
16852             
16853             this.loading = this.list.select('.loading', true).first();
16854             
16855             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16856             
16857             this.loading.hide();
16858         }
16859         
16860         this.loading.show();
16861         
16862         var _combo = this;
16863         
16864         this.page++;
16865         this.loadNext = true;
16866         
16867         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16868         
16869         return;
16870     },
16871     
16872     addItem : function(o)
16873     {   
16874         var dv = ''; // display value
16875         
16876         if (this.displayField) {
16877             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16878         } else {
16879             // this is an error condition!!!
16880             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16881         }
16882         
16883         if(!dv.length){
16884             return;
16885         }
16886         
16887         var choice = this.choices.createChild({
16888             tag: 'li',
16889             cls: 'roo-select2-search-choice',
16890             cn: [
16891                 {
16892                     tag: 'div',
16893                     html: dv
16894                 },
16895                 {
16896                     tag: 'a',
16897                     href: '#',
16898                     cls: 'roo-select2-search-choice-close fa fa-times',
16899                     tabindex: '-1'
16900                 }
16901             ]
16902             
16903         }, this.searchField);
16904         
16905         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16906         
16907         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16908         
16909         this.item.push(o);
16910         
16911         this.lastData = o;
16912         
16913         this.syncValue();
16914         
16915         this.inputEl().dom.value = '';
16916         
16917         this.validate();
16918     },
16919     
16920     onRemoveItem : function(e, _self, o)
16921     {
16922         e.preventDefault();
16923         
16924         this.lastItem = Roo.apply([], this.item);
16925         
16926         var index = this.item.indexOf(o.data) * 1;
16927         
16928         if( index < 0){
16929             Roo.log('not this item?!');
16930             return;
16931         }
16932         
16933         this.item.splice(index, 1);
16934         o.item.remove();
16935         
16936         this.syncValue();
16937         
16938         this.fireEvent('remove', this, e);
16939         
16940         this.validate();
16941         
16942     },
16943     
16944     syncValue : function()
16945     {
16946         if(!this.item.length){
16947             this.clearValue();
16948             return;
16949         }
16950             
16951         var value = [];
16952         var _this = this;
16953         Roo.each(this.item, function(i){
16954             if(_this.valueField){
16955                 value.push(i[_this.valueField]);
16956                 return;
16957             }
16958
16959             value.push(i);
16960         });
16961
16962         this.value = value.join(',');
16963
16964         if(this.hiddenField){
16965             this.hiddenField.dom.value = this.value;
16966         }
16967         
16968         this.store.fireEvent("datachanged", this.store);
16969         
16970         this.validate();
16971     },
16972     
16973     clearItem : function()
16974     {
16975         if(!this.multiple){
16976             return;
16977         }
16978         
16979         this.item = [];
16980         
16981         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16982            c.remove();
16983         });
16984         
16985         this.syncValue();
16986         
16987         this.validate();
16988         
16989         if(this.tickable && !Roo.isTouch){
16990             this.view.refresh();
16991         }
16992     },
16993     
16994     inputEl: function ()
16995     {
16996         if(Roo.isIOS && this.useNativeIOS){
16997             return this.el.select('select.roo-ios-select', true).first();
16998         }
16999         
17000         if(Roo.isTouch && this.mobileTouchView){
17001             return this.el.select('input.form-control',true).first();
17002         }
17003         
17004         if(this.tickable){
17005             return this.searchField;
17006         }
17007         
17008         return this.el.select('input.form-control',true).first();
17009     },
17010     
17011     onTickableFooterButtonClick : function(e, btn, el)
17012     {
17013         e.preventDefault();
17014         
17015         this.lastItem = Roo.apply([], this.item);
17016         
17017         if(btn && btn.name == 'cancel'){
17018             this.tickItems = Roo.apply([], this.item);
17019             this.collapse();
17020             return;
17021         }
17022         
17023         this.clearItem();
17024         
17025         var _this = this;
17026         
17027         Roo.each(this.tickItems, function(o){
17028             _this.addItem(o);
17029         });
17030         
17031         this.collapse();
17032         
17033     },
17034     
17035     validate : function()
17036     {
17037         if(this.getVisibilityEl().hasClass('hidden')){
17038             return true;
17039         }
17040         
17041         var v = this.getRawValue();
17042         
17043         if(this.multiple){
17044             v = this.getValue();
17045         }
17046         
17047         if(this.disabled || this.allowBlank || v.length){
17048             this.markValid();
17049             return true;
17050         }
17051         
17052         this.markInvalid();
17053         return false;
17054     },
17055     
17056     tickableInputEl : function()
17057     {
17058         if(!this.tickable || !this.editable){
17059             return this.inputEl();
17060         }
17061         
17062         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17063     },
17064     
17065     
17066     getAutoCreateTouchView : function()
17067     {
17068         var id = Roo.id();
17069         
17070         var cfg = {
17071             cls: 'form-group' //input-group
17072         };
17073         
17074         var input =  {
17075             tag: 'input',
17076             id : id,
17077             type : this.inputType,
17078             cls : 'form-control x-combo-noedit',
17079             autocomplete: 'new-password',
17080             placeholder : this.placeholder || '',
17081             readonly : true
17082         };
17083         
17084         if (this.name) {
17085             input.name = this.name;
17086         }
17087         
17088         if (this.size) {
17089             input.cls += ' input-' + this.size;
17090         }
17091         
17092         if (this.disabled) {
17093             input.disabled = true;
17094         }
17095         
17096         var inputblock = {
17097             cls : 'roo-combobox-wrap',
17098             cn : [
17099                 input
17100             ]
17101         };
17102         
17103         if(this.before){
17104             inputblock.cls += ' input-group';
17105             
17106             inputblock.cn.unshift({
17107                 tag :'span',
17108                 cls : 'input-group-addon input-group-prepend input-group-text',
17109                 html : this.before
17110             });
17111         }
17112         
17113         if(this.removable && !this.multiple){
17114             inputblock.cls += ' roo-removable';
17115             
17116             inputblock.cn.push({
17117                 tag: 'button',
17118                 html : 'x',
17119                 cls : 'roo-combo-removable-btn close'
17120             });
17121         }
17122
17123         if(this.hasFeedback && !this.allowBlank){
17124             
17125             inputblock.cls += ' has-feedback';
17126             
17127             inputblock.cn.push({
17128                 tag: 'span',
17129                 cls: 'glyphicon form-control-feedback'
17130             });
17131             
17132         }
17133         
17134         if (this.after) {
17135             
17136             inputblock.cls += (this.before) ? '' : ' input-group';
17137             
17138             inputblock.cn.push({
17139                 tag :'span',
17140                 cls : 'input-group-addon input-group-append input-group-text',
17141                 html : this.after
17142             });
17143         }
17144
17145         
17146         var ibwrap = inputblock;
17147         
17148         if(this.multiple){
17149             ibwrap = {
17150                 tag: 'ul',
17151                 cls: 'roo-select2-choices',
17152                 cn:[
17153                     {
17154                         tag: 'li',
17155                         cls: 'roo-select2-search-field',
17156                         cn: [
17157
17158                             inputblock
17159                         ]
17160                     }
17161                 ]
17162             };
17163         
17164             
17165         }
17166         
17167         var combobox = {
17168             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17169             cn: [
17170                 {
17171                     tag: 'input',
17172                     type : 'hidden',
17173                     cls: 'form-hidden-field'
17174                 },
17175                 ibwrap
17176             ]
17177         };
17178         
17179         if(!this.multiple && this.showToggleBtn){
17180             
17181             var caret = {
17182                 cls: 'caret'
17183             };
17184             
17185             if (this.caret != false) {
17186                 caret = {
17187                      tag: 'i',
17188                      cls: 'fa fa-' + this.caret
17189                 };
17190                 
17191             }
17192             
17193             combobox.cn.push({
17194                 tag :'span',
17195                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17196                 cn : [
17197                     Roo.bootstrap.version == 3 ? caret : '',
17198                     {
17199                         tag: 'span',
17200                         cls: 'combobox-clear',
17201                         cn  : [
17202                             {
17203                                 tag : 'i',
17204                                 cls: 'icon-remove'
17205                             }
17206                         ]
17207                     }
17208                 ]
17209
17210             })
17211         }
17212         
17213         if(this.multiple){
17214             combobox.cls += ' roo-select2-container-multi';
17215         }
17216         
17217         var align = this.labelAlign || this.parentLabelAlign();
17218         
17219         if (align ==='left' && this.fieldLabel.length) {
17220
17221             cfg.cn = [
17222                 {
17223                    tag : 'i',
17224                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17225                    tooltip : 'This field is required'
17226                 },
17227                 {
17228                     tag: 'label',
17229                     cls : 'control-label col-form-label',
17230                     html : this.fieldLabel
17231
17232                 },
17233                 {
17234                     cls : 'roo-combobox-wrap ', 
17235                     cn: [
17236                         combobox
17237                     ]
17238                 }
17239             ];
17240             
17241             var labelCfg = cfg.cn[1];
17242             var contentCfg = cfg.cn[2];
17243             
17244
17245             if(this.indicatorpos == 'right'){
17246                 cfg.cn = [
17247                     {
17248                         tag: 'label',
17249                         'for' :  id,
17250                         cls : 'control-label col-form-label',
17251                         cn : [
17252                             {
17253                                 tag : 'span',
17254                                 html : this.fieldLabel
17255                             },
17256                             {
17257                                 tag : 'i',
17258                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17259                                 tooltip : 'This field is required'
17260                             }
17261                         ]
17262                     },
17263                     {
17264                         cls : "roo-combobox-wrap ",
17265                         cn: [
17266                             combobox
17267                         ]
17268                     }
17269
17270                 ];
17271                 
17272                 labelCfg = cfg.cn[0];
17273                 contentCfg = cfg.cn[1];
17274             }
17275             
17276            
17277             
17278             if(this.labelWidth > 12){
17279                 labelCfg.style = "width: " + this.labelWidth + 'px';
17280             }
17281            
17282             if(this.labelWidth < 13 && this.labelmd == 0){
17283                 this.labelmd = this.labelWidth;
17284             }
17285             
17286             if(this.labellg > 0){
17287                 labelCfg.cls += ' col-lg-' + this.labellg;
17288                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17289             }
17290             
17291             if(this.labelmd > 0){
17292                 labelCfg.cls += ' col-md-' + this.labelmd;
17293                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17294             }
17295             
17296             if(this.labelsm > 0){
17297                 labelCfg.cls += ' col-sm-' + this.labelsm;
17298                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17299             }
17300             
17301             if(this.labelxs > 0){
17302                 labelCfg.cls += ' col-xs-' + this.labelxs;
17303                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17304             }
17305                 
17306                 
17307         } else if ( this.fieldLabel.length) {
17308             cfg.cn = [
17309                 {
17310                    tag : 'i',
17311                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17312                    tooltip : 'This field is required'
17313                 },
17314                 {
17315                     tag: 'label',
17316                     cls : 'control-label',
17317                     html : this.fieldLabel
17318
17319                 },
17320                 {
17321                     cls : '', 
17322                     cn: [
17323                         combobox
17324                     ]
17325                 }
17326             ];
17327             
17328             if(this.indicatorpos == 'right'){
17329                 cfg.cn = [
17330                     {
17331                         tag: 'label',
17332                         cls : 'control-label',
17333                         html : this.fieldLabel,
17334                         cn : [
17335                             {
17336                                tag : 'i',
17337                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17338                                tooltip : 'This field is required'
17339                             }
17340                         ]
17341                     },
17342                     {
17343                         cls : '', 
17344                         cn: [
17345                             combobox
17346                         ]
17347                     }
17348                 ];
17349             }
17350         } else {
17351             cfg.cn = combobox;    
17352         }
17353         
17354         
17355         var settings = this;
17356         
17357         ['xs','sm','md','lg'].map(function(size){
17358             if (settings[size]) {
17359                 cfg.cls += ' col-' + size + '-' + settings[size];
17360             }
17361         });
17362         
17363         return cfg;
17364     },
17365     
17366     initTouchView : function()
17367     {
17368         this.renderTouchView();
17369         
17370         this.touchViewEl.on('scroll', function(){
17371             this.el.dom.scrollTop = 0;
17372         }, this);
17373         
17374         this.originalValue = this.getValue();
17375         
17376         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17377         
17378         this.inputEl().on("click", this.showTouchView, this);
17379         if (this.triggerEl) {
17380             this.triggerEl.on("click", this.showTouchView, this);
17381         }
17382         
17383         
17384         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17385         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17386         
17387         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17388         
17389         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17390         this.store.on('load', this.onTouchViewLoad, this);
17391         this.store.on('loadexception', this.onTouchViewLoadException, this);
17392         
17393         if(this.hiddenName){
17394             
17395             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17396             
17397             this.hiddenField.dom.value =
17398                 this.hiddenValue !== undefined ? this.hiddenValue :
17399                 this.value !== undefined ? this.value : '';
17400         
17401             this.el.dom.removeAttribute('name');
17402             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17403         }
17404         
17405         if(this.multiple){
17406             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17407             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17408         }
17409         
17410         if(this.removable && !this.multiple){
17411             var close = this.closeTriggerEl();
17412             if(close){
17413                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17414                 close.on('click', this.removeBtnClick, this, close);
17415             }
17416         }
17417         /*
17418          * fix the bug in Safari iOS8
17419          */
17420         this.inputEl().on("focus", function(e){
17421             document.activeElement.blur();
17422         }, this);
17423         
17424         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17425         
17426         return;
17427         
17428         
17429     },
17430     
17431     renderTouchView : function()
17432     {
17433         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17434         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17435         
17436         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17437         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17438         
17439         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17440         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         this.touchViewBodyEl.setStyle('overflow', 'auto');
17442         
17443         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17444         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17445         
17446         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17447         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17448         
17449     },
17450     
17451     showTouchView : function()
17452     {
17453         if(this.disabled){
17454             return;
17455         }
17456         
17457         this.touchViewHeaderEl.hide();
17458
17459         if(this.modalTitle.length){
17460             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17461             this.touchViewHeaderEl.show();
17462         }
17463
17464         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17465         this.touchViewEl.show();
17466
17467         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17468         
17469         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17470         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17471
17472         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17473
17474         if(this.modalTitle.length){
17475             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17476         }
17477         
17478         this.touchViewBodyEl.setHeight(bodyHeight);
17479
17480         if(this.animate){
17481             var _this = this;
17482             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17483         }else{
17484             this.touchViewEl.addClass(['in','show']);
17485         }
17486         
17487         if(this._touchViewMask){
17488             Roo.get(document.body).addClass("x-body-masked");
17489             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17490             this._touchViewMask.setStyle('z-index', 10000);
17491             this._touchViewMask.addClass('show');
17492         }
17493         
17494         this.doTouchViewQuery();
17495         
17496     },
17497     
17498     hideTouchView : function()
17499     {
17500         this.touchViewEl.removeClass(['in','show']);
17501
17502         if(this.animate){
17503             var _this = this;
17504             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17505         }else{
17506             this.touchViewEl.setStyle('display', 'none');
17507         }
17508         
17509         if(this._touchViewMask){
17510             this._touchViewMask.removeClass('show');
17511             Roo.get(document.body).removeClass("x-body-masked");
17512         }
17513     },
17514     
17515     setTouchViewValue : function()
17516     {
17517         if(this.multiple){
17518             this.clearItem();
17519         
17520             var _this = this;
17521
17522             Roo.each(this.tickItems, function(o){
17523                 this.addItem(o);
17524             }, this);
17525         }
17526         
17527         this.hideTouchView();
17528     },
17529     
17530     doTouchViewQuery : function()
17531     {
17532         var qe = {
17533             query: '',
17534             forceAll: true,
17535             combo: this,
17536             cancel:false
17537         };
17538         
17539         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17540             return false;
17541         }
17542         
17543         if(!this.alwaysQuery || this.mode == 'local'){
17544             this.onTouchViewLoad();
17545             return;
17546         }
17547         
17548         this.store.load();
17549     },
17550     
17551     onTouchViewBeforeLoad : function(combo,opts)
17552     {
17553         return;
17554     },
17555
17556     // private
17557     onTouchViewLoad : function()
17558     {
17559         if(this.store.getCount() < 1){
17560             this.onTouchViewEmptyResults();
17561             return;
17562         }
17563         
17564         this.clearTouchView();
17565         
17566         var rawValue = this.getRawValue();
17567         
17568         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17569         
17570         this.tickItems = [];
17571         
17572         this.store.data.each(function(d, rowIndex){
17573             var row = this.touchViewListGroup.createChild(template);
17574             
17575             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17576                 row.addClass(d.data.cls);
17577             }
17578             
17579             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17580                 var cfg = {
17581                     data : d.data,
17582                     html : d.data[this.displayField]
17583                 };
17584                 
17585                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17586                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17587                 }
17588             }
17589             row.removeClass('selected');
17590             if(!this.multiple && this.valueField &&
17591                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17592             {
17593                 // radio buttons..
17594                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17595                 row.addClass('selected');
17596             }
17597             
17598             if(this.multiple && this.valueField &&
17599                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17600             {
17601                 
17602                 // checkboxes...
17603                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17604                 this.tickItems.push(d.data);
17605             }
17606             
17607             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17608             
17609         }, this);
17610         
17611         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17612         
17613         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17614
17615         if(this.modalTitle.length){
17616             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17617         }
17618
17619         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17620         
17621         if(this.mobile_restrict_height && listHeight < bodyHeight){
17622             this.touchViewBodyEl.setHeight(listHeight);
17623         }
17624         
17625         var _this = this;
17626         
17627         if(firstChecked && listHeight > bodyHeight){
17628             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17629         }
17630         
17631     },
17632     
17633     onTouchViewLoadException : function()
17634     {
17635         this.hideTouchView();
17636     },
17637     
17638     onTouchViewEmptyResults : function()
17639     {
17640         this.clearTouchView();
17641         
17642         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17643         
17644         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17645         
17646     },
17647     
17648     clearTouchView : function()
17649     {
17650         this.touchViewListGroup.dom.innerHTML = '';
17651     },
17652     
17653     onTouchViewClick : function(e, el, o)
17654     {
17655         e.preventDefault();
17656         
17657         var row = o.row;
17658         var rowIndex = o.rowIndex;
17659         
17660         var r = this.store.getAt(rowIndex);
17661         
17662         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17663             
17664             if(!this.multiple){
17665                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17666                     c.dom.removeAttribute('checked');
17667                 }, this);
17668
17669                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17670
17671                 this.setFromData(r.data);
17672
17673                 var close = this.closeTriggerEl();
17674
17675                 if(close){
17676                     close.show();
17677                 }
17678
17679                 this.hideTouchView();
17680
17681                 this.fireEvent('select', this, r, rowIndex);
17682
17683                 return;
17684             }
17685
17686             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17687                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17688                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17689                 return;
17690             }
17691
17692             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17693             this.addItem(r.data);
17694             this.tickItems.push(r.data);
17695         }
17696     },
17697     
17698     getAutoCreateNativeIOS : function()
17699     {
17700         var cfg = {
17701             cls: 'form-group' //input-group,
17702         };
17703         
17704         var combobox =  {
17705             tag: 'select',
17706             cls : 'roo-ios-select'
17707         };
17708         
17709         if (this.name) {
17710             combobox.name = this.name;
17711         }
17712         
17713         if (this.disabled) {
17714             combobox.disabled = true;
17715         }
17716         
17717         var settings = this;
17718         
17719         ['xs','sm','md','lg'].map(function(size){
17720             if (settings[size]) {
17721                 cfg.cls += ' col-' + size + '-' + settings[size];
17722             }
17723         });
17724         
17725         cfg.cn = combobox;
17726         
17727         return cfg;
17728         
17729     },
17730     
17731     initIOSView : function()
17732     {
17733         this.store.on('load', this.onIOSViewLoad, this);
17734         
17735         return;
17736     },
17737     
17738     onIOSViewLoad : function()
17739     {
17740         if(this.store.getCount() < 1){
17741             return;
17742         }
17743         
17744         this.clearIOSView();
17745         
17746         if(this.allowBlank) {
17747             
17748             var default_text = '-- SELECT --';
17749             
17750             if(this.placeholder.length){
17751                 default_text = this.placeholder;
17752             }
17753             
17754             if(this.emptyTitle.length){
17755                 default_text += ' - ' + this.emptyTitle + ' -';
17756             }
17757             
17758             var opt = this.inputEl().createChild({
17759                 tag: 'option',
17760                 value : 0,
17761                 html : default_text
17762             });
17763             
17764             var o = {};
17765             o[this.valueField] = 0;
17766             o[this.displayField] = default_text;
17767             
17768             this.ios_options.push({
17769                 data : o,
17770                 el : opt
17771             });
17772             
17773         }
17774         
17775         this.store.data.each(function(d, rowIndex){
17776             
17777             var html = '';
17778             
17779             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17780                 html = d.data[this.displayField];
17781             }
17782             
17783             var value = '';
17784             
17785             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17786                 value = d.data[this.valueField];
17787             }
17788             
17789             var option = {
17790                 tag: 'option',
17791                 value : value,
17792                 html : html
17793             };
17794             
17795             if(this.value == d.data[this.valueField]){
17796                 option['selected'] = true;
17797             }
17798             
17799             var opt = this.inputEl().createChild(option);
17800             
17801             this.ios_options.push({
17802                 data : d.data,
17803                 el : opt
17804             });
17805             
17806         }, this);
17807         
17808         this.inputEl().on('change', function(){
17809            this.fireEvent('select', this);
17810         }, this);
17811         
17812     },
17813     
17814     clearIOSView: function()
17815     {
17816         this.inputEl().dom.innerHTML = '';
17817         
17818         this.ios_options = [];
17819     },
17820     
17821     setIOSValue: function(v)
17822     {
17823         this.value = v;
17824         
17825         if(!this.ios_options){
17826             return;
17827         }
17828         
17829         Roo.each(this.ios_options, function(opts){
17830            
17831            opts.el.dom.removeAttribute('selected');
17832            
17833            if(opts.data[this.valueField] != v){
17834                return;
17835            }
17836            
17837            opts.el.dom.setAttribute('selected', true);
17838            
17839         }, this);
17840     }
17841
17842     /** 
17843     * @cfg {Boolean} grow 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMin 
17848     * @hide 
17849     */
17850     /** 
17851     * @cfg {Number} growMax 
17852     * @hide 
17853     */
17854     /**
17855      * @hide
17856      * @method autoSize
17857      */
17858 });
17859
17860 Roo.apply(Roo.bootstrap.ComboBox,  {
17861     
17862     header : {
17863         tag: 'div',
17864         cls: 'modal-header',
17865         cn: [
17866             {
17867                 tag: 'h4',
17868                 cls: 'modal-title'
17869             }
17870         ]
17871     },
17872     
17873     body : {
17874         tag: 'div',
17875         cls: 'modal-body',
17876         cn: [
17877             {
17878                 tag: 'ul',
17879                 cls: 'list-group'
17880             }
17881         ]
17882     },
17883     
17884     listItemRadio : {
17885         tag: 'li',
17886         cls: 'list-group-item',
17887         cn: [
17888             {
17889                 tag: 'span',
17890                 cls: 'roo-combobox-list-group-item-value'
17891             },
17892             {
17893                 tag: 'div',
17894                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17895                 cn: [
17896                     {
17897                         tag: 'input',
17898                         type: 'radio'
17899                     },
17900                     {
17901                         tag: 'label'
17902                     }
17903                 ]
17904             }
17905         ]
17906     },
17907     
17908     listItemCheckbox : {
17909         tag: 'li',
17910         cls: 'list-group-item',
17911         cn: [
17912             {
17913                 tag: 'span',
17914                 cls: 'roo-combobox-list-group-item-value'
17915             },
17916             {
17917                 tag: 'div',
17918                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17919                 cn: [
17920                     {
17921                         tag: 'input',
17922                         type: 'checkbox'
17923                     },
17924                     {
17925                         tag: 'label'
17926                     }
17927                 ]
17928             }
17929         ]
17930     },
17931     
17932     emptyResult : {
17933         tag: 'div',
17934         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17935     },
17936     
17937     footer : {
17938         tag: 'div',
17939         cls: 'modal-footer',
17940         cn: [
17941             {
17942                 tag: 'div',
17943                 cls: 'row',
17944                 cn: [
17945                     {
17946                         tag: 'div',
17947                         cls: 'col-xs-6 text-left',
17948                         cn: {
17949                             tag: 'button',
17950                             cls: 'btn btn-danger roo-touch-view-cancel',
17951                             html: 'Cancel'
17952                         }
17953                     },
17954                     {
17955                         tag: 'div',
17956                         cls: 'col-xs-6 text-right',
17957                         cn: {
17958                             tag: 'button',
17959                             cls: 'btn btn-success roo-touch-view-ok',
17960                             html: 'OK'
17961                         }
17962                     }
17963                 ]
17964             }
17965         ]
17966         
17967     }
17968 });
17969
17970 Roo.apply(Roo.bootstrap.ComboBox,  {
17971     
17972     touchViewTemplate : {
17973         tag: 'div',
17974         cls: 'modal fade roo-combobox-touch-view',
17975         cn: [
17976             {
17977                 tag: 'div',
17978                 cls: 'modal-dialog',
17979                 style : 'position:fixed', // we have to fix position....
17980                 cn: [
17981                     {
17982                         tag: 'div',
17983                         cls: 'modal-content',
17984                         cn: [
17985                             Roo.bootstrap.ComboBox.header,
17986                             Roo.bootstrap.ComboBox.body,
17987                             Roo.bootstrap.ComboBox.footer
17988                         ]
17989                     }
17990                 ]
17991             }
17992         ]
17993     }
17994 });/*
17995  * Based on:
17996  * Ext JS Library 1.1.1
17997  * Copyright(c) 2006-2007, Ext JS, LLC.
17998  *
17999  * Originally Released Under LGPL - original licence link has changed is not relivant.
18000  *
18001  * Fork - LGPL
18002  * <script type="text/javascript">
18003  */
18004
18005 /**
18006  * @class Roo.View
18007  * @extends Roo.util.Observable
18008  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18009  * This class also supports single and multi selection modes. <br>
18010  * Create a data model bound view:
18011  <pre><code>
18012  var store = new Roo.data.Store(...);
18013
18014  var view = new Roo.View({
18015     el : "my-element",
18016     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18017  
18018     singleSelect: true,
18019     selectedClass: "ydataview-selected",
18020     store: store
18021  });
18022
18023  // listen for node click?
18024  view.on("click", function(vw, index, node, e){
18025  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18026  });
18027
18028  // load XML data
18029  dataModel.load("foobar.xml");
18030  </code></pre>
18031  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18032  * <br><br>
18033  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18034  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18035  * 
18036  * Note: old style constructor is still suported (container, template, config)
18037  * 
18038  * @constructor
18039  * Create a new View
18040  * @param {Object} config The config object
18041  * 
18042  */
18043 Roo.View = function(config, depreciated_tpl, depreciated_config){
18044     
18045     this.parent = false;
18046     
18047     if (typeof(depreciated_tpl) == 'undefined') {
18048         // new way.. - universal constructor.
18049         Roo.apply(this, config);
18050         this.el  = Roo.get(this.el);
18051     } else {
18052         // old format..
18053         this.el  = Roo.get(config);
18054         this.tpl = depreciated_tpl;
18055         Roo.apply(this, depreciated_config);
18056     }
18057     this.wrapEl  = this.el.wrap().wrap();
18058     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18059     
18060     
18061     if(typeof(this.tpl) == "string"){
18062         this.tpl = new Roo.Template(this.tpl);
18063     } else {
18064         // support xtype ctors..
18065         this.tpl = new Roo.factory(this.tpl, Roo);
18066     }
18067     
18068     
18069     this.tpl.compile();
18070     
18071     /** @private */
18072     this.addEvents({
18073         /**
18074          * @event beforeclick
18075          * Fires before a click is processed. Returns false to cancel the default action.
18076          * @param {Roo.View} this
18077          * @param {Number} index The index of the target node
18078          * @param {HTMLElement} node The target node
18079          * @param {Roo.EventObject} e The raw event object
18080          */
18081             "beforeclick" : true,
18082         /**
18083          * @event click
18084          * Fires when a template node is clicked.
18085          * @param {Roo.View} this
18086          * @param {Number} index The index of the target node
18087          * @param {HTMLElement} node The target node
18088          * @param {Roo.EventObject} e The raw event object
18089          */
18090             "click" : true,
18091         /**
18092          * @event dblclick
18093          * Fires when a template node is double clicked.
18094          * @param {Roo.View} this
18095          * @param {Number} index The index of the target node
18096          * @param {HTMLElement} node The target node
18097          * @param {Roo.EventObject} e The raw event object
18098          */
18099             "dblclick" : true,
18100         /**
18101          * @event contextmenu
18102          * Fires when a template node is right clicked.
18103          * @param {Roo.View} this
18104          * @param {Number} index The index of the target node
18105          * @param {HTMLElement} node The target node
18106          * @param {Roo.EventObject} e The raw event object
18107          */
18108             "contextmenu" : true,
18109         /**
18110          * @event selectionchange
18111          * Fires when the selected nodes change.
18112          * @param {Roo.View} this
18113          * @param {Array} selections Array of the selected nodes
18114          */
18115             "selectionchange" : true,
18116     
18117         /**
18118          * @event beforeselect
18119          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18120          * @param {Roo.View} this
18121          * @param {HTMLElement} node The node to be selected
18122          * @param {Array} selections Array of currently selected nodes
18123          */
18124             "beforeselect" : true,
18125         /**
18126          * @event preparedata
18127          * Fires on every row to render, to allow you to change the data.
18128          * @param {Roo.View} this
18129          * @param {Object} data to be rendered (change this)
18130          */
18131           "preparedata" : true
18132           
18133           
18134         });
18135
18136
18137
18138     this.el.on({
18139         "click": this.onClick,
18140         "dblclick": this.onDblClick,
18141         "contextmenu": this.onContextMenu,
18142         scope:this
18143     });
18144
18145     this.selections = [];
18146     this.nodes = [];
18147     this.cmp = new Roo.CompositeElementLite([]);
18148     if(this.store){
18149         this.store = Roo.factory(this.store, Roo.data);
18150         this.setStore(this.store, true);
18151     }
18152     
18153     if ( this.footer && this.footer.xtype) {
18154            
18155          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18156         
18157         this.footer.dataSource = this.store;
18158         this.footer.container = fctr;
18159         this.footer = Roo.factory(this.footer, Roo);
18160         fctr.insertFirst(this.el);
18161         
18162         // this is a bit insane - as the paging toolbar seems to detach the el..
18163 //        dom.parentNode.parentNode.parentNode
18164          // they get detached?
18165     }
18166     
18167     
18168     Roo.View.superclass.constructor.call(this);
18169     
18170     
18171 };
18172
18173 Roo.extend(Roo.View, Roo.util.Observable, {
18174     
18175      /**
18176      * @cfg {Roo.data.Store} store Data store to load data from.
18177      */
18178     store : false,
18179     
18180     /**
18181      * @cfg {String|Roo.Element} el The container element.
18182      */
18183     el : '',
18184     
18185     /**
18186      * @cfg {String|Roo.Template} tpl The template used by this View 
18187      */
18188     tpl : false,
18189     /**
18190      * @cfg {String} dataName the named area of the template to use as the data area
18191      *                          Works with domtemplates roo-name="name"
18192      */
18193     dataName: false,
18194     /**
18195      * @cfg {String} selectedClass The css class to add to selected nodes
18196      */
18197     selectedClass : "x-view-selected",
18198      /**
18199      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18200      */
18201     emptyText : "",
18202     
18203     /**
18204      * @cfg {String} text to display on mask (default Loading)
18205      */
18206     mask : false,
18207     /**
18208      * @cfg {Boolean} multiSelect Allow multiple selection
18209      */
18210     multiSelect : false,
18211     /**
18212      * @cfg {Boolean} singleSelect Allow single selection
18213      */
18214     singleSelect:  false,
18215     
18216     /**
18217      * @cfg {Boolean} toggleSelect - selecting 
18218      */
18219     toggleSelect : false,
18220     
18221     /**
18222      * @cfg {Boolean} tickable - selecting 
18223      */
18224     tickable : false,
18225     
18226     /**
18227      * Returns the element this view is bound to.
18228      * @return {Roo.Element}
18229      */
18230     getEl : function(){
18231         return this.wrapEl;
18232     },
18233     
18234     
18235
18236     /**
18237      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18238      */
18239     refresh : function(){
18240         //Roo.log('refresh');
18241         var t = this.tpl;
18242         
18243         // if we are using something like 'domtemplate', then
18244         // the what gets used is:
18245         // t.applySubtemplate(NAME, data, wrapping data..)
18246         // the outer template then get' applied with
18247         //     the store 'extra data'
18248         // and the body get's added to the
18249         //      roo-name="data" node?
18250         //      <span class='roo-tpl-{name}'></span> ?????
18251         
18252         
18253         
18254         this.clearSelections();
18255         this.el.update("");
18256         var html = [];
18257         var records = this.store.getRange();
18258         if(records.length < 1) {
18259             
18260             // is this valid??  = should it render a template??
18261             
18262             this.el.update(this.emptyText);
18263             return;
18264         }
18265         var el = this.el;
18266         if (this.dataName) {
18267             this.el.update(t.apply(this.store.meta)); //????
18268             el = this.el.child('.roo-tpl-' + this.dataName);
18269         }
18270         
18271         for(var i = 0, len = records.length; i < len; i++){
18272             var data = this.prepareData(records[i].data, i, records[i]);
18273             this.fireEvent("preparedata", this, data, i, records[i]);
18274             
18275             var d = Roo.apply({}, data);
18276             
18277             if(this.tickable){
18278                 Roo.apply(d, {'roo-id' : Roo.id()});
18279                 
18280                 var _this = this;
18281             
18282                 Roo.each(this.parent.item, function(item){
18283                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18284                         return;
18285                     }
18286                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18287                 });
18288             }
18289             
18290             html[html.length] = Roo.util.Format.trim(
18291                 this.dataName ?
18292                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18293                     t.apply(d)
18294             );
18295         }
18296         
18297         
18298         
18299         el.update(html.join(""));
18300         this.nodes = el.dom.childNodes;
18301         this.updateIndexes(0);
18302     },
18303     
18304
18305     /**
18306      * Function to override to reformat the data that is sent to
18307      * the template for each node.
18308      * DEPRICATED - use the preparedata event handler.
18309      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18310      * a JSON object for an UpdateManager bound view).
18311      */
18312     prepareData : function(data, index, record)
18313     {
18314         this.fireEvent("preparedata", this, data, index, record);
18315         return data;
18316     },
18317
18318     onUpdate : function(ds, record){
18319         // Roo.log('on update');   
18320         this.clearSelections();
18321         var index = this.store.indexOf(record);
18322         var n = this.nodes[index];
18323         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18324         n.parentNode.removeChild(n);
18325         this.updateIndexes(index, index);
18326     },
18327
18328     
18329     
18330 // --------- FIXME     
18331     onAdd : function(ds, records, index)
18332     {
18333         //Roo.log(['on Add', ds, records, index] );        
18334         this.clearSelections();
18335         if(this.nodes.length == 0){
18336             this.refresh();
18337             return;
18338         }
18339         var n = this.nodes[index];
18340         for(var i = 0, len = records.length; i < len; i++){
18341             var d = this.prepareData(records[i].data, i, records[i]);
18342             if(n){
18343                 this.tpl.insertBefore(n, d);
18344             }else{
18345                 
18346                 this.tpl.append(this.el, d);
18347             }
18348         }
18349         this.updateIndexes(index);
18350     },
18351
18352     onRemove : function(ds, record, index){
18353        // Roo.log('onRemove');
18354         this.clearSelections();
18355         var el = this.dataName  ?
18356             this.el.child('.roo-tpl-' + this.dataName) :
18357             this.el; 
18358         
18359         el.dom.removeChild(this.nodes[index]);
18360         this.updateIndexes(index);
18361     },
18362
18363     /**
18364      * Refresh an individual node.
18365      * @param {Number} index
18366      */
18367     refreshNode : function(index){
18368         this.onUpdate(this.store, this.store.getAt(index));
18369     },
18370
18371     updateIndexes : function(startIndex, endIndex){
18372         var ns = this.nodes;
18373         startIndex = startIndex || 0;
18374         endIndex = endIndex || ns.length - 1;
18375         for(var i = startIndex; i <= endIndex; i++){
18376             ns[i].nodeIndex = i;
18377         }
18378     },
18379
18380     /**
18381      * Changes the data store this view uses and refresh the view.
18382      * @param {Store} store
18383      */
18384     setStore : function(store, initial){
18385         if(!initial && this.store){
18386             this.store.un("datachanged", this.refresh);
18387             this.store.un("add", this.onAdd);
18388             this.store.un("remove", this.onRemove);
18389             this.store.un("update", this.onUpdate);
18390             this.store.un("clear", this.refresh);
18391             this.store.un("beforeload", this.onBeforeLoad);
18392             this.store.un("load", this.onLoad);
18393             this.store.un("loadexception", this.onLoad);
18394         }
18395         if(store){
18396           
18397             store.on("datachanged", this.refresh, this);
18398             store.on("add", this.onAdd, this);
18399             store.on("remove", this.onRemove, this);
18400             store.on("update", this.onUpdate, this);
18401             store.on("clear", this.refresh, this);
18402             store.on("beforeload", this.onBeforeLoad, this);
18403             store.on("load", this.onLoad, this);
18404             store.on("loadexception", this.onLoad, this);
18405         }
18406         
18407         if(store){
18408             this.refresh();
18409         }
18410     },
18411     /**
18412      * onbeforeLoad - masks the loading area.
18413      *
18414      */
18415     onBeforeLoad : function(store,opts)
18416     {
18417          //Roo.log('onBeforeLoad');   
18418         if (!opts.add) {
18419             this.el.update("");
18420         }
18421         this.el.mask(this.mask ? this.mask : "Loading" ); 
18422     },
18423     onLoad : function ()
18424     {
18425         this.el.unmask();
18426     },
18427     
18428
18429     /**
18430      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18431      * @param {HTMLElement} node
18432      * @return {HTMLElement} The template node
18433      */
18434     findItemFromChild : function(node){
18435         var el = this.dataName  ?
18436             this.el.child('.roo-tpl-' + this.dataName,true) :
18437             this.el.dom; 
18438         
18439         if(!node || node.parentNode == el){
18440                     return node;
18441             }
18442             var p = node.parentNode;
18443             while(p && p != el){
18444             if(p.parentNode == el){
18445                 return p;
18446             }
18447             p = p.parentNode;
18448         }
18449             return null;
18450     },
18451
18452     /** @ignore */
18453     onClick : function(e){
18454         var item = this.findItemFromChild(e.getTarget());
18455         if(item){
18456             var index = this.indexOf(item);
18457             if(this.onItemClick(item, index, e) !== false){
18458                 this.fireEvent("click", this, index, item, e);
18459             }
18460         }else{
18461             this.clearSelections();
18462         }
18463     },
18464
18465     /** @ignore */
18466     onContextMenu : function(e){
18467         var item = this.findItemFromChild(e.getTarget());
18468         if(item){
18469             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18470         }
18471     },
18472
18473     /** @ignore */
18474     onDblClick : function(e){
18475         var item = this.findItemFromChild(e.getTarget());
18476         if(item){
18477             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18478         }
18479     },
18480
18481     onItemClick : function(item, index, e)
18482     {
18483         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18484             return false;
18485         }
18486         if (this.toggleSelect) {
18487             var m = this.isSelected(item) ? 'unselect' : 'select';
18488             //Roo.log(m);
18489             var _t = this;
18490             _t[m](item, true, false);
18491             return true;
18492         }
18493         if(this.multiSelect || this.singleSelect){
18494             if(this.multiSelect && e.shiftKey && this.lastSelection){
18495                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18496             }else{
18497                 this.select(item, this.multiSelect && e.ctrlKey);
18498                 this.lastSelection = item;
18499             }
18500             
18501             if(!this.tickable){
18502                 e.preventDefault();
18503             }
18504             
18505         }
18506         return true;
18507     },
18508
18509     /**
18510      * Get the number of selected nodes.
18511      * @return {Number}
18512      */
18513     getSelectionCount : function(){
18514         return this.selections.length;
18515     },
18516
18517     /**
18518      * Get the currently selected nodes.
18519      * @return {Array} An array of HTMLElements
18520      */
18521     getSelectedNodes : function(){
18522         return this.selections;
18523     },
18524
18525     /**
18526      * Get the indexes of the selected nodes.
18527      * @return {Array}
18528      */
18529     getSelectedIndexes : function(){
18530         var indexes = [], s = this.selections;
18531         for(var i = 0, len = s.length; i < len; i++){
18532             indexes.push(s[i].nodeIndex);
18533         }
18534         return indexes;
18535     },
18536
18537     /**
18538      * Clear all selections
18539      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18540      */
18541     clearSelections : function(suppressEvent){
18542         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18543             this.cmp.elements = this.selections;
18544             this.cmp.removeClass(this.selectedClass);
18545             this.selections = [];
18546             if(!suppressEvent){
18547                 this.fireEvent("selectionchange", this, this.selections);
18548             }
18549         }
18550     },
18551
18552     /**
18553      * Returns true if the passed node is selected
18554      * @param {HTMLElement/Number} node The node or node index
18555      * @return {Boolean}
18556      */
18557     isSelected : function(node){
18558         var s = this.selections;
18559         if(s.length < 1){
18560             return false;
18561         }
18562         node = this.getNode(node);
18563         return s.indexOf(node) !== -1;
18564     },
18565
18566     /**
18567      * Selects nodes.
18568      * @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
18569      * @param {Boolean} keepExisting (optional) true to keep existing selections
18570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18571      */
18572     select : function(nodeInfo, keepExisting, suppressEvent){
18573         if(nodeInfo instanceof Array){
18574             if(!keepExisting){
18575                 this.clearSelections(true);
18576             }
18577             for(var i = 0, len = nodeInfo.length; i < len; i++){
18578                 this.select(nodeInfo[i], true, true);
18579             }
18580             return;
18581         } 
18582         var node = this.getNode(nodeInfo);
18583         if(!node || this.isSelected(node)){
18584             return; // already selected.
18585         }
18586         if(!keepExisting){
18587             this.clearSelections(true);
18588         }
18589         
18590         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18591             Roo.fly(node).addClass(this.selectedClass);
18592             this.selections.push(node);
18593             if(!suppressEvent){
18594                 this.fireEvent("selectionchange", this, this.selections);
18595             }
18596         }
18597         
18598         
18599     },
18600       /**
18601      * Unselects nodes.
18602      * @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
18603      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18605      */
18606     unselect : function(nodeInfo, keepExisting, suppressEvent)
18607     {
18608         if(nodeInfo instanceof Array){
18609             Roo.each(this.selections, function(s) {
18610                 this.unselect(s, nodeInfo);
18611             }, this);
18612             return;
18613         }
18614         var node = this.getNode(nodeInfo);
18615         if(!node || !this.isSelected(node)){
18616             //Roo.log("not selected");
18617             return; // not selected.
18618         }
18619         // fireevent???
18620         var ns = [];
18621         Roo.each(this.selections, function(s) {
18622             if (s == node ) {
18623                 Roo.fly(node).removeClass(this.selectedClass);
18624
18625                 return;
18626             }
18627             ns.push(s);
18628         },this);
18629         
18630         this.selections= ns;
18631         this.fireEvent("selectionchange", this, this.selections);
18632     },
18633
18634     /**
18635      * Gets a template node.
18636      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18637      * @return {HTMLElement} The node or null if it wasn't found
18638      */
18639     getNode : function(nodeInfo){
18640         if(typeof nodeInfo == "string"){
18641             return document.getElementById(nodeInfo);
18642         }else if(typeof nodeInfo == "number"){
18643             return this.nodes[nodeInfo];
18644         }
18645         return nodeInfo;
18646     },
18647
18648     /**
18649      * Gets a range template nodes.
18650      * @param {Number} startIndex
18651      * @param {Number} endIndex
18652      * @return {Array} An array of nodes
18653      */
18654     getNodes : function(start, end){
18655         var ns = this.nodes;
18656         start = start || 0;
18657         end = typeof end == "undefined" ? ns.length - 1 : end;
18658         var nodes = [];
18659         if(start <= end){
18660             for(var i = start; i <= end; i++){
18661                 nodes.push(ns[i]);
18662             }
18663         } else{
18664             for(var i = start; i >= end; i--){
18665                 nodes.push(ns[i]);
18666             }
18667         }
18668         return nodes;
18669     },
18670
18671     /**
18672      * Finds the index of the passed node
18673      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18674      * @return {Number} The index of the node or -1
18675      */
18676     indexOf : function(node){
18677         node = this.getNode(node);
18678         if(typeof node.nodeIndex == "number"){
18679             return node.nodeIndex;
18680         }
18681         var ns = this.nodes;
18682         for(var i = 0, len = ns.length; i < len; i++){
18683             if(ns[i] == node){
18684                 return i;
18685             }
18686         }
18687         return -1;
18688     }
18689 });
18690 /*
18691  * - LGPL
18692  *
18693  * based on jquery fullcalendar
18694  * 
18695  */
18696
18697 Roo.bootstrap = Roo.bootstrap || {};
18698 /**
18699  * @class Roo.bootstrap.Calendar
18700  * @extends Roo.bootstrap.Component
18701  * Bootstrap Calendar class
18702  * @cfg {Boolean} loadMask (true|false) default false
18703  * @cfg {Object} header generate the user specific header of the calendar, default false
18704
18705  * @constructor
18706  * Create a new Container
18707  * @param {Object} config The config object
18708  */
18709
18710
18711
18712 Roo.bootstrap.Calendar = function(config){
18713     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18714      this.addEvents({
18715         /**
18716              * @event select
18717              * Fires when a date is selected
18718              * @param {DatePicker} this
18719              * @param {Date} date The selected date
18720              */
18721         'select': true,
18722         /**
18723              * @event monthchange
18724              * Fires when the displayed month changes 
18725              * @param {DatePicker} this
18726              * @param {Date} date The selected month
18727              */
18728         'monthchange': true,
18729         /**
18730              * @event evententer
18731              * Fires when mouse over an event
18732              * @param {Calendar} this
18733              * @param {event} Event
18734              */
18735         'evententer': true,
18736         /**
18737              * @event eventleave
18738              * Fires when the mouse leaves an
18739              * @param {Calendar} this
18740              * @param {event}
18741              */
18742         'eventleave': true,
18743         /**
18744              * @event eventclick
18745              * Fires when the mouse click an
18746              * @param {Calendar} this
18747              * @param {event}
18748              */
18749         'eventclick': true
18750         
18751     });
18752
18753 };
18754
18755 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18756     
18757      /**
18758      * @cfg {Number} startDay
18759      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18760      */
18761     startDay : 0,
18762     
18763     loadMask : false,
18764     
18765     header : false,
18766       
18767     getAutoCreate : function(){
18768         
18769         
18770         var fc_button = function(name, corner, style, content ) {
18771             return Roo.apply({},{
18772                 tag : 'span',
18773                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18774                          (corner.length ?
18775                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18776                             ''
18777                         ),
18778                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18779                 unselectable: 'on'
18780             });
18781         };
18782         
18783         var header = {};
18784         
18785         if(!this.header){
18786             header = {
18787                 tag : 'table',
18788                 cls : 'fc-header',
18789                 style : 'width:100%',
18790                 cn : [
18791                     {
18792                         tag: 'tr',
18793                         cn : [
18794                             {
18795                                 tag : 'td',
18796                                 cls : 'fc-header-left',
18797                                 cn : [
18798                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18799                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18800                                     { tag: 'span', cls: 'fc-header-space' },
18801                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18802
18803
18804                                 ]
18805                             },
18806
18807                             {
18808                                 tag : 'td',
18809                                 cls : 'fc-header-center',
18810                                 cn : [
18811                                     {
18812                                         tag: 'span',
18813                                         cls: 'fc-header-title',
18814                                         cn : {
18815                                             tag: 'H2',
18816                                             html : 'month / year'
18817                                         }
18818                                     }
18819
18820                                 ]
18821                             },
18822                             {
18823                                 tag : 'td',
18824                                 cls : 'fc-header-right',
18825                                 cn : [
18826                               /*      fc_button('month', 'left', '', 'month' ),
18827                                     fc_button('week', '', '', 'week' ),
18828                                     fc_button('day', 'right', '', 'day' )
18829                                 */    
18830
18831                                 ]
18832                             }
18833
18834                         ]
18835                     }
18836                 ]
18837             };
18838         }
18839         
18840         header = this.header;
18841         
18842        
18843         var cal_heads = function() {
18844             var ret = [];
18845             // fixme - handle this.
18846             
18847             for (var i =0; i < Date.dayNames.length; i++) {
18848                 var d = Date.dayNames[i];
18849                 ret.push({
18850                     tag: 'th',
18851                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18852                     html : d.substring(0,3)
18853                 });
18854                 
18855             }
18856             ret[0].cls += ' fc-first';
18857             ret[6].cls += ' fc-last';
18858             return ret;
18859         };
18860         var cal_cell = function(n) {
18861             return  {
18862                 tag: 'td',
18863                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18864                 cn : [
18865                     {
18866                         cn : [
18867                             {
18868                                 cls: 'fc-day-number',
18869                                 html: 'D'
18870                             },
18871                             {
18872                                 cls: 'fc-day-content',
18873                              
18874                                 cn : [
18875                                      {
18876                                         style: 'position: relative;' // height: 17px;
18877                                     }
18878                                 ]
18879                             }
18880                             
18881                             
18882                         ]
18883                     }
18884                 ]
18885                 
18886             }
18887         };
18888         var cal_rows = function() {
18889             
18890             var ret = [];
18891             for (var r = 0; r < 6; r++) {
18892                 var row= {
18893                     tag : 'tr',
18894                     cls : 'fc-week',
18895                     cn : []
18896                 };
18897                 
18898                 for (var i =0; i < Date.dayNames.length; i++) {
18899                     var d = Date.dayNames[i];
18900                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18901
18902                 }
18903                 row.cn[0].cls+=' fc-first';
18904                 row.cn[0].cn[0].style = 'min-height:90px';
18905                 row.cn[6].cls+=' fc-last';
18906                 ret.push(row);
18907                 
18908             }
18909             ret[0].cls += ' fc-first';
18910             ret[4].cls += ' fc-prev-last';
18911             ret[5].cls += ' fc-last';
18912             return ret;
18913             
18914         };
18915         
18916         var cal_table = {
18917             tag: 'table',
18918             cls: 'fc-border-separate',
18919             style : 'width:100%',
18920             cellspacing  : 0,
18921             cn : [
18922                 { 
18923                     tag: 'thead',
18924                     cn : [
18925                         { 
18926                             tag: 'tr',
18927                             cls : 'fc-first fc-last',
18928                             cn : cal_heads()
18929                         }
18930                     ]
18931                 },
18932                 { 
18933                     tag: 'tbody',
18934                     cn : cal_rows()
18935                 }
18936                   
18937             ]
18938         };
18939          
18940          var cfg = {
18941             cls : 'fc fc-ltr',
18942             cn : [
18943                 header,
18944                 {
18945                     cls : 'fc-content',
18946                     style : "position: relative;",
18947                     cn : [
18948                         {
18949                             cls : 'fc-view fc-view-month fc-grid',
18950                             style : 'position: relative',
18951                             unselectable : 'on',
18952                             cn : [
18953                                 {
18954                                     cls : 'fc-event-container',
18955                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18956                                 },
18957                                 cal_table
18958                             ]
18959                         }
18960                     ]
18961     
18962                 }
18963            ] 
18964             
18965         };
18966         
18967          
18968         
18969         return cfg;
18970     },
18971     
18972     
18973     initEvents : function()
18974     {
18975         if(!this.store){
18976             throw "can not find store for calendar";
18977         }
18978         
18979         var mark = {
18980             tag: "div",
18981             cls:"x-dlg-mask",
18982             style: "text-align:center",
18983             cn: [
18984                 {
18985                     tag: "div",
18986                     style: "background-color:white;width:50%;margin:250 auto",
18987                     cn: [
18988                         {
18989                             tag: "img",
18990                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18991                         },
18992                         {
18993                             tag: "span",
18994                             html: "Loading"
18995                         }
18996                         
18997                     ]
18998                 }
18999             ]
19000         };
19001         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19002         
19003         var size = this.el.select('.fc-content', true).first().getSize();
19004         this.maskEl.setSize(size.width, size.height);
19005         this.maskEl.enableDisplayMode("block");
19006         if(!this.loadMask){
19007             this.maskEl.hide();
19008         }
19009         
19010         this.store = Roo.factory(this.store, Roo.data);
19011         this.store.on('load', this.onLoad, this);
19012         this.store.on('beforeload', this.onBeforeLoad, this);
19013         
19014         this.resize();
19015         
19016         this.cells = this.el.select('.fc-day',true);
19017         //Roo.log(this.cells);
19018         this.textNodes = this.el.query('.fc-day-number');
19019         this.cells.addClassOnOver('fc-state-hover');
19020         
19021         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19022         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19023         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19024         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19025         
19026         this.on('monthchange', this.onMonthChange, this);
19027         
19028         this.update(new Date().clearTime());
19029     },
19030     
19031     resize : function() {
19032         var sz  = this.el.getSize();
19033         
19034         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19035         this.el.select('.fc-day-content div',true).setHeight(34);
19036     },
19037     
19038     
19039     // private
19040     showPrevMonth : function(e){
19041         this.update(this.activeDate.add("mo", -1));
19042     },
19043     showToday : function(e){
19044         this.update(new Date().clearTime());
19045     },
19046     // private
19047     showNextMonth : function(e){
19048         this.update(this.activeDate.add("mo", 1));
19049     },
19050
19051     // private
19052     showPrevYear : function(){
19053         this.update(this.activeDate.add("y", -1));
19054     },
19055
19056     // private
19057     showNextYear : function(){
19058         this.update(this.activeDate.add("y", 1));
19059     },
19060
19061     
19062    // private
19063     update : function(date)
19064     {
19065         var vd = this.activeDate;
19066         this.activeDate = date;
19067 //        if(vd && this.el){
19068 //            var t = date.getTime();
19069 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19070 //                Roo.log('using add remove');
19071 //                
19072 //                this.fireEvent('monthchange', this, date);
19073 //                
19074 //                this.cells.removeClass("fc-state-highlight");
19075 //                this.cells.each(function(c){
19076 //                   if(c.dateValue == t){
19077 //                       c.addClass("fc-state-highlight");
19078 //                       setTimeout(function(){
19079 //                            try{c.dom.firstChild.focus();}catch(e){}
19080 //                       }, 50);
19081 //                       return false;
19082 //                   }
19083 //                   return true;
19084 //                });
19085 //                return;
19086 //            }
19087 //        }
19088         
19089         var days = date.getDaysInMonth();
19090         
19091         var firstOfMonth = date.getFirstDateOfMonth();
19092         var startingPos = firstOfMonth.getDay()-this.startDay;
19093         
19094         if(startingPos < this.startDay){
19095             startingPos += 7;
19096         }
19097         
19098         var pm = date.add(Date.MONTH, -1);
19099         var prevStart = pm.getDaysInMonth()-startingPos;
19100 //        
19101         this.cells = this.el.select('.fc-day',true);
19102         this.textNodes = this.el.query('.fc-day-number');
19103         this.cells.addClassOnOver('fc-state-hover');
19104         
19105         var cells = this.cells.elements;
19106         var textEls = this.textNodes;
19107         
19108         Roo.each(cells, function(cell){
19109             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19110         });
19111         
19112         days += startingPos;
19113
19114         // convert everything to numbers so it's fast
19115         var day = 86400000;
19116         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19117         //Roo.log(d);
19118         //Roo.log(pm);
19119         //Roo.log(prevStart);
19120         
19121         var today = new Date().clearTime().getTime();
19122         var sel = date.clearTime().getTime();
19123         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19124         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19125         var ddMatch = this.disabledDatesRE;
19126         var ddText = this.disabledDatesText;
19127         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19128         var ddaysText = this.disabledDaysText;
19129         var format = this.format;
19130         
19131         var setCellClass = function(cal, cell){
19132             cell.row = 0;
19133             cell.events = [];
19134             cell.more = [];
19135             //Roo.log('set Cell Class');
19136             cell.title = "";
19137             var t = d.getTime();
19138             
19139             //Roo.log(d);
19140             
19141             cell.dateValue = t;
19142             if(t == today){
19143                 cell.className += " fc-today";
19144                 cell.className += " fc-state-highlight";
19145                 cell.title = cal.todayText;
19146             }
19147             if(t == sel){
19148                 // disable highlight in other month..
19149                 //cell.className += " fc-state-highlight";
19150                 
19151             }
19152             // disabling
19153             if(t < min) {
19154                 cell.className = " fc-state-disabled";
19155                 cell.title = cal.minText;
19156                 return;
19157             }
19158             if(t > max) {
19159                 cell.className = " fc-state-disabled";
19160                 cell.title = cal.maxText;
19161                 return;
19162             }
19163             if(ddays){
19164                 if(ddays.indexOf(d.getDay()) != -1){
19165                     cell.title = ddaysText;
19166                     cell.className = " fc-state-disabled";
19167                 }
19168             }
19169             if(ddMatch && format){
19170                 var fvalue = d.dateFormat(format);
19171                 if(ddMatch.test(fvalue)){
19172                     cell.title = ddText.replace("%0", fvalue);
19173                     cell.className = " fc-state-disabled";
19174                 }
19175             }
19176             
19177             if (!cell.initialClassName) {
19178                 cell.initialClassName = cell.dom.className;
19179             }
19180             
19181             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19182         };
19183
19184         var i = 0;
19185         
19186         for(; i < startingPos; i++) {
19187             textEls[i].innerHTML = (++prevStart);
19188             d.setDate(d.getDate()+1);
19189             
19190             cells[i].className = "fc-past fc-other-month";
19191             setCellClass(this, cells[i]);
19192         }
19193         
19194         var intDay = 0;
19195         
19196         for(; i < days; i++){
19197             intDay = i - startingPos + 1;
19198             textEls[i].innerHTML = (intDay);
19199             d.setDate(d.getDate()+1);
19200             
19201             cells[i].className = ''; // "x-date-active";
19202             setCellClass(this, cells[i]);
19203         }
19204         var extraDays = 0;
19205         
19206         for(; i < 42; i++) {
19207             textEls[i].innerHTML = (++extraDays);
19208             d.setDate(d.getDate()+1);
19209             
19210             cells[i].className = "fc-future fc-other-month";
19211             setCellClass(this, cells[i]);
19212         }
19213         
19214         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19215         
19216         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19217         
19218         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19219         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19220         
19221         if(totalRows != 6){
19222             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19223             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19224         }
19225         
19226         this.fireEvent('monthchange', this, date);
19227         
19228         
19229         /*
19230         if(!this.internalRender){
19231             var main = this.el.dom.firstChild;
19232             var w = main.offsetWidth;
19233             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19234             Roo.fly(main).setWidth(w);
19235             this.internalRender = true;
19236             // opera does not respect the auto grow header center column
19237             // then, after it gets a width opera refuses to recalculate
19238             // without a second pass
19239             if(Roo.isOpera && !this.secondPass){
19240                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19241                 this.secondPass = true;
19242                 this.update.defer(10, this, [date]);
19243             }
19244         }
19245         */
19246         
19247     },
19248     
19249     findCell : function(dt) {
19250         dt = dt.clearTime().getTime();
19251         var ret = false;
19252         this.cells.each(function(c){
19253             //Roo.log("check " +c.dateValue + '?=' + dt);
19254             if(c.dateValue == dt){
19255                 ret = c;
19256                 return false;
19257             }
19258             return true;
19259         });
19260         
19261         return ret;
19262     },
19263     
19264     findCells : function(ev) {
19265         var s = ev.start.clone().clearTime().getTime();
19266        // Roo.log(s);
19267         var e= ev.end.clone().clearTime().getTime();
19268        // Roo.log(e);
19269         var ret = [];
19270         this.cells.each(function(c){
19271              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19272             
19273             if(c.dateValue > e){
19274                 return ;
19275             }
19276             if(c.dateValue < s){
19277                 return ;
19278             }
19279             ret.push(c);
19280         });
19281         
19282         return ret;    
19283     },
19284     
19285 //    findBestRow: function(cells)
19286 //    {
19287 //        var ret = 0;
19288 //        
19289 //        for (var i =0 ; i < cells.length;i++) {
19290 //            ret  = Math.max(cells[i].rows || 0,ret);
19291 //        }
19292 //        return ret;
19293 //        
19294 //    },
19295     
19296     
19297     addItem : function(ev)
19298     {
19299         // look for vertical location slot in
19300         var cells = this.findCells(ev);
19301         
19302 //        ev.row = this.findBestRow(cells);
19303         
19304         // work out the location.
19305         
19306         var crow = false;
19307         var rows = [];
19308         for(var i =0; i < cells.length; i++) {
19309             
19310             cells[i].row = cells[0].row;
19311             
19312             if(i == 0){
19313                 cells[i].row = cells[i].row + 1;
19314             }
19315             
19316             if (!crow) {
19317                 crow = {
19318                     start : cells[i],
19319                     end :  cells[i]
19320                 };
19321                 continue;
19322             }
19323             if (crow.start.getY() == cells[i].getY()) {
19324                 // on same row.
19325                 crow.end = cells[i];
19326                 continue;
19327             }
19328             // different row.
19329             rows.push(crow);
19330             crow = {
19331                 start: cells[i],
19332                 end : cells[i]
19333             };
19334             
19335         }
19336         
19337         rows.push(crow);
19338         ev.els = [];
19339         ev.rows = rows;
19340         ev.cells = cells;
19341         
19342         cells[0].events.push(ev);
19343         
19344         this.calevents.push(ev);
19345     },
19346     
19347     clearEvents: function() {
19348         
19349         if(!this.calevents){
19350             return;
19351         }
19352         
19353         Roo.each(this.cells.elements, function(c){
19354             c.row = 0;
19355             c.events = [];
19356             c.more = [];
19357         });
19358         
19359         Roo.each(this.calevents, function(e) {
19360             Roo.each(e.els, function(el) {
19361                 el.un('mouseenter' ,this.onEventEnter, this);
19362                 el.un('mouseleave' ,this.onEventLeave, this);
19363                 el.remove();
19364             },this);
19365         },this);
19366         
19367         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19368             e.remove();
19369         });
19370         
19371     },
19372     
19373     renderEvents: function()
19374     {   
19375         var _this = this;
19376         
19377         this.cells.each(function(c) {
19378             
19379             if(c.row < 5){
19380                 return;
19381             }
19382             
19383             var ev = c.events;
19384             
19385             var r = 4;
19386             if(c.row != c.events.length){
19387                 r = 4 - (4 - (c.row - c.events.length));
19388             }
19389             
19390             c.events = ev.slice(0, r);
19391             c.more = ev.slice(r);
19392             
19393             if(c.more.length && c.more.length == 1){
19394                 c.events.push(c.more.pop());
19395             }
19396             
19397             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19398             
19399         });
19400             
19401         this.cells.each(function(c) {
19402             
19403             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19404             
19405             
19406             for (var e = 0; e < c.events.length; e++){
19407                 var ev = c.events[e];
19408                 var rows = ev.rows;
19409                 
19410                 for(var i = 0; i < rows.length; i++) {
19411                 
19412                     // how many rows should it span..
19413
19414                     var  cfg = {
19415                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19416                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19417
19418                         unselectable : "on",
19419                         cn : [
19420                             {
19421                                 cls: 'fc-event-inner',
19422                                 cn : [
19423     //                                {
19424     //                                  tag:'span',
19425     //                                  cls: 'fc-event-time',
19426     //                                  html : cells.length > 1 ? '' : ev.time
19427     //                                },
19428                                     {
19429                                       tag:'span',
19430                                       cls: 'fc-event-title',
19431                                       html : String.format('{0}', ev.title)
19432                                     }
19433
19434
19435                                 ]
19436                             },
19437                             {
19438                                 cls: 'ui-resizable-handle ui-resizable-e',
19439                                 html : '&nbsp;&nbsp;&nbsp'
19440                             }
19441
19442                         ]
19443                     };
19444
19445                     if (i == 0) {
19446                         cfg.cls += ' fc-event-start';
19447                     }
19448                     if ((i+1) == rows.length) {
19449                         cfg.cls += ' fc-event-end';
19450                     }
19451
19452                     var ctr = _this.el.select('.fc-event-container',true).first();
19453                     var cg = ctr.createChild(cfg);
19454
19455                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19456                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19457
19458                     var r = (c.more.length) ? 1 : 0;
19459                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19460                     cg.setWidth(ebox.right - sbox.x -2);
19461
19462                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19463                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19464                     cg.on('click', _this.onEventClick, _this, ev);
19465
19466                     ev.els.push(cg);
19467                     
19468                 }
19469                 
19470             }
19471             
19472             
19473             if(c.more.length){
19474                 var  cfg = {
19475                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19476                     style : 'position: absolute',
19477                     unselectable : "on",
19478                     cn : [
19479                         {
19480                             cls: 'fc-event-inner',
19481                             cn : [
19482                                 {
19483                                   tag:'span',
19484                                   cls: 'fc-event-title',
19485                                   html : 'More'
19486                                 }
19487
19488
19489                             ]
19490                         },
19491                         {
19492                             cls: 'ui-resizable-handle ui-resizable-e',
19493                             html : '&nbsp;&nbsp;&nbsp'
19494                         }
19495
19496                     ]
19497                 };
19498
19499                 var ctr = _this.el.select('.fc-event-container',true).first();
19500                 var cg = ctr.createChild(cfg);
19501
19502                 var sbox = c.select('.fc-day-content',true).first().getBox();
19503                 var ebox = c.select('.fc-day-content',true).first().getBox();
19504                 //Roo.log(cg);
19505                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19506                 cg.setWidth(ebox.right - sbox.x -2);
19507
19508                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19509                 
19510             }
19511             
19512         });
19513         
19514         
19515         
19516     },
19517     
19518     onEventEnter: function (e, el,event,d) {
19519         this.fireEvent('evententer', this, el, event);
19520     },
19521     
19522     onEventLeave: function (e, el,event,d) {
19523         this.fireEvent('eventleave', this, el, event);
19524     },
19525     
19526     onEventClick: function (e, el,event,d) {
19527         this.fireEvent('eventclick', this, el, event);
19528     },
19529     
19530     onMonthChange: function () {
19531         this.store.load();
19532     },
19533     
19534     onMoreEventClick: function(e, el, more)
19535     {
19536         var _this = this;
19537         
19538         this.calpopover.placement = 'right';
19539         this.calpopover.setTitle('More');
19540         
19541         this.calpopover.setContent('');
19542         
19543         var ctr = this.calpopover.el.select('.popover-content', true).first();
19544         
19545         Roo.each(more, function(m){
19546             var cfg = {
19547                 cls : 'fc-event-hori fc-event-draggable',
19548                 html : m.title
19549             };
19550             var cg = ctr.createChild(cfg);
19551             
19552             cg.on('click', _this.onEventClick, _this, m);
19553         });
19554         
19555         this.calpopover.show(el);
19556         
19557         
19558     },
19559     
19560     onLoad: function () 
19561     {   
19562         this.calevents = [];
19563         var cal = this;
19564         
19565         if(this.store.getCount() > 0){
19566             this.store.data.each(function(d){
19567                cal.addItem({
19568                     id : d.data.id,
19569                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19570                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19571                     time : d.data.start_time,
19572                     title : d.data.title,
19573                     description : d.data.description,
19574                     venue : d.data.venue
19575                 });
19576             });
19577         }
19578         
19579         this.renderEvents();
19580         
19581         if(this.calevents.length && this.loadMask){
19582             this.maskEl.hide();
19583         }
19584     },
19585     
19586     onBeforeLoad: function()
19587     {
19588         this.clearEvents();
19589         if(this.loadMask){
19590             this.maskEl.show();
19591         }
19592     }
19593 });
19594
19595  
19596  /*
19597  * - LGPL
19598  *
19599  * element
19600  * 
19601  */
19602
19603 /**
19604  * @class Roo.bootstrap.Popover
19605  * @extends Roo.bootstrap.Component
19606  * Bootstrap Popover class
19607  * @cfg {String} html contents of the popover   (or false to use children..)
19608  * @cfg {String} title of popover (or false to hide)
19609  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19610  * @cfg {String} trigger click || hover (or false to trigger manually)
19611  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19612  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19613  *      - if false and it has a 'parent' then it will be automatically added to that element
19614  *      - if string - Roo.get  will be called 
19615  * @cfg {Number} delay - delay before showing
19616  
19617  * @constructor
19618  * Create a new Popover
19619  * @param {Object} config The config object
19620  */
19621
19622 Roo.bootstrap.Popover = function(config){
19623     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19624     
19625     this.addEvents({
19626         // raw events
19627          /**
19628          * @event show
19629          * After the popover show
19630          * 
19631          * @param {Roo.bootstrap.Popover} this
19632          */
19633         "show" : true,
19634         /**
19635          * @event hide
19636          * After the popover hide
19637          * 
19638          * @param {Roo.bootstrap.Popover} this
19639          */
19640         "hide" : true
19641     });
19642 };
19643
19644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19645     
19646     title: false,
19647     html: false,
19648     
19649     placement : 'right',
19650     trigger : 'hover', // hover
19651     modal : false,
19652     delay : 0,
19653     
19654     over: false,
19655     
19656     can_build_overlaid : false,
19657     
19658     maskEl : false, // the mask element
19659     headerEl : false,
19660     contentEl : false,
19661     
19662     
19663     getChildContainer : function()
19664     {
19665         return this.contentEl;
19666         
19667     },
19668     getPopoverHeader : function()
19669     {
19670         this.title = true; // flag not to hide it..
19671         this.headerEl.addClass('p-0');
19672         return this.headerEl
19673     },
19674     
19675     
19676     getAutoCreate : function(){
19677          
19678         var cfg = {
19679            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19680            style: 'display:block',
19681            cn : [
19682                 {
19683                     cls : 'arrow'
19684                 },
19685                 {
19686                     cls : 'popover-inner ',
19687                     cn : [
19688                         {
19689                             tag: 'h3',
19690                             cls: 'popover-title popover-header',
19691                             html : this.title === false ? '' : this.title
19692                         },
19693                         {
19694                             cls : 'popover-content popover-body '  + (this.cls || ''),
19695                             html : this.html || ''
19696                         }
19697                     ]
19698                     
19699                 }
19700            ]
19701         };
19702         
19703         return cfg;
19704     },
19705     /**
19706      * @param {string} the title
19707      */
19708     setTitle: function(str)
19709     {
19710         this.title = str;
19711         if (this.el) {
19712             this.headerEl.dom.innerHTML = str;
19713         }
19714         
19715     },
19716     /**
19717      * @param {string} the body content
19718      */
19719     setContent: function(str)
19720     {
19721         this.html = str;
19722         if (this.contentEl) {
19723             this.contentEl.dom.innerHTML = str;
19724         }
19725         
19726     },
19727     // as it get's added to the bottom of the page.
19728     onRender : function(ct, position)
19729     {
19730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19731         
19732         
19733         
19734         if(!this.el){
19735             var cfg = Roo.apply({},  this.getAutoCreate());
19736             cfg.id = Roo.id();
19737             
19738             if (this.cls) {
19739                 cfg.cls += ' ' + this.cls;
19740             }
19741             if (this.style) {
19742                 cfg.style = this.style;
19743             }
19744             //Roo.log("adding to ");
19745             this.el = Roo.get(document.body).createChild(cfg, position);
19746 //            Roo.log(this.el);
19747         }
19748         
19749         this.contentEl = this.el.select('.popover-content',true).first();
19750         this.headerEl =  this.el.select('.popover-title',true).first();
19751         
19752         var nitems = [];
19753         if(typeof(this.items) != 'undefined'){
19754             var items = this.items;
19755             delete this.items;
19756
19757             for(var i =0;i < items.length;i++) {
19758                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19759             }
19760         }
19761
19762         this.items = nitems;
19763         
19764         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19765         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19766         
19767         
19768         
19769         this.initEvents();
19770     },
19771     
19772     resizeMask : function()
19773     {
19774         this.maskEl.setSize(
19775             Roo.lib.Dom.getViewWidth(true),
19776             Roo.lib.Dom.getViewHeight(true)
19777         );
19778     },
19779     
19780     initEvents : function()
19781     {
19782         
19783         if (!this.modal) { 
19784             Roo.bootstrap.Popover.register(this);
19785         }
19786          
19787         
19788         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19789         this.el.enableDisplayMode('block');
19790         this.el.hide();
19791         if (this.over === false && !this.parent()) {
19792             return; 
19793         }
19794         if (this.triggers === false) {
19795             return;
19796         }
19797          
19798         // support parent
19799         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19800         var triggers = this.trigger ? this.trigger.split(' ') : [];
19801         Roo.each(triggers, function(trigger) {
19802         
19803             if (trigger == 'click') {
19804                 on_el.on('click', this.toggle, this);
19805             } else if (trigger != 'manual') {
19806                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19807                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19808       
19809                 on_el.on(eventIn  ,this.enter, this);
19810                 on_el.on(eventOut, this.leave, this);
19811             }
19812         }, this);
19813         
19814     },
19815     
19816     
19817     // private
19818     timeout : null,
19819     hoverState : null,
19820     
19821     toggle : function () {
19822         this.hoverState == 'in' ? this.leave() : this.enter();
19823     },
19824     
19825     enter : function () {
19826         
19827         clearTimeout(this.timeout);
19828     
19829         this.hoverState = 'in';
19830     
19831         if (!this.delay || !this.delay.show) {
19832             this.show();
19833             return;
19834         }
19835         var _t = this;
19836         this.timeout = setTimeout(function () {
19837             if (_t.hoverState == 'in') {
19838                 _t.show();
19839             }
19840         }, this.delay.show)
19841     },
19842     
19843     leave : function() {
19844         clearTimeout(this.timeout);
19845     
19846         this.hoverState = 'out';
19847     
19848         if (!this.delay || !this.delay.hide) {
19849             this.hide();
19850             return;
19851         }
19852         var _t = this;
19853         this.timeout = setTimeout(function () {
19854             if (_t.hoverState == 'out') {
19855                 _t.hide();
19856             }
19857         }, this.delay.hide)
19858     },
19859     /**
19860      * Show the popover
19861      * @param {Roo.Element|string|false} - element to align and point to.
19862      */
19863     show : function (on_el)
19864     {
19865         
19866         on_el = on_el || false; // default to false
19867         if (!on_el) {
19868             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19869                 on_el = this.parent().el;
19870             } else if (this.over) {
19871                 Roo.get(this.over);
19872             }
19873             
19874         }
19875         
19876         if (!this.el) {
19877             this.render(document.body);
19878         }
19879         
19880         
19881         this.el.removeClass([
19882             'fade','top','bottom', 'left', 'right','in',
19883             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19884         ]);
19885         
19886         if (this.title === false) {
19887             this.headerEl.hide();
19888         }
19889         
19890         
19891         var placement = typeof this.placement == 'function' ?
19892             this.placement.call(this, this.el, on_el) :
19893             this.placement;
19894             
19895         /*
19896         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19897         
19898         // I think  'auto right' - but 
19899         
19900         var autoPlace = autoToken.test(placement);
19901         if (autoPlace) {
19902             placement = placement.replace(autoToken, '') || 'top';
19903         }
19904         */
19905         
19906         
19907         this.el.show();
19908         this.el.dom.style.display='block';
19909         
19910         //this.el.appendTo(on_el);
19911         
19912         var p = this.getPosition();
19913         var box = this.el.getBox();
19914         
19915         
19916         var align = Roo.bootstrap.Popover.alignment[placement];
19917         this.el.addClass(align[2]);
19918
19919 //        Roo.log(align);
19920
19921         if (on_el) {
19922             this.el.alignTo(on_el, align[0],align[1]);
19923         } else {
19924             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19925             var es = this.el.getSize();
19926             var x = Roo.lib.Dom.getViewWidth()/2;
19927             var y = Roo.lib.Dom.getViewHeight()/2;
19928             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19929             
19930         }
19931
19932         
19933         //var arrow = this.el.select('.arrow',true).first();
19934         //arrow.set(align[2], 
19935         
19936         this.el.addClass('in');
19937         
19938         
19939         if (this.el.hasClass('fade')) {
19940             // fade it?
19941         }
19942         
19943         this.hoverState = 'in';
19944         
19945         if (this.modal) {
19946             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19947             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19948             this.maskEl.dom.style.display = 'block';
19949             this.maskEl.addClass('show');
19950         }
19951         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19952
19953         
19954         
19955         this.fireEvent('show', this);
19956         
19957     },
19958     hide : function()
19959     {
19960         this.el.setXY([0,0]);
19961         this.el.removeClass('in');
19962         this.el.hide();
19963         this.hoverState = null;
19964         this.maskEl.hide(); // always..
19965         this.fireEvent('hide', this);
19966     }
19967     
19968 });
19969
19970
19971 Roo.apply(Roo.bootstrap.Popover, {
19972
19973     alignment : {
19974         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19975         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19976         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19977         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19978     },
19979     
19980     zIndex : 20001,
19981
19982     clickHander : false,
19983     
19984
19985     onMouseDown : function(e)
19986     {
19987         if (!e.getTarget(".roo-popover")) {
19988             this.hideAll();
19989         }
19990          
19991     },
19992     
19993     popups : [],
19994     
19995     register : function(popup)
19996     {
19997         if (!Roo.bootstrap.Popover.clickHandler) {
19998             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19999         }
20000         // hide other popups.
20001         this.hideAll();
20002         this.popups.push(popup);
20003     },
20004     hideAll : function()
20005     {
20006         this.popups.forEach(function(p) {
20007             p.hide();
20008         });
20009     }
20010
20011 });/*
20012  * - LGPL
20013  *
20014  * Card header - holder for the card header elements.
20015  * 
20016  */
20017
20018 /**
20019  * @class Roo.bootstrap.PopoverNav
20020  * @extends Roo.bootstrap.NavGroup
20021  * Bootstrap Popover header navigation class
20022  * @constructor
20023  * Create a new Popover Header Navigation 
20024  * @param {Object} config The config object
20025  */
20026
20027 Roo.bootstrap.PopoverNav = function(config){
20028     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20029 };
20030
20031 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20032     
20033     
20034     container_method : 'getPopoverHeader' 
20035     
20036      
20037     
20038     
20039    
20040 });
20041
20042  
20043
20044  /*
20045  * - LGPL
20046  *
20047  * Progress
20048  * 
20049  */
20050
20051 /**
20052  * @class Roo.bootstrap.Progress
20053  * @extends Roo.bootstrap.Component
20054  * Bootstrap Progress class
20055  * @cfg {Boolean} striped striped of the progress bar
20056  * @cfg {Boolean} active animated of the progress bar
20057  * 
20058  * 
20059  * @constructor
20060  * Create a new Progress
20061  * @param {Object} config The config object
20062  */
20063
20064 Roo.bootstrap.Progress = function(config){
20065     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20066 };
20067
20068 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20069     
20070     striped : false,
20071     active: false,
20072     
20073     getAutoCreate : function(){
20074         var cfg = {
20075             tag: 'div',
20076             cls: 'progress'
20077         };
20078         
20079         
20080         if(this.striped){
20081             cfg.cls += ' progress-striped';
20082         }
20083       
20084         if(this.active){
20085             cfg.cls += ' active';
20086         }
20087         
20088         
20089         return cfg;
20090     }
20091    
20092 });
20093
20094  
20095
20096  /*
20097  * - LGPL
20098  *
20099  * ProgressBar
20100  * 
20101  */
20102
20103 /**
20104  * @class Roo.bootstrap.ProgressBar
20105  * @extends Roo.bootstrap.Component
20106  * Bootstrap ProgressBar class
20107  * @cfg {Number} aria_valuenow aria-value now
20108  * @cfg {Number} aria_valuemin aria-value min
20109  * @cfg {Number} aria_valuemax aria-value max
20110  * @cfg {String} label label for the progress bar
20111  * @cfg {String} panel (success | info | warning | danger )
20112  * @cfg {String} role role of the progress bar
20113  * @cfg {String} sr_only text
20114  * 
20115  * 
20116  * @constructor
20117  * Create a new ProgressBar
20118  * @param {Object} config The config object
20119  */
20120
20121 Roo.bootstrap.ProgressBar = function(config){
20122     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20123 };
20124
20125 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20126     
20127     aria_valuenow : 0,
20128     aria_valuemin : 0,
20129     aria_valuemax : 100,
20130     label : false,
20131     panel : false,
20132     role : false,
20133     sr_only: false,
20134     
20135     getAutoCreate : function()
20136     {
20137         
20138         var cfg = {
20139             tag: 'div',
20140             cls: 'progress-bar',
20141             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20142         };
20143         
20144         if(this.sr_only){
20145             cfg.cn = {
20146                 tag: 'span',
20147                 cls: 'sr-only',
20148                 html: this.sr_only
20149             }
20150         }
20151         
20152         if(this.role){
20153             cfg.role = this.role;
20154         }
20155         
20156         if(this.aria_valuenow){
20157             cfg['aria-valuenow'] = this.aria_valuenow;
20158         }
20159         
20160         if(this.aria_valuemin){
20161             cfg['aria-valuemin'] = this.aria_valuemin;
20162         }
20163         
20164         if(this.aria_valuemax){
20165             cfg['aria-valuemax'] = this.aria_valuemax;
20166         }
20167         
20168         if(this.label && !this.sr_only){
20169             cfg.html = this.label;
20170         }
20171         
20172         if(this.panel){
20173             cfg.cls += ' progress-bar-' + this.panel;
20174         }
20175         
20176         return cfg;
20177     },
20178     
20179     update : function(aria_valuenow)
20180     {
20181         this.aria_valuenow = aria_valuenow;
20182         
20183         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20184     }
20185    
20186 });
20187
20188  
20189
20190  /*
20191  * - LGPL
20192  *
20193  * column
20194  * 
20195  */
20196
20197 /**
20198  * @class Roo.bootstrap.TabGroup
20199  * @extends Roo.bootstrap.Column
20200  * Bootstrap Column class
20201  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20202  * @cfg {Boolean} carousel true to make the group behave like a carousel
20203  * @cfg {Boolean} bullets show bullets for the panels
20204  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20205  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20206  * @cfg {Boolean} showarrow (true|false) show arrow default true
20207  * 
20208  * @constructor
20209  * Create a new TabGroup
20210  * @param {Object} config The config object
20211  */
20212
20213 Roo.bootstrap.TabGroup = function(config){
20214     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20215     if (!this.navId) {
20216         this.navId = Roo.id();
20217     }
20218     this.tabs = [];
20219     Roo.bootstrap.TabGroup.register(this);
20220     
20221 };
20222
20223 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20224     
20225     carousel : false,
20226     transition : false,
20227     bullets : 0,
20228     timer : 0,
20229     autoslide : false,
20230     slideFn : false,
20231     slideOnTouch : false,
20232     showarrow : true,
20233     
20234     getAutoCreate : function()
20235     {
20236         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20237         
20238         cfg.cls += ' tab-content';
20239         
20240         if (this.carousel) {
20241             cfg.cls += ' carousel slide';
20242             
20243             cfg.cn = [{
20244                cls : 'carousel-inner',
20245                cn : []
20246             }];
20247         
20248             if(this.bullets  && !Roo.isTouch){
20249                 
20250                 var bullets = {
20251                     cls : 'carousel-bullets',
20252                     cn : []
20253                 };
20254                
20255                 if(this.bullets_cls){
20256                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20257                 }
20258                 
20259                 bullets.cn.push({
20260                     cls : 'clear'
20261                 });
20262                 
20263                 cfg.cn[0].cn.push(bullets);
20264             }
20265             
20266             if(this.showarrow){
20267                 cfg.cn[0].cn.push({
20268                     tag : 'div',
20269                     class : 'carousel-arrow',
20270                     cn : [
20271                         {
20272                             tag : 'div',
20273                             class : 'carousel-prev',
20274                             cn : [
20275                                 {
20276                                     tag : 'i',
20277                                     class : 'fa fa-chevron-left'
20278                                 }
20279                             ]
20280                         },
20281                         {
20282                             tag : 'div',
20283                             class : 'carousel-next',
20284                             cn : [
20285                                 {
20286                                     tag : 'i',
20287                                     class : 'fa fa-chevron-right'
20288                                 }
20289                             ]
20290                         }
20291                     ]
20292                 });
20293             }
20294             
20295         }
20296         
20297         return cfg;
20298     },
20299     
20300     initEvents:  function()
20301     {
20302 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20303 //            this.el.on("touchstart", this.onTouchStart, this);
20304 //        }
20305         
20306         if(this.autoslide){
20307             var _this = this;
20308             
20309             this.slideFn = window.setInterval(function() {
20310                 _this.showPanelNext();
20311             }, this.timer);
20312         }
20313         
20314         if(this.showarrow){
20315             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20316             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20317         }
20318         
20319         
20320     },
20321     
20322 //    onTouchStart : function(e, el, o)
20323 //    {
20324 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20325 //            return;
20326 //        }
20327 //        
20328 //        this.showPanelNext();
20329 //    },
20330     
20331     
20332     getChildContainer : function()
20333     {
20334         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20335     },
20336     
20337     /**
20338     * register a Navigation item
20339     * @param {Roo.bootstrap.NavItem} the navitem to add
20340     */
20341     register : function(item)
20342     {
20343         this.tabs.push( item);
20344         item.navId = this.navId; // not really needed..
20345         this.addBullet();
20346     
20347     },
20348     
20349     getActivePanel : function()
20350     {
20351         var r = false;
20352         Roo.each(this.tabs, function(t) {
20353             if (t.active) {
20354                 r = t;
20355                 return false;
20356             }
20357             return null;
20358         });
20359         return r;
20360         
20361     },
20362     getPanelByName : function(n)
20363     {
20364         var r = false;
20365         Roo.each(this.tabs, function(t) {
20366             if (t.tabId == n) {
20367                 r = t;
20368                 return false;
20369             }
20370             return null;
20371         });
20372         return r;
20373     },
20374     indexOfPanel : function(p)
20375     {
20376         var r = false;
20377         Roo.each(this.tabs, function(t,i) {
20378             if (t.tabId == p.tabId) {
20379                 r = i;
20380                 return false;
20381             }
20382             return null;
20383         });
20384         return r;
20385     },
20386     /**
20387      * show a specific panel
20388      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20389      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20390      */
20391     showPanel : function (pan)
20392     {
20393         if(this.transition || typeof(pan) == 'undefined'){
20394             Roo.log("waiting for the transitionend");
20395             return false;
20396         }
20397         
20398         if (typeof(pan) == 'number') {
20399             pan = this.tabs[pan];
20400         }
20401         
20402         if (typeof(pan) == 'string') {
20403             pan = this.getPanelByName(pan);
20404         }
20405         
20406         var cur = this.getActivePanel();
20407         
20408         if(!pan || !cur){
20409             Roo.log('pan or acitve pan is undefined');
20410             return false;
20411         }
20412         
20413         if (pan.tabId == this.getActivePanel().tabId) {
20414             return true;
20415         }
20416         
20417         if (false === cur.fireEvent('beforedeactivate')) {
20418             return false;
20419         }
20420         
20421         if(this.bullets > 0 && !Roo.isTouch){
20422             this.setActiveBullet(this.indexOfPanel(pan));
20423         }
20424         
20425         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20426             
20427             //class="carousel-item carousel-item-next carousel-item-left"
20428             
20429             this.transition = true;
20430             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20431             var lr = dir == 'next' ? 'left' : 'right';
20432             pan.el.addClass(dir); // or prev
20433             pan.el.addClass('carousel-item-' + dir); // or prev
20434             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20435             cur.el.addClass(lr); // or right
20436             pan.el.addClass(lr);
20437             cur.el.addClass('carousel-item-' +lr); // or right
20438             pan.el.addClass('carousel-item-' +lr);
20439             
20440             
20441             var _this = this;
20442             cur.el.on('transitionend', function() {
20443                 Roo.log("trans end?");
20444                 
20445                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20446                 pan.setActive(true);
20447                 
20448                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20449                 cur.setActive(false);
20450                 
20451                 _this.transition = false;
20452                 
20453             }, this, { single:  true } );
20454             
20455             return true;
20456         }
20457         
20458         cur.setActive(false);
20459         pan.setActive(true);
20460         
20461         return true;
20462         
20463     },
20464     showPanelNext : function()
20465     {
20466         var i = this.indexOfPanel(this.getActivePanel());
20467         
20468         if (i >= this.tabs.length - 1 && !this.autoslide) {
20469             return;
20470         }
20471         
20472         if (i >= this.tabs.length - 1 && this.autoslide) {
20473             i = -1;
20474         }
20475         
20476         this.showPanel(this.tabs[i+1]);
20477     },
20478     
20479     showPanelPrev : function()
20480     {
20481         var i = this.indexOfPanel(this.getActivePanel());
20482         
20483         if (i  < 1 && !this.autoslide) {
20484             return;
20485         }
20486         
20487         if (i < 1 && this.autoslide) {
20488             i = this.tabs.length;
20489         }
20490         
20491         this.showPanel(this.tabs[i-1]);
20492     },
20493     
20494     
20495     addBullet: function()
20496     {
20497         if(!this.bullets || Roo.isTouch){
20498             return;
20499         }
20500         var ctr = this.el.select('.carousel-bullets',true).first();
20501         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20502         var bullet = ctr.createChild({
20503             cls : 'bullet bullet-' + i
20504         },ctr.dom.lastChild);
20505         
20506         
20507         var _this = this;
20508         
20509         bullet.on('click', (function(e, el, o, ii, t){
20510
20511             e.preventDefault();
20512
20513             this.showPanel(ii);
20514
20515             if(this.autoslide && this.slideFn){
20516                 clearInterval(this.slideFn);
20517                 this.slideFn = window.setInterval(function() {
20518                     _this.showPanelNext();
20519                 }, this.timer);
20520             }
20521
20522         }).createDelegate(this, [i, bullet], true));
20523                 
20524         
20525     },
20526      
20527     setActiveBullet : function(i)
20528     {
20529         if(Roo.isTouch){
20530             return;
20531         }
20532         
20533         Roo.each(this.el.select('.bullet', true).elements, function(el){
20534             el.removeClass('selected');
20535         });
20536
20537         var bullet = this.el.select('.bullet-' + i, true).first();
20538         
20539         if(!bullet){
20540             return;
20541         }
20542         
20543         bullet.addClass('selected');
20544     }
20545     
20546     
20547   
20548 });
20549
20550  
20551
20552  
20553  
20554 Roo.apply(Roo.bootstrap.TabGroup, {
20555     
20556     groups: {},
20557      /**
20558     * register a Navigation Group
20559     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20560     */
20561     register : function(navgrp)
20562     {
20563         this.groups[navgrp.navId] = navgrp;
20564         
20565     },
20566     /**
20567     * fetch a Navigation Group based on the navigation ID
20568     * if one does not exist , it will get created.
20569     * @param {string} the navgroup to add
20570     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20571     */
20572     get: function(navId) {
20573         if (typeof(this.groups[navId]) == 'undefined') {
20574             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20575         }
20576         return this.groups[navId] ;
20577     }
20578     
20579     
20580     
20581 });
20582
20583  /*
20584  * - LGPL
20585  *
20586  * TabPanel
20587  * 
20588  */
20589
20590 /**
20591  * @class Roo.bootstrap.TabPanel
20592  * @extends Roo.bootstrap.Component
20593  * Bootstrap TabPanel class
20594  * @cfg {Boolean} active panel active
20595  * @cfg {String} html panel content
20596  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20597  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20598  * @cfg {String} href click to link..
20599  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20600  * 
20601  * 
20602  * @constructor
20603  * Create a new TabPanel
20604  * @param {Object} config The config object
20605  */
20606
20607 Roo.bootstrap.TabPanel = function(config){
20608     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20609     this.addEvents({
20610         /**
20611              * @event changed
20612              * Fires when the active status changes
20613              * @param {Roo.bootstrap.TabPanel} this
20614              * @param {Boolean} state the new state
20615             
20616          */
20617         'changed': true,
20618         /**
20619              * @event beforedeactivate
20620              * Fires before a tab is de-activated - can be used to do validation on a form.
20621              * @param {Roo.bootstrap.TabPanel} this
20622              * @return {Boolean} false if there is an error
20623             
20624          */
20625         'beforedeactivate': true
20626      });
20627     
20628     this.tabId = this.tabId || Roo.id();
20629   
20630 };
20631
20632 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20633     
20634     active: false,
20635     html: false,
20636     tabId: false,
20637     navId : false,
20638     href : '',
20639     touchSlide : false,
20640     getAutoCreate : function(){
20641         
20642         
20643         var cfg = {
20644             tag: 'div',
20645             // item is needed for carousel - not sure if it has any effect otherwise
20646             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20647             html: this.html || ''
20648         };
20649         
20650         if(this.active){
20651             cfg.cls += ' active';
20652         }
20653         
20654         if(this.tabId){
20655             cfg.tabId = this.tabId;
20656         }
20657         
20658         
20659         
20660         return cfg;
20661     },
20662     
20663     initEvents:  function()
20664     {
20665         var p = this.parent();
20666         
20667         this.navId = this.navId || p.navId;
20668         
20669         if (typeof(this.navId) != 'undefined') {
20670             // not really needed.. but just in case.. parent should be a NavGroup.
20671             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20672             
20673             tg.register(this);
20674             
20675             var i = tg.tabs.length - 1;
20676             
20677             if(this.active && tg.bullets > 0 && i < tg.bullets){
20678                 tg.setActiveBullet(i);
20679             }
20680         }
20681         
20682         this.el.on('click', this.onClick, this);
20683         
20684         if(Roo.isTouch && this.touchSlide){
20685             this.el.on("touchstart", this.onTouchStart, this);
20686             this.el.on("touchmove", this.onTouchMove, this);
20687             this.el.on("touchend", this.onTouchEnd, this);
20688         }
20689         
20690     },
20691     
20692     onRender : function(ct, position)
20693     {
20694         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20695     },
20696     
20697     setActive : function(state)
20698     {
20699         Roo.log("panel - set active " + this.tabId + "=" + state);
20700         
20701         this.active = state;
20702         if (!state) {
20703             this.el.removeClass('active');
20704             
20705         } else  if (!this.el.hasClass('active')) {
20706             this.el.addClass('active');
20707         }
20708         
20709         this.fireEvent('changed', this, state);
20710     },
20711     
20712     onClick : function(e)
20713     {
20714         e.preventDefault();
20715         
20716         if(!this.href.length){
20717             return;
20718         }
20719         
20720         window.location.href = this.href;
20721     },
20722     
20723     startX : 0,
20724     startY : 0,
20725     endX : 0,
20726     endY : 0,
20727     swiping : false,
20728     
20729     onTouchStart : function(e)
20730     {
20731         this.swiping = false;
20732         
20733         this.startX = e.browserEvent.touches[0].clientX;
20734         this.startY = e.browserEvent.touches[0].clientY;
20735     },
20736     
20737     onTouchMove : function(e)
20738     {
20739         this.swiping = true;
20740         
20741         this.endX = e.browserEvent.touches[0].clientX;
20742         this.endY = e.browserEvent.touches[0].clientY;
20743     },
20744     
20745     onTouchEnd : function(e)
20746     {
20747         if(!this.swiping){
20748             this.onClick(e);
20749             return;
20750         }
20751         
20752         var tabGroup = this.parent();
20753         
20754         if(this.endX > this.startX){ // swiping right
20755             tabGroup.showPanelPrev();
20756             return;
20757         }
20758         
20759         if(this.startX > this.endX){ // swiping left
20760             tabGroup.showPanelNext();
20761             return;
20762         }
20763     }
20764     
20765     
20766 });
20767  
20768
20769  
20770
20771  /*
20772  * - LGPL
20773  *
20774  * DateField
20775  * 
20776  */
20777
20778 /**
20779  * @class Roo.bootstrap.DateField
20780  * @extends Roo.bootstrap.Input
20781  * Bootstrap DateField class
20782  * @cfg {Number} weekStart default 0
20783  * @cfg {String} viewMode default empty, (months|years)
20784  * @cfg {String} minViewMode default empty, (months|years)
20785  * @cfg {Number} startDate default -Infinity
20786  * @cfg {Number} endDate default Infinity
20787  * @cfg {Boolean} todayHighlight default false
20788  * @cfg {Boolean} todayBtn default false
20789  * @cfg {Boolean} calendarWeeks default false
20790  * @cfg {Object} daysOfWeekDisabled default empty
20791  * @cfg {Boolean} singleMode default false (true | false)
20792  * 
20793  * @cfg {Boolean} keyboardNavigation default true
20794  * @cfg {String} language default en
20795  * 
20796  * @constructor
20797  * Create a new DateField
20798  * @param {Object} config The config object
20799  */
20800
20801 Roo.bootstrap.DateField = function(config){
20802     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20803      this.addEvents({
20804             /**
20805              * @event show
20806              * Fires when this field show.
20807              * @param {Roo.bootstrap.DateField} this
20808              * @param {Mixed} date The date value
20809              */
20810             show : true,
20811             /**
20812              * @event show
20813              * Fires when this field hide.
20814              * @param {Roo.bootstrap.DateField} this
20815              * @param {Mixed} date The date value
20816              */
20817             hide : true,
20818             /**
20819              * @event select
20820              * Fires when select a date.
20821              * @param {Roo.bootstrap.DateField} this
20822              * @param {Mixed} date The date value
20823              */
20824             select : true,
20825             /**
20826              * @event beforeselect
20827              * Fires when before select a date.
20828              * @param {Roo.bootstrap.DateField} this
20829              * @param {Mixed} date The date value
20830              */
20831             beforeselect : true
20832         });
20833 };
20834
20835 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20836     
20837     /**
20838      * @cfg {String} format
20839      * The default date format string which can be overriden for localization support.  The format must be
20840      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20841      */
20842     format : "m/d/y",
20843     /**
20844      * @cfg {String} altFormats
20845      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20846      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20847      */
20848     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20849     
20850     weekStart : 0,
20851     
20852     viewMode : '',
20853     
20854     minViewMode : '',
20855     
20856     todayHighlight : false,
20857     
20858     todayBtn: false,
20859     
20860     language: 'en',
20861     
20862     keyboardNavigation: true,
20863     
20864     calendarWeeks: false,
20865     
20866     startDate: -Infinity,
20867     
20868     endDate: Infinity,
20869     
20870     daysOfWeekDisabled: [],
20871     
20872     _events: [],
20873     
20874     singleMode : false,
20875     
20876     UTCDate: function()
20877     {
20878         return new Date(Date.UTC.apply(Date, arguments));
20879     },
20880     
20881     UTCToday: function()
20882     {
20883         var today = new Date();
20884         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20885     },
20886     
20887     getDate: function() {
20888             var d = this.getUTCDate();
20889             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20890     },
20891     
20892     getUTCDate: function() {
20893             return this.date;
20894     },
20895     
20896     setDate: function(d) {
20897             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20898     },
20899     
20900     setUTCDate: function(d) {
20901             this.date = d;
20902             this.setValue(this.formatDate(this.date));
20903     },
20904         
20905     onRender: function(ct, position)
20906     {
20907         
20908         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20909         
20910         this.language = this.language || 'en';
20911         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20912         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20913         
20914         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20915         this.format = this.format || 'm/d/y';
20916         this.isInline = false;
20917         this.isInput = true;
20918         this.component = this.el.select('.add-on', true).first() || false;
20919         this.component = (this.component && this.component.length === 0) ? false : this.component;
20920         this.hasInput = this.component && this.inputEl().length;
20921         
20922         if (typeof(this.minViewMode === 'string')) {
20923             switch (this.minViewMode) {
20924                 case 'months':
20925                     this.minViewMode = 1;
20926                     break;
20927                 case 'years':
20928                     this.minViewMode = 2;
20929                     break;
20930                 default:
20931                     this.minViewMode = 0;
20932                     break;
20933             }
20934         }
20935         
20936         if (typeof(this.viewMode === 'string')) {
20937             switch (this.viewMode) {
20938                 case 'months':
20939                     this.viewMode = 1;
20940                     break;
20941                 case 'years':
20942                     this.viewMode = 2;
20943                     break;
20944                 default:
20945                     this.viewMode = 0;
20946                     break;
20947             }
20948         }
20949                 
20950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20951         
20952 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20953         
20954         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20955         
20956         this.picker().on('mousedown', this.onMousedown, this);
20957         this.picker().on('click', this.onClick, this);
20958         
20959         this.picker().addClass('datepicker-dropdown');
20960         
20961         this.startViewMode = this.viewMode;
20962         
20963         if(this.singleMode){
20964             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20965                 v.setVisibilityMode(Roo.Element.DISPLAY);
20966                 v.hide();
20967             });
20968             
20969             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20970                 v.setStyle('width', '189px');
20971             });
20972         }
20973         
20974         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20975             if(!this.calendarWeeks){
20976                 v.remove();
20977                 return;
20978             }
20979             
20980             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20981             v.attr('colspan', function(i, val){
20982                 return parseInt(val) + 1;
20983             });
20984         });
20985                         
20986         
20987         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20988         
20989         this.setStartDate(this.startDate);
20990         this.setEndDate(this.endDate);
20991         
20992         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20993         
20994         this.fillDow();
20995         this.fillMonths();
20996         this.update();
20997         this.showMode();
20998         
20999         if(this.isInline) {
21000             this.showPopup();
21001         }
21002     },
21003     
21004     picker : function()
21005     {
21006         return this.pickerEl;
21007 //        return this.el.select('.datepicker', true).first();
21008     },
21009     
21010     fillDow: function()
21011     {
21012         var dowCnt = this.weekStart;
21013         
21014         var dow = {
21015             tag: 'tr',
21016             cn: [
21017                 
21018             ]
21019         };
21020         
21021         if(this.calendarWeeks){
21022             dow.cn.push({
21023                 tag: 'th',
21024                 cls: 'cw',
21025                 html: '&nbsp;'
21026             })
21027         }
21028         
21029         while (dowCnt < this.weekStart + 7) {
21030             dow.cn.push({
21031                 tag: 'th',
21032                 cls: 'dow',
21033                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21034             });
21035         }
21036         
21037         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21038     },
21039     
21040     fillMonths: function()
21041     {    
21042         var i = 0;
21043         var months = this.picker().select('>.datepicker-months td', true).first();
21044         
21045         months.dom.innerHTML = '';
21046         
21047         while (i < 12) {
21048             var month = {
21049                 tag: 'span',
21050                 cls: 'month',
21051                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21052             };
21053             
21054             months.createChild(month);
21055         }
21056         
21057     },
21058     
21059     update: function()
21060     {
21061         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
21062         
21063         if (this.date < this.startDate) {
21064             this.viewDate = new Date(this.startDate);
21065         } else if (this.date > this.endDate) {
21066             this.viewDate = new Date(this.endDate);
21067         } else {
21068             this.viewDate = new Date(this.date);
21069         }
21070         
21071         this.fill();
21072     },
21073     
21074     fill: function() 
21075     {
21076         var d = new Date(this.viewDate),
21077                 year = d.getUTCFullYear(),
21078                 month = d.getUTCMonth(),
21079                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21080                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21081                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21082                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21083                 currentDate = this.date && this.date.valueOf(),
21084                 today = this.UTCToday();
21085         
21086         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21087         
21088 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21089         
21090 //        this.picker.select('>tfoot th.today').
21091 //                                              .text(dates[this.language].today)
21092 //                                              .toggle(this.todayBtn !== false);
21093     
21094         this.updateNavArrows();
21095         this.fillMonths();
21096                                                 
21097         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21098         
21099         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21100          
21101         prevMonth.setUTCDate(day);
21102         
21103         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21104         
21105         var nextMonth = new Date(prevMonth);
21106         
21107         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21108         
21109         nextMonth = nextMonth.valueOf();
21110         
21111         var fillMonths = false;
21112         
21113         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21114         
21115         while(prevMonth.valueOf() <= nextMonth) {
21116             var clsName = '';
21117             
21118             if (prevMonth.getUTCDay() === this.weekStart) {
21119                 if(fillMonths){
21120                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21121                 }
21122                     
21123                 fillMonths = {
21124                     tag: 'tr',
21125                     cn: []
21126                 };
21127                 
21128                 if(this.calendarWeeks){
21129                     // ISO 8601: First week contains first thursday.
21130                     // ISO also states week starts on Monday, but we can be more abstract here.
21131                     var
21132                     // Start of current week: based on weekstart/current date
21133                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21134                     // Thursday of this week
21135                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21136                     // First Thursday of year, year from thursday
21137                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21138                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21139                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21140                     
21141                     fillMonths.cn.push({
21142                         tag: 'td',
21143                         cls: 'cw',
21144                         html: calWeek
21145                     });
21146                 }
21147             }
21148             
21149             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21150                 clsName += ' old';
21151             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21152                 clsName += ' new';
21153             }
21154             if (this.todayHighlight &&
21155                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21156                 prevMonth.getUTCMonth() == today.getMonth() &&
21157                 prevMonth.getUTCDate() == today.getDate()) {
21158                 clsName += ' today';
21159             }
21160             
21161             if (currentDate && prevMonth.valueOf() === currentDate) {
21162                 clsName += ' active';
21163             }
21164             
21165             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21166                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21167                     clsName += ' disabled';
21168             }
21169             
21170             fillMonths.cn.push({
21171                 tag: 'td',
21172                 cls: 'day ' + clsName,
21173                 html: prevMonth.getDate()
21174             });
21175             
21176             prevMonth.setDate(prevMonth.getDate()+1);
21177         }
21178           
21179         var currentYear = this.date && this.date.getUTCFullYear();
21180         var currentMonth = this.date && this.date.getUTCMonth();
21181         
21182         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21183         
21184         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21185             v.removeClass('active');
21186             
21187             if(currentYear === year && k === currentMonth){
21188                 v.addClass('active');
21189             }
21190             
21191             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21192                 v.addClass('disabled');
21193             }
21194             
21195         });
21196         
21197         
21198         year = parseInt(year/10, 10) * 10;
21199         
21200         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21201         
21202         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21203         
21204         year -= 1;
21205         for (var i = -1; i < 11; i++) {
21206             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21207                 tag: 'span',
21208                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21209                 html: year
21210             });
21211             
21212             year += 1;
21213         }
21214     },
21215     
21216     showMode: function(dir) 
21217     {
21218         if (dir) {
21219             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21220         }
21221         
21222         Roo.each(this.picker().select('>div',true).elements, function(v){
21223             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21224             v.hide();
21225         });
21226         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21227     },
21228     
21229     place: function()
21230     {
21231         if(this.isInline) {
21232             return;
21233         }
21234         
21235         this.picker().removeClass(['bottom', 'top']);
21236         
21237         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21238             /*
21239              * place to the top of element!
21240              *
21241              */
21242             
21243             this.picker().addClass('top');
21244             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21245             
21246             return;
21247         }
21248         
21249         this.picker().addClass('bottom');
21250         
21251         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21252     },
21253     
21254     parseDate : function(value)
21255     {
21256         if(!value || value instanceof Date){
21257             return value;
21258         }
21259         var v = Date.parseDate(value, this.format);
21260         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21261             v = Date.parseDate(value, 'Y-m-d');
21262         }
21263         if(!v && this.altFormats){
21264             if(!this.altFormatsArray){
21265                 this.altFormatsArray = this.altFormats.split("|");
21266             }
21267             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21268                 v = Date.parseDate(value, this.altFormatsArray[i]);
21269             }
21270         }
21271         return v;
21272     },
21273     
21274     formatDate : function(date, fmt)
21275     {   
21276         return (!date || !(date instanceof Date)) ?
21277         date : date.dateFormat(fmt || this.format);
21278     },
21279     
21280     onFocus : function()
21281     {
21282         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21283         this.showPopup();
21284     },
21285     
21286     onBlur : function()
21287     {
21288         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21289         
21290         var d = this.inputEl().getValue();
21291         
21292         this.setValue(d);
21293                 
21294         this.hidePopup();
21295     },
21296     
21297     showPopup : function()
21298     {
21299         this.picker().show();
21300         this.update();
21301         this.place();
21302         
21303         this.fireEvent('showpopup', this, this.date);
21304     },
21305     
21306     hidePopup : function()
21307     {
21308         if(this.isInline) {
21309             return;
21310         }
21311         this.picker().hide();
21312         this.viewMode = this.startViewMode;
21313         this.showMode();
21314         
21315         this.fireEvent('hidepopup', this, this.date);
21316         
21317     },
21318     
21319     onMousedown: function(e)
21320     {
21321         e.stopPropagation();
21322         e.preventDefault();
21323     },
21324     
21325     keyup: function(e)
21326     {
21327         Roo.bootstrap.DateField.superclass.keyup.call(this);
21328         this.update();
21329     },
21330
21331     setValue: function(v)
21332     {
21333         if(this.fireEvent('beforeselect', this, v) !== false){
21334             var d = new Date(this.parseDate(v) ).clearTime();
21335         
21336             if(isNaN(d.getTime())){
21337                 this.date = this.viewDate = '';
21338                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21339                 return;
21340             }
21341
21342             v = this.formatDate(d);
21343
21344             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21345
21346             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21347
21348             this.update();
21349
21350             this.fireEvent('select', this, this.date);
21351         }
21352     },
21353     
21354     getValue: function()
21355     {
21356         return this.formatDate(this.date);
21357     },
21358     
21359     fireKey: function(e)
21360     {
21361         if (!this.picker().isVisible()){
21362             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21363                 this.showPopup();
21364             }
21365             return;
21366         }
21367         
21368         var dateChanged = false,
21369         dir, day, month,
21370         newDate, newViewDate;
21371         
21372         switch(e.keyCode){
21373             case 27: // escape
21374                 this.hidePopup();
21375                 e.preventDefault();
21376                 break;
21377             case 37: // left
21378             case 39: // right
21379                 if (!this.keyboardNavigation) {
21380                     break;
21381                 }
21382                 dir = e.keyCode == 37 ? -1 : 1;
21383                 
21384                 if (e.ctrlKey){
21385                     newDate = this.moveYear(this.date, dir);
21386                     newViewDate = this.moveYear(this.viewDate, dir);
21387                 } else if (e.shiftKey){
21388                     newDate = this.moveMonth(this.date, dir);
21389                     newViewDate = this.moveMonth(this.viewDate, dir);
21390                 } else {
21391                     newDate = new Date(this.date);
21392                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21393                     newViewDate = new Date(this.viewDate);
21394                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21395                 }
21396                 if (this.dateWithinRange(newDate)){
21397                     this.date = newDate;
21398                     this.viewDate = newViewDate;
21399                     this.setValue(this.formatDate(this.date));
21400 //                    this.update();
21401                     e.preventDefault();
21402                     dateChanged = true;
21403                 }
21404                 break;
21405             case 38: // up
21406             case 40: // down
21407                 if (!this.keyboardNavigation) {
21408                     break;
21409                 }
21410                 dir = e.keyCode == 38 ? -1 : 1;
21411                 if (e.ctrlKey){
21412                     newDate = this.moveYear(this.date, dir);
21413                     newViewDate = this.moveYear(this.viewDate, dir);
21414                 } else if (e.shiftKey){
21415                     newDate = this.moveMonth(this.date, dir);
21416                     newViewDate = this.moveMonth(this.viewDate, dir);
21417                 } else {
21418                     newDate = new Date(this.date);
21419                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21420                     newViewDate = new Date(this.viewDate);
21421                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21422                 }
21423                 if (this.dateWithinRange(newDate)){
21424                     this.date = newDate;
21425                     this.viewDate = newViewDate;
21426                     this.setValue(this.formatDate(this.date));
21427 //                    this.update();
21428                     e.preventDefault();
21429                     dateChanged = true;
21430                 }
21431                 break;
21432             case 13: // enter
21433                 this.setValue(this.formatDate(this.date));
21434                 this.hidePopup();
21435                 e.preventDefault();
21436                 break;
21437             case 9: // tab
21438                 this.setValue(this.formatDate(this.date));
21439                 this.hidePopup();
21440                 break;
21441             case 16: // shift
21442             case 17: // ctrl
21443             case 18: // alt
21444                 break;
21445             default :
21446                 this.hidePopup();
21447                 
21448         }
21449     },
21450     
21451     
21452     onClick: function(e) 
21453     {
21454         e.stopPropagation();
21455         e.preventDefault();
21456         
21457         var target = e.getTarget();
21458         
21459         if(target.nodeName.toLowerCase() === 'i'){
21460             target = Roo.get(target).dom.parentNode;
21461         }
21462         
21463         var nodeName = target.nodeName;
21464         var className = target.className;
21465         var html = target.innerHTML;
21466         //Roo.log(nodeName);
21467         
21468         switch(nodeName.toLowerCase()) {
21469             case 'th':
21470                 switch(className) {
21471                     case 'switch':
21472                         this.showMode(1);
21473                         break;
21474                     case 'prev':
21475                     case 'next':
21476                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21477                         switch(this.viewMode){
21478                                 case 0:
21479                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21480                                         break;
21481                                 case 1:
21482                                 case 2:
21483                                         this.viewDate = this.moveYear(this.viewDate, dir);
21484                                         break;
21485                         }
21486                         this.fill();
21487                         break;
21488                     case 'today':
21489                         var date = new Date();
21490                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21491 //                        this.fill()
21492                         this.setValue(this.formatDate(this.date));
21493                         
21494                         this.hidePopup();
21495                         break;
21496                 }
21497                 break;
21498             case 'span':
21499                 if (className.indexOf('disabled') < 0) {
21500                     this.viewDate.setUTCDate(1);
21501                     if (className.indexOf('month') > -1) {
21502                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21503                     } else {
21504                         var year = parseInt(html, 10) || 0;
21505                         this.viewDate.setUTCFullYear(year);
21506                         
21507                     }
21508                     
21509                     if(this.singleMode){
21510                         this.setValue(this.formatDate(this.viewDate));
21511                         this.hidePopup();
21512                         return;
21513                     }
21514                     
21515                     this.showMode(-1);
21516                     this.fill();
21517                 }
21518                 break;
21519                 
21520             case 'td':
21521                 //Roo.log(className);
21522                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21523                     var day = parseInt(html, 10) || 1;
21524                     var year = this.viewDate.getUTCFullYear(),
21525                         month = this.viewDate.getUTCMonth();
21526
21527                     if (className.indexOf('old') > -1) {
21528                         if(month === 0 ){
21529                             month = 11;
21530                             year -= 1;
21531                         }else{
21532                             month -= 1;
21533                         }
21534                     } else if (className.indexOf('new') > -1) {
21535                         if (month == 11) {
21536                             month = 0;
21537                             year += 1;
21538                         } else {
21539                             month += 1;
21540                         }
21541                     }
21542                     //Roo.log([year,month,day]);
21543                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21544                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21545 //                    this.fill();
21546                     //Roo.log(this.formatDate(this.date));
21547                     this.setValue(this.formatDate(this.date));
21548                     this.hidePopup();
21549                 }
21550                 break;
21551         }
21552     },
21553     
21554     setStartDate: function(startDate)
21555     {
21556         this.startDate = startDate || -Infinity;
21557         if (this.startDate !== -Infinity) {
21558             this.startDate = this.parseDate(this.startDate);
21559         }
21560         this.update();
21561         this.updateNavArrows();
21562     },
21563
21564     setEndDate: function(endDate)
21565     {
21566         this.endDate = endDate || Infinity;
21567         if (this.endDate !== Infinity) {
21568             this.endDate = this.parseDate(this.endDate);
21569         }
21570         this.update();
21571         this.updateNavArrows();
21572     },
21573     
21574     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21575     {
21576         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21577         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21578             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21579         }
21580         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21581             return parseInt(d, 10);
21582         });
21583         this.update();
21584         this.updateNavArrows();
21585     },
21586     
21587     updateNavArrows: function() 
21588     {
21589         if(this.singleMode){
21590             return;
21591         }
21592         
21593         var d = new Date(this.viewDate),
21594         year = d.getUTCFullYear(),
21595         month = d.getUTCMonth();
21596         
21597         Roo.each(this.picker().select('.prev', true).elements, function(v){
21598             v.show();
21599             switch (this.viewMode) {
21600                 case 0:
21601
21602                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21603                         v.hide();
21604                     }
21605                     break;
21606                 case 1:
21607                 case 2:
21608                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21609                         v.hide();
21610                     }
21611                     break;
21612             }
21613         });
21614         
21615         Roo.each(this.picker().select('.next', true).elements, function(v){
21616             v.show();
21617             switch (this.viewMode) {
21618                 case 0:
21619
21620                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21621                         v.hide();
21622                     }
21623                     break;
21624                 case 1:
21625                 case 2:
21626                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21627                         v.hide();
21628                     }
21629                     break;
21630             }
21631         })
21632     },
21633     
21634     moveMonth: function(date, dir)
21635     {
21636         if (!dir) {
21637             return date;
21638         }
21639         var new_date = new Date(date.valueOf()),
21640         day = new_date.getUTCDate(),
21641         month = new_date.getUTCMonth(),
21642         mag = Math.abs(dir),
21643         new_month, test;
21644         dir = dir > 0 ? 1 : -1;
21645         if (mag == 1){
21646             test = dir == -1
21647             // If going back one month, make sure month is not current month
21648             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21649             ? function(){
21650                 return new_date.getUTCMonth() == month;
21651             }
21652             // If going forward one month, make sure month is as expected
21653             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21654             : function(){
21655                 return new_date.getUTCMonth() != new_month;
21656             };
21657             new_month = month + dir;
21658             new_date.setUTCMonth(new_month);
21659             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21660             if (new_month < 0 || new_month > 11) {
21661                 new_month = (new_month + 12) % 12;
21662             }
21663         } else {
21664             // For magnitudes >1, move one month at a time...
21665             for (var i=0; i<mag; i++) {
21666                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21667                 new_date = this.moveMonth(new_date, dir);
21668             }
21669             // ...then reset the day, keeping it in the new month
21670             new_month = new_date.getUTCMonth();
21671             new_date.setUTCDate(day);
21672             test = function(){
21673                 return new_month != new_date.getUTCMonth();
21674             };
21675         }
21676         // Common date-resetting loop -- if date is beyond end of month, make it
21677         // end of month
21678         while (test()){
21679             new_date.setUTCDate(--day);
21680             new_date.setUTCMonth(new_month);
21681         }
21682         return new_date;
21683     },
21684
21685     moveYear: function(date, dir)
21686     {
21687         return this.moveMonth(date, dir*12);
21688     },
21689
21690     dateWithinRange: function(date)
21691     {
21692         return date >= this.startDate && date <= this.endDate;
21693     },
21694
21695     
21696     remove: function() 
21697     {
21698         this.picker().remove();
21699     },
21700     
21701     validateValue : function(value)
21702     {
21703         if(this.getVisibilityEl().hasClass('hidden')){
21704             return true;
21705         }
21706         
21707         if(value.length < 1)  {
21708             if(this.allowBlank){
21709                 return true;
21710             }
21711             return false;
21712         }
21713         
21714         if(value.length < this.minLength){
21715             return false;
21716         }
21717         if(value.length > this.maxLength){
21718             return false;
21719         }
21720         if(this.vtype){
21721             var vt = Roo.form.VTypes;
21722             if(!vt[this.vtype](value, this)){
21723                 return false;
21724             }
21725         }
21726         if(typeof this.validator == "function"){
21727             var msg = this.validator(value);
21728             if(msg !== true){
21729                 return false;
21730             }
21731         }
21732         
21733         if(this.regex && !this.regex.test(value)){
21734             return false;
21735         }
21736         
21737         if(typeof(this.parseDate(value)) == 'undefined'){
21738             return false;
21739         }
21740         
21741         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21742             return false;
21743         }      
21744         
21745         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21746             return false;
21747         } 
21748         
21749         
21750         return true;
21751     },
21752     
21753     reset : function()
21754     {
21755         this.date = this.viewDate = '';
21756         
21757         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21758     }
21759    
21760 });
21761
21762 Roo.apply(Roo.bootstrap.DateField,  {
21763     
21764     head : {
21765         tag: 'thead',
21766         cn: [
21767         {
21768             tag: 'tr',
21769             cn: [
21770             {
21771                 tag: 'th',
21772                 cls: 'prev',
21773                 html: '<i class="fa fa-arrow-left"/>'
21774             },
21775             {
21776                 tag: 'th',
21777                 cls: 'switch',
21778                 colspan: '5'
21779             },
21780             {
21781                 tag: 'th',
21782                 cls: 'next',
21783                 html: '<i class="fa fa-arrow-right"/>'
21784             }
21785
21786             ]
21787         }
21788         ]
21789     },
21790     
21791     content : {
21792         tag: 'tbody',
21793         cn: [
21794         {
21795             tag: 'tr',
21796             cn: [
21797             {
21798                 tag: 'td',
21799                 colspan: '7'
21800             }
21801             ]
21802         }
21803         ]
21804     },
21805     
21806     footer : {
21807         tag: 'tfoot',
21808         cn: [
21809         {
21810             tag: 'tr',
21811             cn: [
21812             {
21813                 tag: 'th',
21814                 colspan: '7',
21815                 cls: 'today'
21816             }
21817                     
21818             ]
21819         }
21820         ]
21821     },
21822     
21823     dates:{
21824         en: {
21825             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21826             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21827             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21828             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21829             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21830             today: "Today"
21831         }
21832     },
21833     
21834     modes: [
21835     {
21836         clsName: 'days',
21837         navFnc: 'Month',
21838         navStep: 1
21839     },
21840     {
21841         clsName: 'months',
21842         navFnc: 'FullYear',
21843         navStep: 1
21844     },
21845     {
21846         clsName: 'years',
21847         navFnc: 'FullYear',
21848         navStep: 10
21849     }]
21850 });
21851
21852 Roo.apply(Roo.bootstrap.DateField,  {
21853   
21854     template : {
21855         tag: 'div',
21856         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21857         cn: [
21858         {
21859             tag: 'div',
21860             cls: 'datepicker-days',
21861             cn: [
21862             {
21863                 tag: 'table',
21864                 cls: 'table-condensed',
21865                 cn:[
21866                 Roo.bootstrap.DateField.head,
21867                 {
21868                     tag: 'tbody'
21869                 },
21870                 Roo.bootstrap.DateField.footer
21871                 ]
21872             }
21873             ]
21874         },
21875         {
21876             tag: 'div',
21877             cls: 'datepicker-months',
21878             cn: [
21879             {
21880                 tag: 'table',
21881                 cls: 'table-condensed',
21882                 cn:[
21883                 Roo.bootstrap.DateField.head,
21884                 Roo.bootstrap.DateField.content,
21885                 Roo.bootstrap.DateField.footer
21886                 ]
21887             }
21888             ]
21889         },
21890         {
21891             tag: 'div',
21892             cls: 'datepicker-years',
21893             cn: [
21894             {
21895                 tag: 'table',
21896                 cls: 'table-condensed',
21897                 cn:[
21898                 Roo.bootstrap.DateField.head,
21899                 Roo.bootstrap.DateField.content,
21900                 Roo.bootstrap.DateField.footer
21901                 ]
21902             }
21903             ]
21904         }
21905         ]
21906     }
21907 });
21908
21909  
21910
21911  /*
21912  * - LGPL
21913  *
21914  * TimeField
21915  * 
21916  */
21917
21918 /**
21919  * @class Roo.bootstrap.TimeField
21920  * @extends Roo.bootstrap.Input
21921  * Bootstrap DateField class
21922  * 
21923  * 
21924  * @constructor
21925  * Create a new TimeField
21926  * @param {Object} config The config object
21927  */
21928
21929 Roo.bootstrap.TimeField = function(config){
21930     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21931     this.addEvents({
21932             /**
21933              * @event show
21934              * Fires when this field show.
21935              * @param {Roo.bootstrap.DateField} thisthis
21936              * @param {Mixed} date The date value
21937              */
21938             show : true,
21939             /**
21940              * @event show
21941              * Fires when this field hide.
21942              * @param {Roo.bootstrap.DateField} this
21943              * @param {Mixed} date The date value
21944              */
21945             hide : true,
21946             /**
21947              * @event select
21948              * Fires when select a date.
21949              * @param {Roo.bootstrap.DateField} this
21950              * @param {Mixed} date The date value
21951              */
21952             select : true
21953         });
21954 };
21955
21956 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21957     
21958     /**
21959      * @cfg {String} format
21960      * The default time format string which can be overriden for localization support.  The format must be
21961      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21962      */
21963     format : "H:i",
21964        
21965     onRender: function(ct, position)
21966     {
21967         
21968         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21969                 
21970         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21971         
21972         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21973         
21974         this.pop = this.picker().select('>.datepicker-time',true).first();
21975         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21976         
21977         this.picker().on('mousedown', this.onMousedown, this);
21978         this.picker().on('click', this.onClick, this);
21979         
21980         this.picker().addClass('datepicker-dropdown');
21981     
21982         this.fillTime();
21983         this.update();
21984             
21985         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21986         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21987         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21988         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21989         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21990         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21991
21992     },
21993     
21994     fireKey: function(e){
21995         if (!this.picker().isVisible()){
21996             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21997                 this.show();
21998             }
21999             return;
22000         }
22001
22002         e.preventDefault();
22003         
22004         switch(e.keyCode){
22005             case 27: // escape
22006                 this.hide();
22007                 break;
22008             case 37: // left
22009             case 39: // right
22010                 this.onTogglePeriod();
22011                 break;
22012             case 38: // up
22013                 this.onIncrementMinutes();
22014                 break;
22015             case 40: // down
22016                 this.onDecrementMinutes();
22017                 break;
22018             case 13: // enter
22019             case 9: // tab
22020                 this.setTime();
22021                 break;
22022         }
22023     },
22024     
22025     onClick: function(e) {
22026         e.stopPropagation();
22027         e.preventDefault();
22028     },
22029     
22030     picker : function()
22031     {
22032         return this.el.select('.datepicker', true).first();
22033     },
22034     
22035     fillTime: function()
22036     {    
22037         var time = this.pop.select('tbody', true).first();
22038         
22039         time.dom.innerHTML = '';
22040         
22041         time.createChild({
22042             tag: 'tr',
22043             cn: [
22044                 {
22045                     tag: 'td',
22046                     cn: [
22047                         {
22048                             tag: 'a',
22049                             href: '#',
22050                             cls: 'btn',
22051                             cn: [
22052                                 {
22053                                     tag: 'span',
22054                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22055                                 }
22056                             ]
22057                         } 
22058                     ]
22059                 },
22060                 {
22061                     tag: 'td',
22062                     cls: 'separator'
22063                 },
22064                 {
22065                     tag: 'td',
22066                     cn: [
22067                         {
22068                             tag: 'a',
22069                             href: '#',
22070                             cls: 'btn',
22071                             cn: [
22072                                 {
22073                                     tag: 'span',
22074                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22075                                 }
22076                             ]
22077                         }
22078                     ]
22079                 },
22080                 {
22081                     tag: 'td',
22082                     cls: 'separator'
22083                 }
22084             ]
22085         });
22086         
22087         time.createChild({
22088             tag: 'tr',
22089             cn: [
22090                 {
22091                     tag: 'td',
22092                     cn: [
22093                         {
22094                             tag: 'span',
22095                             cls: 'timepicker-hour',
22096                             html: '00'
22097                         }  
22098                     ]
22099                 },
22100                 {
22101                     tag: 'td',
22102                     cls: 'separator',
22103                     html: ':'
22104                 },
22105                 {
22106                     tag: 'td',
22107                     cn: [
22108                         {
22109                             tag: 'span',
22110                             cls: 'timepicker-minute',
22111                             html: '00'
22112                         }  
22113                     ]
22114                 },
22115                 {
22116                     tag: 'td',
22117                     cls: 'separator'
22118                 },
22119                 {
22120                     tag: 'td',
22121                     cn: [
22122                         {
22123                             tag: 'button',
22124                             type: 'button',
22125                             cls: 'btn btn-primary period',
22126                             html: 'AM'
22127                             
22128                         }
22129                     ]
22130                 }
22131             ]
22132         });
22133         
22134         time.createChild({
22135             tag: 'tr',
22136             cn: [
22137                 {
22138                     tag: 'td',
22139                     cn: [
22140                         {
22141                             tag: 'a',
22142                             href: '#',
22143                             cls: 'btn',
22144                             cn: [
22145                                 {
22146                                     tag: 'span',
22147                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22148                                 }
22149                             ]
22150                         }
22151                     ]
22152                 },
22153                 {
22154                     tag: 'td',
22155                     cls: 'separator'
22156                 },
22157                 {
22158                     tag: 'td',
22159                     cn: [
22160                         {
22161                             tag: 'a',
22162                             href: '#',
22163                             cls: 'btn',
22164                             cn: [
22165                                 {
22166                                     tag: 'span',
22167                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22168                                 }
22169                             ]
22170                         }
22171                     ]
22172                 },
22173                 {
22174                     tag: 'td',
22175                     cls: 'separator'
22176                 }
22177             ]
22178         });
22179         
22180     },
22181     
22182     update: function()
22183     {
22184         
22185         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22186         
22187         this.fill();
22188     },
22189     
22190     fill: function() 
22191     {
22192         var hours = this.time.getHours();
22193         var minutes = this.time.getMinutes();
22194         var period = 'AM';
22195         
22196         if(hours > 11){
22197             period = 'PM';
22198         }
22199         
22200         if(hours == 0){
22201             hours = 12;
22202         }
22203         
22204         
22205         if(hours > 12){
22206             hours = hours - 12;
22207         }
22208         
22209         if(hours < 10){
22210             hours = '0' + hours;
22211         }
22212         
22213         if(minutes < 10){
22214             minutes = '0' + minutes;
22215         }
22216         
22217         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22218         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22219         this.pop.select('button', true).first().dom.innerHTML = period;
22220         
22221     },
22222     
22223     place: function()
22224     {   
22225         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22226         
22227         var cls = ['bottom'];
22228         
22229         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22230             cls.pop();
22231             cls.push('top');
22232         }
22233         
22234         cls.push('right');
22235         
22236         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22237             cls.pop();
22238             cls.push('left');
22239         }
22240         
22241         this.picker().addClass(cls.join('-'));
22242         
22243         var _this = this;
22244         
22245         Roo.each(cls, function(c){
22246             if(c == 'bottom'){
22247                 _this.picker().setTop(_this.inputEl().getHeight());
22248                 return;
22249             }
22250             if(c == 'top'){
22251                 _this.picker().setTop(0 - _this.picker().getHeight());
22252                 return;
22253             }
22254             
22255             if(c == 'left'){
22256                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22257                 return;
22258             }
22259             if(c == 'right'){
22260                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22261                 return;
22262             }
22263         });
22264         
22265     },
22266   
22267     onFocus : function()
22268     {
22269         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22270         this.show();
22271     },
22272     
22273     onBlur : function()
22274     {
22275         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22276         this.hide();
22277     },
22278     
22279     show : function()
22280     {
22281         this.picker().show();
22282         this.pop.show();
22283         this.update();
22284         this.place();
22285         
22286         this.fireEvent('show', this, this.date);
22287     },
22288     
22289     hide : function()
22290     {
22291         this.picker().hide();
22292         this.pop.hide();
22293         
22294         this.fireEvent('hide', this, this.date);
22295     },
22296     
22297     setTime : function()
22298     {
22299         this.hide();
22300         this.setValue(this.time.format(this.format));
22301         
22302         this.fireEvent('select', this, this.date);
22303         
22304         
22305     },
22306     
22307     onMousedown: function(e){
22308         e.stopPropagation();
22309         e.preventDefault();
22310     },
22311     
22312     onIncrementHours: function()
22313     {
22314         Roo.log('onIncrementHours');
22315         this.time = this.time.add(Date.HOUR, 1);
22316         this.update();
22317         
22318     },
22319     
22320     onDecrementHours: function()
22321     {
22322         Roo.log('onDecrementHours');
22323         this.time = this.time.add(Date.HOUR, -1);
22324         this.update();
22325     },
22326     
22327     onIncrementMinutes: function()
22328     {
22329         Roo.log('onIncrementMinutes');
22330         this.time = this.time.add(Date.MINUTE, 1);
22331         this.update();
22332     },
22333     
22334     onDecrementMinutes: function()
22335     {
22336         Roo.log('onDecrementMinutes');
22337         this.time = this.time.add(Date.MINUTE, -1);
22338         this.update();
22339     },
22340     
22341     onTogglePeriod: function()
22342     {
22343         Roo.log('onTogglePeriod');
22344         this.time = this.time.add(Date.HOUR, 12);
22345         this.update();
22346     }
22347     
22348    
22349 });
22350
22351 Roo.apply(Roo.bootstrap.TimeField,  {
22352     
22353     content : {
22354         tag: 'tbody',
22355         cn: [
22356             {
22357                 tag: 'tr',
22358                 cn: [
22359                 {
22360                     tag: 'td',
22361                     colspan: '7'
22362                 }
22363                 ]
22364             }
22365         ]
22366     },
22367     
22368     footer : {
22369         tag: 'tfoot',
22370         cn: [
22371             {
22372                 tag: 'tr',
22373                 cn: [
22374                 {
22375                     tag: 'th',
22376                     colspan: '7',
22377                     cls: '',
22378                     cn: [
22379                         {
22380                             tag: 'button',
22381                             cls: 'btn btn-info ok',
22382                             html: 'OK'
22383                         }
22384                     ]
22385                 }
22386
22387                 ]
22388             }
22389         ]
22390     }
22391 });
22392
22393 Roo.apply(Roo.bootstrap.TimeField,  {
22394   
22395     template : {
22396         tag: 'div',
22397         cls: 'datepicker dropdown-menu',
22398         cn: [
22399             {
22400                 tag: 'div',
22401                 cls: 'datepicker-time',
22402                 cn: [
22403                 {
22404                     tag: 'table',
22405                     cls: 'table-condensed',
22406                     cn:[
22407                     Roo.bootstrap.TimeField.content,
22408                     Roo.bootstrap.TimeField.footer
22409                     ]
22410                 }
22411                 ]
22412             }
22413         ]
22414     }
22415 });
22416
22417  
22418
22419  /*
22420  * - LGPL
22421  *
22422  * MonthField
22423  * 
22424  */
22425
22426 /**
22427  * @class Roo.bootstrap.MonthField
22428  * @extends Roo.bootstrap.Input
22429  * Bootstrap MonthField class
22430  * 
22431  * @cfg {String} language default en
22432  * 
22433  * @constructor
22434  * Create a new MonthField
22435  * @param {Object} config The config object
22436  */
22437
22438 Roo.bootstrap.MonthField = function(config){
22439     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22440     
22441     this.addEvents({
22442         /**
22443          * @event show
22444          * Fires when this field show.
22445          * @param {Roo.bootstrap.MonthField} this
22446          * @param {Mixed} date The date value
22447          */
22448         show : true,
22449         /**
22450          * @event show
22451          * Fires when this field hide.
22452          * @param {Roo.bootstrap.MonthField} this
22453          * @param {Mixed} date The date value
22454          */
22455         hide : true,
22456         /**
22457          * @event select
22458          * Fires when select a date.
22459          * @param {Roo.bootstrap.MonthField} this
22460          * @param {String} oldvalue The old value
22461          * @param {String} newvalue The new value
22462          */
22463         select : true
22464     });
22465 };
22466
22467 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22468     
22469     onRender: function(ct, position)
22470     {
22471         
22472         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22473         
22474         this.language = this.language || 'en';
22475         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22476         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22477         
22478         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22479         this.isInline = false;
22480         this.isInput = true;
22481         this.component = this.el.select('.add-on', true).first() || false;
22482         this.component = (this.component && this.component.length === 0) ? false : this.component;
22483         this.hasInput = this.component && this.inputEL().length;
22484         
22485         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22486         
22487         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22488         
22489         this.picker().on('mousedown', this.onMousedown, this);
22490         this.picker().on('click', this.onClick, this);
22491         
22492         this.picker().addClass('datepicker-dropdown');
22493         
22494         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22495             v.setStyle('width', '189px');
22496         });
22497         
22498         this.fillMonths();
22499         
22500         this.update();
22501         
22502         if(this.isInline) {
22503             this.show();
22504         }
22505         
22506     },
22507     
22508     setValue: function(v, suppressEvent)
22509     {   
22510         var o = this.getValue();
22511         
22512         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22513         
22514         this.update();
22515
22516         if(suppressEvent !== true){
22517             this.fireEvent('select', this, o, v);
22518         }
22519         
22520     },
22521     
22522     getValue: function()
22523     {
22524         return this.value;
22525     },
22526     
22527     onClick: function(e) 
22528     {
22529         e.stopPropagation();
22530         e.preventDefault();
22531         
22532         var target = e.getTarget();
22533         
22534         if(target.nodeName.toLowerCase() === 'i'){
22535             target = Roo.get(target).dom.parentNode;
22536         }
22537         
22538         var nodeName = target.nodeName;
22539         var className = target.className;
22540         var html = target.innerHTML;
22541         
22542         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22543             return;
22544         }
22545         
22546         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22547         
22548         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22549         
22550         this.hide();
22551                         
22552     },
22553     
22554     picker : function()
22555     {
22556         return this.pickerEl;
22557     },
22558     
22559     fillMonths: function()
22560     {    
22561         var i = 0;
22562         var months = this.picker().select('>.datepicker-months td', true).first();
22563         
22564         months.dom.innerHTML = '';
22565         
22566         while (i < 12) {
22567             var month = {
22568                 tag: 'span',
22569                 cls: 'month',
22570                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22571             };
22572             
22573             months.createChild(month);
22574         }
22575         
22576     },
22577     
22578     update: function()
22579     {
22580         var _this = this;
22581         
22582         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22583             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22584         }
22585         
22586         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22587             e.removeClass('active');
22588             
22589             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22590                 e.addClass('active');
22591             }
22592         })
22593     },
22594     
22595     place: function()
22596     {
22597         if(this.isInline) {
22598             return;
22599         }
22600         
22601         this.picker().removeClass(['bottom', 'top']);
22602         
22603         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22604             /*
22605              * place to the top of element!
22606              *
22607              */
22608             
22609             this.picker().addClass('top');
22610             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22611             
22612             return;
22613         }
22614         
22615         this.picker().addClass('bottom');
22616         
22617         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22618     },
22619     
22620     onFocus : function()
22621     {
22622         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22623         this.show();
22624     },
22625     
22626     onBlur : function()
22627     {
22628         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22629         
22630         var d = this.inputEl().getValue();
22631         
22632         this.setValue(d);
22633                 
22634         this.hide();
22635     },
22636     
22637     show : function()
22638     {
22639         this.picker().show();
22640         this.picker().select('>.datepicker-months', true).first().show();
22641         this.update();
22642         this.place();
22643         
22644         this.fireEvent('show', this, this.date);
22645     },
22646     
22647     hide : function()
22648     {
22649         if(this.isInline) {
22650             return;
22651         }
22652         this.picker().hide();
22653         this.fireEvent('hide', this, this.date);
22654         
22655     },
22656     
22657     onMousedown: function(e)
22658     {
22659         e.stopPropagation();
22660         e.preventDefault();
22661     },
22662     
22663     keyup: function(e)
22664     {
22665         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22666         this.update();
22667     },
22668
22669     fireKey: function(e)
22670     {
22671         if (!this.picker().isVisible()){
22672             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22673                 this.show();
22674             }
22675             return;
22676         }
22677         
22678         var dir;
22679         
22680         switch(e.keyCode){
22681             case 27: // escape
22682                 this.hide();
22683                 e.preventDefault();
22684                 break;
22685             case 37: // left
22686             case 39: // right
22687                 dir = e.keyCode == 37 ? -1 : 1;
22688                 
22689                 this.vIndex = this.vIndex + dir;
22690                 
22691                 if(this.vIndex < 0){
22692                     this.vIndex = 0;
22693                 }
22694                 
22695                 if(this.vIndex > 11){
22696                     this.vIndex = 11;
22697                 }
22698                 
22699                 if(isNaN(this.vIndex)){
22700                     this.vIndex = 0;
22701                 }
22702                 
22703                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22704                 
22705                 break;
22706             case 38: // up
22707             case 40: // down
22708                 
22709                 dir = e.keyCode == 38 ? -1 : 1;
22710                 
22711                 this.vIndex = this.vIndex + dir * 4;
22712                 
22713                 if(this.vIndex < 0){
22714                     this.vIndex = 0;
22715                 }
22716                 
22717                 if(this.vIndex > 11){
22718                     this.vIndex = 11;
22719                 }
22720                 
22721                 if(isNaN(this.vIndex)){
22722                     this.vIndex = 0;
22723                 }
22724                 
22725                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22726                 break;
22727                 
22728             case 13: // enter
22729                 
22730                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22731                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22732                 }
22733                 
22734                 this.hide();
22735                 e.preventDefault();
22736                 break;
22737             case 9: // tab
22738                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22739                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22740                 }
22741                 this.hide();
22742                 break;
22743             case 16: // shift
22744             case 17: // ctrl
22745             case 18: // alt
22746                 break;
22747             default :
22748                 this.hide();
22749                 
22750         }
22751     },
22752     
22753     remove: function() 
22754     {
22755         this.picker().remove();
22756     }
22757    
22758 });
22759
22760 Roo.apply(Roo.bootstrap.MonthField,  {
22761     
22762     content : {
22763         tag: 'tbody',
22764         cn: [
22765         {
22766             tag: 'tr',
22767             cn: [
22768             {
22769                 tag: 'td',
22770                 colspan: '7'
22771             }
22772             ]
22773         }
22774         ]
22775     },
22776     
22777     dates:{
22778         en: {
22779             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22780             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22781         }
22782     }
22783 });
22784
22785 Roo.apply(Roo.bootstrap.MonthField,  {
22786   
22787     template : {
22788         tag: 'div',
22789         cls: 'datepicker dropdown-menu roo-dynamic',
22790         cn: [
22791             {
22792                 tag: 'div',
22793                 cls: 'datepicker-months',
22794                 cn: [
22795                 {
22796                     tag: 'table',
22797                     cls: 'table-condensed',
22798                     cn:[
22799                         Roo.bootstrap.DateField.content
22800                     ]
22801                 }
22802                 ]
22803             }
22804         ]
22805     }
22806 });
22807
22808  
22809
22810  
22811  /*
22812  * - LGPL
22813  *
22814  * CheckBox
22815  * 
22816  */
22817
22818 /**
22819  * @class Roo.bootstrap.CheckBox
22820  * @extends Roo.bootstrap.Input
22821  * Bootstrap CheckBox class
22822  * 
22823  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22824  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22825  * @cfg {String} boxLabel The text that appears beside the checkbox
22826  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22827  * @cfg {Boolean} checked initnal the element
22828  * @cfg {Boolean} inline inline the element (default false)
22829  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22830  * @cfg {String} tooltip label tooltip
22831  * 
22832  * @constructor
22833  * Create a new CheckBox
22834  * @param {Object} config The config object
22835  */
22836
22837 Roo.bootstrap.CheckBox = function(config){
22838     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22839    
22840     this.addEvents({
22841         /**
22842         * @event check
22843         * Fires when the element is checked or unchecked.
22844         * @param {Roo.bootstrap.CheckBox} this This input
22845         * @param {Boolean} checked The new checked value
22846         */
22847        check : true,
22848        /**
22849         * @event click
22850         * Fires when the element is click.
22851         * @param {Roo.bootstrap.CheckBox} this This input
22852         */
22853        click : true
22854     });
22855     
22856 };
22857
22858 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22859   
22860     inputType: 'checkbox',
22861     inputValue: 1,
22862     valueOff: 0,
22863     boxLabel: false,
22864     checked: false,
22865     weight : false,
22866     inline: false,
22867     tooltip : '',
22868     
22869     // checkbox success does not make any sense really.. 
22870     invalidClass : "",
22871     validClass : "",
22872     
22873     
22874     getAutoCreate : function()
22875     {
22876         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22877         
22878         var id = Roo.id();
22879         
22880         var cfg = {};
22881         
22882         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22883         
22884         if(this.inline){
22885             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22886         }
22887         
22888         var input =  {
22889             tag: 'input',
22890             id : id,
22891             type : this.inputType,
22892             value : this.inputValue,
22893             cls : 'roo-' + this.inputType, //'form-box',
22894             placeholder : this.placeholder || ''
22895             
22896         };
22897         
22898         if(this.inputType != 'radio'){
22899             var hidden =  {
22900                 tag: 'input',
22901                 type : 'hidden',
22902                 cls : 'roo-hidden-value',
22903                 value : this.checked ? this.inputValue : this.valueOff
22904             };
22905         }
22906         
22907             
22908         if (this.weight) { // Validity check?
22909             cfg.cls += " " + this.inputType + "-" + this.weight;
22910         }
22911         
22912         if (this.disabled) {
22913             input.disabled=true;
22914         }
22915         
22916         if(this.checked){
22917             input.checked = this.checked;
22918         }
22919         
22920         if (this.name) {
22921             
22922             input.name = this.name;
22923             
22924             if(this.inputType != 'radio'){
22925                 hidden.name = this.name;
22926                 input.name = '_hidden_' + this.name;
22927             }
22928         }
22929         
22930         if (this.size) {
22931             input.cls += ' input-' + this.size;
22932         }
22933         
22934         var settings=this;
22935         
22936         ['xs','sm','md','lg'].map(function(size){
22937             if (settings[size]) {
22938                 cfg.cls += ' col-' + size + '-' + settings[size];
22939             }
22940         });
22941         
22942         var inputblock = input;
22943          
22944         if (this.before || this.after) {
22945             
22946             inputblock = {
22947                 cls : 'input-group',
22948                 cn :  [] 
22949             };
22950             
22951             if (this.before) {
22952                 inputblock.cn.push({
22953                     tag :'span',
22954                     cls : 'input-group-addon',
22955                     html : this.before
22956                 });
22957             }
22958             
22959             inputblock.cn.push(input);
22960             
22961             if(this.inputType != 'radio'){
22962                 inputblock.cn.push(hidden);
22963             }
22964             
22965             if (this.after) {
22966                 inputblock.cn.push({
22967                     tag :'span',
22968                     cls : 'input-group-addon',
22969                     html : this.after
22970                 });
22971             }
22972             
22973         }
22974         var boxLabelCfg = false;
22975         
22976         if(this.boxLabel){
22977            
22978             boxLabelCfg = {
22979                 tag: 'label',
22980                 //'for': id, // box label is handled by onclick - so no for...
22981                 cls: 'box-label',
22982                 html: this.boxLabel
22983             };
22984             if(this.tooltip){
22985                 boxLabelCfg.tooltip = this.tooltip;
22986             }
22987              
22988         }
22989         
22990         
22991         if (align ==='left' && this.fieldLabel.length) {
22992 //                Roo.log("left and has label");
22993             cfg.cn = [
22994                 {
22995                     tag: 'label',
22996                     'for' :  id,
22997                     cls : 'control-label',
22998                     html : this.fieldLabel
22999                 },
23000                 {
23001                     cls : "", 
23002                     cn: [
23003                         inputblock
23004                     ]
23005                 }
23006             ];
23007             
23008             if (boxLabelCfg) {
23009                 cfg.cn[1].cn.push(boxLabelCfg);
23010             }
23011             
23012             if(this.labelWidth > 12){
23013                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23014             }
23015             
23016             if(this.labelWidth < 13 && this.labelmd == 0){
23017                 this.labelmd = this.labelWidth;
23018             }
23019             
23020             if(this.labellg > 0){
23021                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23022                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23023             }
23024             
23025             if(this.labelmd > 0){
23026                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23027                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23028             }
23029             
23030             if(this.labelsm > 0){
23031                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23032                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23033             }
23034             
23035             if(this.labelxs > 0){
23036                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23037                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23038             }
23039             
23040         } else if ( this.fieldLabel.length) {
23041 //                Roo.log(" label");
23042                 cfg.cn = [
23043                    
23044                     {
23045                         tag: this.boxLabel ? 'span' : 'label',
23046                         'for': id,
23047                         cls: 'control-label box-input-label',
23048                         //cls : 'input-group-addon',
23049                         html : this.fieldLabel
23050                     },
23051                     
23052                     inputblock
23053                     
23054                 ];
23055                 if (boxLabelCfg) {
23056                     cfg.cn.push(boxLabelCfg);
23057                 }
23058
23059         } else {
23060             
23061 //                Roo.log(" no label && no align");
23062                 cfg.cn = [  inputblock ] ;
23063                 if (boxLabelCfg) {
23064                     cfg.cn.push(boxLabelCfg);
23065                 }
23066
23067                 
23068         }
23069         
23070        
23071         
23072         if(this.inputType != 'radio'){
23073             cfg.cn.push(hidden);
23074         }
23075         
23076         return cfg;
23077         
23078     },
23079     
23080     /**
23081      * return the real input element.
23082      */
23083     inputEl: function ()
23084     {
23085         return this.el.select('input.roo-' + this.inputType,true).first();
23086     },
23087     hiddenEl: function ()
23088     {
23089         return this.el.select('input.roo-hidden-value',true).first();
23090     },
23091     
23092     labelEl: function()
23093     {
23094         return this.el.select('label.control-label',true).first();
23095     },
23096     /* depricated... */
23097     
23098     label: function()
23099     {
23100         return this.labelEl();
23101     },
23102     
23103     boxLabelEl: function()
23104     {
23105         return this.el.select('label.box-label',true).first();
23106     },
23107     
23108     initEvents : function()
23109     {
23110 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23111         
23112         this.inputEl().on('click', this.onClick,  this);
23113         
23114         if (this.boxLabel) { 
23115             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23116         }
23117         
23118         this.startValue = this.getValue();
23119         
23120         if(this.groupId){
23121             Roo.bootstrap.CheckBox.register(this);
23122         }
23123     },
23124     
23125     onClick : function(e)
23126     {   
23127         if(this.fireEvent('click', this, e) !== false){
23128             this.setChecked(!this.checked);
23129         }
23130         
23131     },
23132     
23133     setChecked : function(state,suppressEvent)
23134     {
23135         this.startValue = this.getValue();
23136
23137         if(this.inputType == 'radio'){
23138             
23139             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140                 e.dom.checked = false;
23141             });
23142             
23143             this.inputEl().dom.checked = true;
23144             
23145             this.inputEl().dom.value = this.inputValue;
23146             
23147             if(suppressEvent !== true){
23148                 this.fireEvent('check', this, true);
23149             }
23150             
23151             this.validate();
23152             
23153             return;
23154         }
23155         
23156         this.checked = state;
23157         
23158         this.inputEl().dom.checked = state;
23159         
23160         
23161         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23162         
23163         if(suppressEvent !== true){
23164             this.fireEvent('check', this, state);
23165         }
23166         
23167         this.validate();
23168     },
23169     
23170     getValue : function()
23171     {
23172         if(this.inputType == 'radio'){
23173             return this.getGroupValue();
23174         }
23175         
23176         return this.hiddenEl().dom.value;
23177         
23178     },
23179     
23180     getGroupValue : function()
23181     {
23182         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23183             return '';
23184         }
23185         
23186         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23187     },
23188     
23189     setValue : function(v,suppressEvent)
23190     {
23191         if(this.inputType == 'radio'){
23192             this.setGroupValue(v, suppressEvent);
23193             return;
23194         }
23195         
23196         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23197         
23198         this.validate();
23199     },
23200     
23201     setGroupValue : function(v, suppressEvent)
23202     {
23203         this.startValue = this.getValue();
23204         
23205         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23206             e.dom.checked = false;
23207             
23208             if(e.dom.value == v){
23209                 e.dom.checked = true;
23210             }
23211         });
23212         
23213         if(suppressEvent !== true){
23214             this.fireEvent('check', this, true);
23215         }
23216
23217         this.validate();
23218         
23219         return;
23220     },
23221     
23222     validate : function()
23223     {
23224         if(this.getVisibilityEl().hasClass('hidden')){
23225             return true;
23226         }
23227         
23228         if(
23229                 this.disabled || 
23230                 (this.inputType == 'radio' && this.validateRadio()) ||
23231                 (this.inputType == 'checkbox' && this.validateCheckbox())
23232         ){
23233             this.markValid();
23234             return true;
23235         }
23236         
23237         this.markInvalid();
23238         return false;
23239     },
23240     
23241     validateRadio : function()
23242     {
23243         if(this.getVisibilityEl().hasClass('hidden')){
23244             return true;
23245         }
23246         
23247         if(this.allowBlank){
23248             return true;
23249         }
23250         
23251         var valid = false;
23252         
23253         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23254             if(!e.dom.checked){
23255                 return;
23256             }
23257             
23258             valid = true;
23259             
23260             return false;
23261         });
23262         
23263         return valid;
23264     },
23265     
23266     validateCheckbox : function()
23267     {
23268         if(!this.groupId){
23269             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23270             //return (this.getValue() == this.inputValue) ? true : false;
23271         }
23272         
23273         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23274         
23275         if(!group){
23276             return false;
23277         }
23278         
23279         var r = false;
23280         
23281         for(var i in group){
23282             if(group[i].el.isVisible(true)){
23283                 r = false;
23284                 break;
23285             }
23286             
23287             r = true;
23288         }
23289         
23290         for(var i in group){
23291             if(r){
23292                 break;
23293             }
23294             
23295             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23296         }
23297         
23298         return r;
23299     },
23300     
23301     /**
23302      * Mark this field as valid
23303      */
23304     markValid : function()
23305     {
23306         var _this = this;
23307         
23308         this.fireEvent('valid', this);
23309         
23310         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23311         
23312         if(this.groupId){
23313             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23314         }
23315         
23316         if(label){
23317             label.markValid();
23318         }
23319
23320         if(this.inputType == 'radio'){
23321             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23322                 var fg = e.findParent('.form-group', false, true);
23323                 if (Roo.bootstrap.version == 3) {
23324                     fg.removeClass([_this.invalidClass, _this.validClass]);
23325                     fg.addClass(_this.validClass);
23326                 } else {
23327                     fg.removeClass(['is-valid', 'is-invalid']);
23328                     fg.addClass('is-valid');
23329                 }
23330             });
23331             
23332             return;
23333         }
23334
23335         if(!this.groupId){
23336             var fg = this.el.findParent('.form-group', false, true);
23337             if (Roo.bootstrap.version == 3) {
23338                 fg.removeClass([this.invalidClass, this.validClass]);
23339                 fg.addClass(this.validClass);
23340             } else {
23341                 fg.removeClass(['is-valid', 'is-invalid']);
23342                 fg.addClass('is-valid');
23343             }
23344             return;
23345         }
23346         
23347         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23348         
23349         if(!group){
23350             return;
23351         }
23352         
23353         for(var i in group){
23354             var fg = group[i].el.findParent('.form-group', false, true);
23355             if (Roo.bootstrap.version == 3) {
23356                 fg.removeClass([this.invalidClass, this.validClass]);
23357                 fg.addClass(this.validClass);
23358             } else {
23359                 fg.removeClass(['is-valid', 'is-invalid']);
23360                 fg.addClass('is-valid');
23361             }
23362         }
23363     },
23364     
23365      /**
23366      * Mark this field as invalid
23367      * @param {String} msg The validation message
23368      */
23369     markInvalid : function(msg)
23370     {
23371         if(this.allowBlank){
23372             return;
23373         }
23374         
23375         var _this = this;
23376         
23377         this.fireEvent('invalid', this, msg);
23378         
23379         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23380         
23381         if(this.groupId){
23382             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23383         }
23384         
23385         if(label){
23386             label.markInvalid();
23387         }
23388             
23389         if(this.inputType == 'radio'){
23390             
23391             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23392                 var fg = e.findParent('.form-group', false, true);
23393                 if (Roo.bootstrap.version == 3) {
23394                     fg.removeClass([_this.invalidClass, _this.validClass]);
23395                     fg.addClass(_this.invalidClass);
23396                 } else {
23397                     fg.removeClass(['is-invalid', 'is-valid']);
23398                     fg.addClass('is-invalid');
23399                 }
23400             });
23401             
23402             return;
23403         }
23404         
23405         if(!this.groupId){
23406             var fg = this.el.findParent('.form-group', false, true);
23407             if (Roo.bootstrap.version == 3) {
23408                 fg.removeClass([_this.invalidClass, _this.validClass]);
23409                 fg.addClass(_this.invalidClass);
23410             } else {
23411                 fg.removeClass(['is-invalid', 'is-valid']);
23412                 fg.addClass('is-invalid');
23413             }
23414             return;
23415         }
23416         
23417         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23418         
23419         if(!group){
23420             return;
23421         }
23422         
23423         for(var i in group){
23424             var fg = group[i].el.findParent('.form-group', false, true);
23425             if (Roo.bootstrap.version == 3) {
23426                 fg.removeClass([_this.invalidClass, _this.validClass]);
23427                 fg.addClass(_this.invalidClass);
23428             } else {
23429                 fg.removeClass(['is-invalid', 'is-valid']);
23430                 fg.addClass('is-invalid');
23431             }
23432         }
23433         
23434     },
23435     
23436     clearInvalid : function()
23437     {
23438         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23439         
23440         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23441         
23442         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23443         
23444         if (label && label.iconEl) {
23445             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23446             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23447         }
23448     },
23449     
23450     disable : function()
23451     {
23452         if(this.inputType != 'radio'){
23453             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23454             return;
23455         }
23456         
23457         var _this = this;
23458         
23459         if(this.rendered){
23460             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23461                 _this.getActionEl().addClass(this.disabledClass);
23462                 e.dom.disabled = true;
23463             });
23464         }
23465         
23466         this.disabled = true;
23467         this.fireEvent("disable", this);
23468         return this;
23469     },
23470
23471     enable : function()
23472     {
23473         if(this.inputType != 'radio'){
23474             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23475             return;
23476         }
23477         
23478         var _this = this;
23479         
23480         if(this.rendered){
23481             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482                 _this.getActionEl().removeClass(this.disabledClass);
23483                 e.dom.disabled = false;
23484             });
23485         }
23486         
23487         this.disabled = false;
23488         this.fireEvent("enable", this);
23489         return this;
23490     },
23491     
23492     setBoxLabel : function(v)
23493     {
23494         this.boxLabel = v;
23495         
23496         if(this.rendered){
23497             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23498         }
23499     }
23500
23501 });
23502
23503 Roo.apply(Roo.bootstrap.CheckBox, {
23504     
23505     groups: {},
23506     
23507      /**
23508     * register a CheckBox Group
23509     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23510     */
23511     register : function(checkbox)
23512     {
23513         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23514             this.groups[checkbox.groupId] = {};
23515         }
23516         
23517         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23518             return;
23519         }
23520         
23521         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23522         
23523     },
23524     /**
23525     * fetch a CheckBox Group based on the group ID
23526     * @param {string} the group ID
23527     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23528     */
23529     get: function(groupId) {
23530         if (typeof(this.groups[groupId]) == 'undefined') {
23531             return false;
23532         }
23533         
23534         return this.groups[groupId] ;
23535     }
23536     
23537     
23538 });
23539 /*
23540  * - LGPL
23541  *
23542  * RadioItem
23543  * 
23544  */
23545
23546 /**
23547  * @class Roo.bootstrap.Radio
23548  * @extends Roo.bootstrap.Component
23549  * Bootstrap Radio class
23550  * @cfg {String} boxLabel - the label associated
23551  * @cfg {String} value - the value of radio
23552  * 
23553  * @constructor
23554  * Create a new Radio
23555  * @param {Object} config The config object
23556  */
23557 Roo.bootstrap.Radio = function(config){
23558     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23559     
23560 };
23561
23562 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23563     
23564     boxLabel : '',
23565     
23566     value : '',
23567     
23568     getAutoCreate : function()
23569     {
23570         var cfg = {
23571             tag : 'div',
23572             cls : 'form-group radio',
23573             cn : [
23574                 {
23575                     tag : 'label',
23576                     cls : 'box-label',
23577                     html : this.boxLabel
23578                 }
23579             ]
23580         };
23581         
23582         return cfg;
23583     },
23584     
23585     initEvents : function() 
23586     {
23587         this.parent().register(this);
23588         
23589         this.el.on('click', this.onClick, this);
23590         
23591     },
23592     
23593     onClick : function(e)
23594     {
23595         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23596             this.setChecked(true);
23597         }
23598     },
23599     
23600     setChecked : function(state, suppressEvent)
23601     {
23602         this.parent().setValue(this.value, suppressEvent);
23603         
23604     },
23605     
23606     setBoxLabel : function(v)
23607     {
23608         this.boxLabel = v;
23609         
23610         if(this.rendered){
23611             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23612         }
23613     }
23614     
23615 });
23616  
23617
23618  /*
23619  * - LGPL
23620  *
23621  * Input
23622  * 
23623  */
23624
23625 /**
23626  * @class Roo.bootstrap.SecurePass
23627  * @extends Roo.bootstrap.Input
23628  * Bootstrap SecurePass class
23629  *
23630  * 
23631  * @constructor
23632  * Create a new SecurePass
23633  * @param {Object} config The config object
23634  */
23635  
23636 Roo.bootstrap.SecurePass = function (config) {
23637     // these go here, so the translation tool can replace them..
23638     this.errors = {
23639         PwdEmpty: "Please type a password, and then retype it to confirm.",
23640         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23641         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23642         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23643         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23644         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23645         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23646         TooWeak: "Your password is Too Weak."
23647     },
23648     this.meterLabel = "Password strength:";
23649     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23650     this.meterClass = [
23651         "roo-password-meter-tooweak", 
23652         "roo-password-meter-weak", 
23653         "roo-password-meter-medium", 
23654         "roo-password-meter-strong", 
23655         "roo-password-meter-grey"
23656     ];
23657     
23658     this.errors = {};
23659     
23660     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23661 }
23662
23663 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23664     /**
23665      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23666      * {
23667      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23668      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23669      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23670      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23671      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23672      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23673      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23674      * })
23675      */
23676     // private
23677     
23678     meterWidth: 300,
23679     errorMsg :'',    
23680     errors: false,
23681     imageRoot: '/',
23682     /**
23683      * @cfg {String/Object} Label for the strength meter (defaults to
23684      * 'Password strength:')
23685      */
23686     // private
23687     meterLabel: '',
23688     /**
23689      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23690      * ['Weak', 'Medium', 'Strong'])
23691      */
23692     // private    
23693     pwdStrengths: false,    
23694     // private
23695     strength: 0,
23696     // private
23697     _lastPwd: null,
23698     // private
23699     kCapitalLetter: 0,
23700     kSmallLetter: 1,
23701     kDigit: 2,
23702     kPunctuation: 3,
23703     
23704     insecure: false,
23705     // private
23706     initEvents: function ()
23707     {
23708         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23709
23710         if (this.el.is('input[type=password]') && Roo.isSafari) {
23711             this.el.on('keydown', this.SafariOnKeyDown, this);
23712         }
23713
23714         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23715     },
23716     // private
23717     onRender: function (ct, position)
23718     {
23719         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23720         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23721         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23722
23723         this.trigger.createChild({
23724                    cn: [
23725                     {
23726                     //id: 'PwdMeter',
23727                     tag: 'div',
23728                     cls: 'roo-password-meter-grey col-xs-12',
23729                     style: {
23730                         //width: 0,
23731                         //width: this.meterWidth + 'px'                                                
23732                         }
23733                     },
23734                     {                            
23735                          cls: 'roo-password-meter-text'                          
23736                     }
23737                 ]            
23738         });
23739
23740          
23741         if (this.hideTrigger) {
23742             this.trigger.setDisplayed(false);
23743         }
23744         this.setSize(this.width || '', this.height || '');
23745     },
23746     // private
23747     onDestroy: function ()
23748     {
23749         if (this.trigger) {
23750             this.trigger.removeAllListeners();
23751             this.trigger.remove();
23752         }
23753         if (this.wrap) {
23754             this.wrap.remove();
23755         }
23756         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23757     },
23758     // private
23759     checkStrength: function ()
23760     {
23761         var pwd = this.inputEl().getValue();
23762         if (pwd == this._lastPwd) {
23763             return;
23764         }
23765
23766         var strength;
23767         if (this.ClientSideStrongPassword(pwd)) {
23768             strength = 3;
23769         } else if (this.ClientSideMediumPassword(pwd)) {
23770             strength = 2;
23771         } else if (this.ClientSideWeakPassword(pwd)) {
23772             strength = 1;
23773         } else {
23774             strength = 0;
23775         }
23776         
23777         Roo.log('strength1: ' + strength);
23778         
23779         //var pm = this.trigger.child('div/div/div').dom;
23780         var pm = this.trigger.child('div/div');
23781         pm.removeClass(this.meterClass);
23782         pm.addClass(this.meterClass[strength]);
23783                 
23784         
23785         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23786                 
23787         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23788         
23789         this._lastPwd = pwd;
23790     },
23791     reset: function ()
23792     {
23793         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23794         
23795         this._lastPwd = '';
23796         
23797         var pm = this.trigger.child('div/div');
23798         pm.removeClass(this.meterClass);
23799         pm.addClass('roo-password-meter-grey');        
23800         
23801         
23802         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23803         
23804         pt.innerHTML = '';
23805         this.inputEl().dom.type='password';
23806     },
23807     // private
23808     validateValue: function (value)
23809     {
23810         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23811             return false;
23812         }
23813         if (value.length == 0) {
23814             if (this.allowBlank) {
23815                 this.clearInvalid();
23816                 return true;
23817             }
23818
23819             this.markInvalid(this.errors.PwdEmpty);
23820             this.errorMsg = this.errors.PwdEmpty;
23821             return false;
23822         }
23823         
23824         if(this.insecure){
23825             return true;
23826         }
23827         
23828         if (!value.match(/[\x21-\x7e]+/)) {
23829             this.markInvalid(this.errors.PwdBadChar);
23830             this.errorMsg = this.errors.PwdBadChar;
23831             return false;
23832         }
23833         if (value.length < 6) {
23834             this.markInvalid(this.errors.PwdShort);
23835             this.errorMsg = this.errors.PwdShort;
23836             return false;
23837         }
23838         if (value.length > 16) {
23839             this.markInvalid(this.errors.PwdLong);
23840             this.errorMsg = this.errors.PwdLong;
23841             return false;
23842         }
23843         var strength;
23844         if (this.ClientSideStrongPassword(value)) {
23845             strength = 3;
23846         } else if (this.ClientSideMediumPassword(value)) {
23847             strength = 2;
23848         } else if (this.ClientSideWeakPassword(value)) {
23849             strength = 1;
23850         } else {
23851             strength = 0;
23852         }
23853
23854         
23855         if (strength < 2) {
23856             //this.markInvalid(this.errors.TooWeak);
23857             this.errorMsg = this.errors.TooWeak;
23858             //return false;
23859         }
23860         
23861         
23862         console.log('strength2: ' + strength);
23863         
23864         //var pm = this.trigger.child('div/div/div').dom;
23865         
23866         var pm = this.trigger.child('div/div');
23867         pm.removeClass(this.meterClass);
23868         pm.addClass(this.meterClass[strength]);
23869                 
23870         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23871                 
23872         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23873         
23874         this.errorMsg = ''; 
23875         return true;
23876     },
23877     // private
23878     CharacterSetChecks: function (type)
23879     {
23880         this.type = type;
23881         this.fResult = false;
23882     },
23883     // private
23884     isctype: function (character, type)
23885     {
23886         switch (type) {  
23887             case this.kCapitalLetter:
23888                 if (character >= 'A' && character <= 'Z') {
23889                     return true;
23890                 }
23891                 break;
23892             
23893             case this.kSmallLetter:
23894                 if (character >= 'a' && character <= 'z') {
23895                     return true;
23896                 }
23897                 break;
23898             
23899             case this.kDigit:
23900                 if (character >= '0' && character <= '9') {
23901                     return true;
23902                 }
23903                 break;
23904             
23905             case this.kPunctuation:
23906                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23907                     return true;
23908                 }
23909                 break;
23910             
23911             default:
23912                 return false;
23913         }
23914
23915     },
23916     // private
23917     IsLongEnough: function (pwd, size)
23918     {
23919         return !(pwd == null || isNaN(size) || pwd.length < size);
23920     },
23921     // private
23922     SpansEnoughCharacterSets: function (word, nb)
23923     {
23924         if (!this.IsLongEnough(word, nb))
23925         {
23926             return false;
23927         }
23928
23929         var characterSetChecks = new Array(
23930             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23931             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23932         );
23933         
23934         for (var index = 0; index < word.length; ++index) {
23935             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23936                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23937                     characterSetChecks[nCharSet].fResult = true;
23938                     break;
23939                 }
23940             }
23941         }
23942
23943         var nCharSets = 0;
23944         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23945             if (characterSetChecks[nCharSet].fResult) {
23946                 ++nCharSets;
23947             }
23948         }
23949
23950         if (nCharSets < nb) {
23951             return false;
23952         }
23953         return true;
23954     },
23955     // private
23956     ClientSideStrongPassword: function (pwd)
23957     {
23958         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23959     },
23960     // private
23961     ClientSideMediumPassword: function (pwd)
23962     {
23963         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23964     },
23965     // private
23966     ClientSideWeakPassword: function (pwd)
23967     {
23968         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23969     }
23970           
23971 })//<script type="text/javascript">
23972
23973 /*
23974  * Based  Ext JS Library 1.1.1
23975  * Copyright(c) 2006-2007, Ext JS, LLC.
23976  * LGPL
23977  *
23978  */
23979  
23980 /**
23981  * @class Roo.HtmlEditorCore
23982  * @extends Roo.Component
23983  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23984  *
23985  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23986  */
23987
23988 Roo.HtmlEditorCore = function(config){
23989     
23990     
23991     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23992     
23993     
23994     this.addEvents({
23995         /**
23996          * @event initialize
23997          * Fires when the editor is fully initialized (including the iframe)
23998          * @param {Roo.HtmlEditorCore} this
23999          */
24000         initialize: true,
24001         /**
24002          * @event activate
24003          * Fires when the editor is first receives the focus. Any insertion must wait
24004          * until after this event.
24005          * @param {Roo.HtmlEditorCore} this
24006          */
24007         activate: true,
24008          /**
24009          * @event beforesync
24010          * Fires before the textarea is updated with content from the editor iframe. Return false
24011          * to cancel the sync.
24012          * @param {Roo.HtmlEditorCore} this
24013          * @param {String} html
24014          */
24015         beforesync: true,
24016          /**
24017          * @event beforepush
24018          * Fires before the iframe editor is updated with content from the textarea. Return false
24019          * to cancel the push.
24020          * @param {Roo.HtmlEditorCore} this
24021          * @param {String} html
24022          */
24023         beforepush: true,
24024          /**
24025          * @event sync
24026          * Fires when the textarea is updated with content from the editor iframe.
24027          * @param {Roo.HtmlEditorCore} this
24028          * @param {String} html
24029          */
24030         sync: true,
24031          /**
24032          * @event push
24033          * Fires when the iframe editor is updated with content from the textarea.
24034          * @param {Roo.HtmlEditorCore} this
24035          * @param {String} html
24036          */
24037         push: true,
24038         
24039         /**
24040          * @event editorevent
24041          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24042          * @param {Roo.HtmlEditorCore} this
24043          */
24044         editorevent: true
24045         
24046     });
24047     
24048     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24049     
24050     // defaults : white / black...
24051     this.applyBlacklists();
24052     
24053     
24054     
24055 };
24056
24057
24058 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24059
24060
24061      /**
24062      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24063      */
24064     
24065     owner : false,
24066     
24067      /**
24068      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24069      *                        Roo.resizable.
24070      */
24071     resizable : false,
24072      /**
24073      * @cfg {Number} height (in pixels)
24074      */   
24075     height: 300,
24076    /**
24077      * @cfg {Number} width (in pixels)
24078      */   
24079     width: 500,
24080     
24081     /**
24082      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24083      * 
24084      */
24085     stylesheets: false,
24086     
24087     // id of frame..
24088     frameId: false,
24089     
24090     // private properties
24091     validationEvent : false,
24092     deferHeight: true,
24093     initialized : false,
24094     activated : false,
24095     sourceEditMode : false,
24096     onFocus : Roo.emptyFn,
24097     iframePad:3,
24098     hideMode:'offsets',
24099     
24100     clearUp: true,
24101     
24102     // blacklist + whitelisted elements..
24103     black: false,
24104     white: false,
24105      
24106     bodyCls : '',
24107
24108     /**
24109      * Protected method that will not generally be called directly. It
24110      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24111      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24112      */
24113     getDocMarkup : function(){
24114         // body styles..
24115         var st = '';
24116         
24117         // inherit styels from page...?? 
24118         if (this.stylesheets === false) {
24119             
24120             Roo.get(document.head).select('style').each(function(node) {
24121                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24122             });
24123             
24124             Roo.get(document.head).select('link').each(function(node) { 
24125                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24126             });
24127             
24128         } else if (!this.stylesheets.length) {
24129                 // simple..
24130                 st = '<style type="text/css">' +
24131                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24132                    '</style>';
24133         } else {
24134             for (var i in this.stylesheets) { 
24135                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24136             }
24137             
24138         }
24139         
24140         st +=  '<style type="text/css">' +
24141             'IMG { cursor: pointer } ' +
24142         '</style>';
24143
24144         var cls = 'roo-htmleditor-body';
24145         
24146         if(this.bodyCls.length){
24147             cls += ' ' + this.bodyCls;
24148         }
24149         
24150         return '<html><head>' + st  +
24151             //<style type="text/css">' +
24152             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24153             //'</style>' +
24154             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24155     },
24156
24157     // private
24158     onRender : function(ct, position)
24159     {
24160         var _t = this;
24161         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24162         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24163         
24164         
24165         this.el.dom.style.border = '0 none';
24166         this.el.dom.setAttribute('tabIndex', -1);
24167         this.el.addClass('x-hidden hide');
24168         
24169         
24170         
24171         if(Roo.isIE){ // fix IE 1px bogus margin
24172             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24173         }
24174        
24175         
24176         this.frameId = Roo.id();
24177         
24178          
24179         
24180         var iframe = this.owner.wrap.createChild({
24181             tag: 'iframe',
24182             cls: 'form-control', // bootstrap..
24183             id: this.frameId,
24184             name: this.frameId,
24185             frameBorder : 'no',
24186             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24187         }, this.el
24188         );
24189         
24190         
24191         this.iframe = iframe.dom;
24192
24193          this.assignDocWin();
24194         
24195         this.doc.designMode = 'on';
24196        
24197         this.doc.open();
24198         this.doc.write(this.getDocMarkup());
24199         this.doc.close();
24200
24201         
24202         var task = { // must defer to wait for browser to be ready
24203             run : function(){
24204                 //console.log("run task?" + this.doc.readyState);
24205                 this.assignDocWin();
24206                 if(this.doc.body || this.doc.readyState == 'complete'){
24207                     try {
24208                         this.doc.designMode="on";
24209                     } catch (e) {
24210                         return;
24211                     }
24212                     Roo.TaskMgr.stop(task);
24213                     this.initEditor.defer(10, this);
24214                 }
24215             },
24216             interval : 10,
24217             duration: 10000,
24218             scope: this
24219         };
24220         Roo.TaskMgr.start(task);
24221
24222     },
24223
24224     // private
24225     onResize : function(w, h)
24226     {
24227          Roo.log('resize: ' +w + ',' + h );
24228         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24229         if(!this.iframe){
24230             return;
24231         }
24232         if(typeof w == 'number'){
24233             
24234             this.iframe.style.width = w + 'px';
24235         }
24236         if(typeof h == 'number'){
24237             
24238             this.iframe.style.height = h + 'px';
24239             if(this.doc){
24240                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24241             }
24242         }
24243         
24244     },
24245
24246     /**
24247      * Toggles the editor between standard and source edit mode.
24248      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24249      */
24250     toggleSourceEdit : function(sourceEditMode){
24251         
24252         this.sourceEditMode = sourceEditMode === true;
24253         
24254         if(this.sourceEditMode){
24255  
24256             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24257             
24258         }else{
24259             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24260             //this.iframe.className = '';
24261             this.deferFocus();
24262         }
24263         //this.setSize(this.owner.wrap.getSize());
24264         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24265     },
24266
24267     
24268   
24269
24270     /**
24271      * Protected method that will not generally be called directly. If you need/want
24272      * custom HTML cleanup, this is the method you should override.
24273      * @param {String} html The HTML to be cleaned
24274      * return {String} The cleaned HTML
24275      */
24276     cleanHtml : function(html){
24277         html = String(html);
24278         if(html.length > 5){
24279             if(Roo.isSafari){ // strip safari nonsense
24280                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24281             }
24282         }
24283         if(html == '&nbsp;'){
24284             html = '';
24285         }
24286         return html;
24287     },
24288
24289     /**
24290      * HTML Editor -> Textarea
24291      * Protected method that will not generally be called directly. Syncs the contents
24292      * of the editor iframe with the textarea.
24293      */
24294     syncValue : function(){
24295         if(this.initialized){
24296             var bd = (this.doc.body || this.doc.documentElement);
24297             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24298             var html = bd.innerHTML;
24299             if(Roo.isSafari){
24300                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24301                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24302                 if(m && m[1]){
24303                     html = '<div style="'+m[0]+'">' + html + '</div>';
24304                 }
24305             }
24306             html = this.cleanHtml(html);
24307             // fix up the special chars.. normaly like back quotes in word...
24308             // however we do not want to do this with chinese..
24309             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24310                 
24311                 var cc = match.charCodeAt();
24312
24313                 // Get the character value, handling surrogate pairs
24314                 if (match.length == 2) {
24315                     // It's a surrogate pair, calculate the Unicode code point
24316                     var high = match.charCodeAt(0) - 0xD800;
24317                     var low  = match.charCodeAt(1) - 0xDC00;
24318                     cc = (high * 0x400) + low + 0x10000;
24319                 }  else if (
24320                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24321                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24322                     (cc >= 0xf900 && cc < 0xfb00 )
24323                 ) {
24324                         return match;
24325                 }  
24326          
24327                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24328                 return "&#" + cc + ";";
24329                 
24330                 
24331             });
24332             
24333             
24334              
24335             if(this.owner.fireEvent('beforesync', this, html) !== false){
24336                 this.el.dom.value = html;
24337                 this.owner.fireEvent('sync', this, html);
24338             }
24339         }
24340     },
24341
24342     /**
24343      * Protected method that will not generally be called directly. Pushes the value of the textarea
24344      * into the iframe editor.
24345      */
24346     pushValue : function(){
24347         if(this.initialized){
24348             var v = this.el.dom.value.trim();
24349             
24350 //            if(v.length < 1){
24351 //                v = '&#160;';
24352 //            }
24353             
24354             if(this.owner.fireEvent('beforepush', this, v) !== false){
24355                 var d = (this.doc.body || this.doc.documentElement);
24356                 d.innerHTML = v;
24357                 this.cleanUpPaste();
24358                 this.el.dom.value = d.innerHTML;
24359                 this.owner.fireEvent('push', this, v);
24360             }
24361         }
24362     },
24363
24364     // private
24365     deferFocus : function(){
24366         this.focus.defer(10, this);
24367     },
24368
24369     // doc'ed in Field
24370     focus : function(){
24371         if(this.win && !this.sourceEditMode){
24372             this.win.focus();
24373         }else{
24374             this.el.focus();
24375         }
24376     },
24377     
24378     assignDocWin: function()
24379     {
24380         var iframe = this.iframe;
24381         
24382          if(Roo.isIE){
24383             this.doc = iframe.contentWindow.document;
24384             this.win = iframe.contentWindow;
24385         } else {
24386 //            if (!Roo.get(this.frameId)) {
24387 //                return;
24388 //            }
24389 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24390 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24391             
24392             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24393                 return;
24394             }
24395             
24396             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24397             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24398         }
24399     },
24400     
24401     // private
24402     initEditor : function(){
24403         //console.log("INIT EDITOR");
24404         this.assignDocWin();
24405         
24406         
24407         
24408         this.doc.designMode="on";
24409         this.doc.open();
24410         this.doc.write(this.getDocMarkup());
24411         this.doc.close();
24412         
24413         var dbody = (this.doc.body || this.doc.documentElement);
24414         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24415         // this copies styles from the containing element into thsi one..
24416         // not sure why we need all of this..
24417         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24418         
24419         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24420         //ss['background-attachment'] = 'fixed'; // w3c
24421         dbody.bgProperties = 'fixed'; // ie
24422         //Roo.DomHelper.applyStyles(dbody, ss);
24423         Roo.EventManager.on(this.doc, {
24424             //'mousedown': this.onEditorEvent,
24425             'mouseup': this.onEditorEvent,
24426             'dblclick': this.onEditorEvent,
24427             'click': this.onEditorEvent,
24428             'keyup': this.onEditorEvent,
24429             buffer:100,
24430             scope: this
24431         });
24432         if(Roo.isGecko){
24433             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24434         }
24435         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24436             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24437         }
24438         this.initialized = true;
24439
24440         this.owner.fireEvent('initialize', this);
24441         this.pushValue();
24442     },
24443
24444     // private
24445     onDestroy : function(){
24446         
24447         
24448         
24449         if(this.rendered){
24450             
24451             //for (var i =0; i < this.toolbars.length;i++) {
24452             //    // fixme - ask toolbars for heights?
24453             //    this.toolbars[i].onDestroy();
24454            // }
24455             
24456             //this.wrap.dom.innerHTML = '';
24457             //this.wrap.remove();
24458         }
24459     },
24460
24461     // private
24462     onFirstFocus : function(){
24463         
24464         this.assignDocWin();
24465         
24466         
24467         this.activated = true;
24468          
24469     
24470         if(Roo.isGecko){ // prevent silly gecko errors
24471             this.win.focus();
24472             var s = this.win.getSelection();
24473             if(!s.focusNode || s.focusNode.nodeType != 3){
24474                 var r = s.getRangeAt(0);
24475                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24476                 r.collapse(true);
24477                 this.deferFocus();
24478             }
24479             try{
24480                 this.execCmd('useCSS', true);
24481                 this.execCmd('styleWithCSS', false);
24482             }catch(e){}
24483         }
24484         this.owner.fireEvent('activate', this);
24485     },
24486
24487     // private
24488     adjustFont: function(btn){
24489         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24490         //if(Roo.isSafari){ // safari
24491         //    adjust *= 2;
24492        // }
24493         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24494         if(Roo.isSafari){ // safari
24495             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24496             v =  (v < 10) ? 10 : v;
24497             v =  (v > 48) ? 48 : v;
24498             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24499             
24500         }
24501         
24502         
24503         v = Math.max(1, v+adjust);
24504         
24505         this.execCmd('FontSize', v  );
24506     },
24507
24508     onEditorEvent : function(e)
24509     {
24510         this.owner.fireEvent('editorevent', this, e);
24511       //  this.updateToolbar();
24512         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24513     },
24514
24515     insertTag : function(tg)
24516     {
24517         // could be a bit smarter... -> wrap the current selected tRoo..
24518         if (tg.toLowerCase() == 'span' ||
24519             tg.toLowerCase() == 'code' ||
24520             tg.toLowerCase() == 'sup' ||
24521             tg.toLowerCase() == 'sub' 
24522             ) {
24523             
24524             range = this.createRange(this.getSelection());
24525             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24526             wrappingNode.appendChild(range.extractContents());
24527             range.insertNode(wrappingNode);
24528
24529             return;
24530             
24531             
24532             
24533         }
24534         this.execCmd("formatblock",   tg);
24535         
24536     },
24537     
24538     insertText : function(txt)
24539     {
24540         
24541         
24542         var range = this.createRange();
24543         range.deleteContents();
24544                //alert(Sender.getAttribute('label'));
24545                
24546         range.insertNode(this.doc.createTextNode(txt));
24547     } ,
24548     
24549      
24550
24551     /**
24552      * Executes a Midas editor command on the editor document and performs necessary focus and
24553      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24554      * @param {String} cmd The Midas command
24555      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24556      */
24557     relayCmd : function(cmd, value){
24558         this.win.focus();
24559         this.execCmd(cmd, value);
24560         this.owner.fireEvent('editorevent', this);
24561         //this.updateToolbar();
24562         this.owner.deferFocus();
24563     },
24564
24565     /**
24566      * Executes a Midas editor command directly on the editor document.
24567      * For visual commands, you should use {@link #relayCmd} instead.
24568      * <b>This should only be called after the editor is initialized.</b>
24569      * @param {String} cmd The Midas command
24570      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24571      */
24572     execCmd : function(cmd, value){
24573         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24574         this.syncValue();
24575     },
24576  
24577  
24578    
24579     /**
24580      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24581      * to insert tRoo.
24582      * @param {String} text | dom node.. 
24583      */
24584     insertAtCursor : function(text)
24585     {
24586         
24587         if(!this.activated){
24588             return;
24589         }
24590         /*
24591         if(Roo.isIE){
24592             this.win.focus();
24593             var r = this.doc.selection.createRange();
24594             if(r){
24595                 r.collapse(true);
24596                 r.pasteHTML(text);
24597                 this.syncValue();
24598                 this.deferFocus();
24599             
24600             }
24601             return;
24602         }
24603         */
24604         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24605             this.win.focus();
24606             
24607             
24608             // from jquery ui (MIT licenced)
24609             var range, node;
24610             var win = this.win;
24611             
24612             if (win.getSelection && win.getSelection().getRangeAt) {
24613                 range = win.getSelection().getRangeAt(0);
24614                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24615                 range.insertNode(node);
24616             } else if (win.document.selection && win.document.selection.createRange) {
24617                 // no firefox support
24618                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24619                 win.document.selection.createRange().pasteHTML(txt);
24620             } else {
24621                 // no firefox support
24622                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24623                 this.execCmd('InsertHTML', txt);
24624             } 
24625             
24626             this.syncValue();
24627             
24628             this.deferFocus();
24629         }
24630     },
24631  // private
24632     mozKeyPress : function(e){
24633         if(e.ctrlKey){
24634             var c = e.getCharCode(), cmd;
24635           
24636             if(c > 0){
24637                 c = String.fromCharCode(c).toLowerCase();
24638                 switch(c){
24639                     case 'b':
24640                         cmd = 'bold';
24641                         break;
24642                     case 'i':
24643                         cmd = 'italic';
24644                         break;
24645                     
24646                     case 'u':
24647                         cmd = 'underline';
24648                         break;
24649                     
24650                     case 'v':
24651                         this.cleanUpPaste.defer(100, this);
24652                         return;
24653                         
24654                 }
24655                 if(cmd){
24656                     this.win.focus();
24657                     this.execCmd(cmd);
24658                     this.deferFocus();
24659                     e.preventDefault();
24660                 }
24661                 
24662             }
24663         }
24664     },
24665
24666     // private
24667     fixKeys : function(){ // load time branching for fastest keydown performance
24668         if(Roo.isIE){
24669             return function(e){
24670                 var k = e.getKey(), r;
24671                 if(k == e.TAB){
24672                     e.stopEvent();
24673                     r = this.doc.selection.createRange();
24674                     if(r){
24675                         r.collapse(true);
24676                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24677                         this.deferFocus();
24678                     }
24679                     return;
24680                 }
24681                 
24682                 if(k == e.ENTER){
24683                     r = this.doc.selection.createRange();
24684                     if(r){
24685                         var target = r.parentElement();
24686                         if(!target || target.tagName.toLowerCase() != 'li'){
24687                             e.stopEvent();
24688                             r.pasteHTML('<br />');
24689                             r.collapse(false);
24690                             r.select();
24691                         }
24692                     }
24693                 }
24694                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24695                     this.cleanUpPaste.defer(100, this);
24696                     return;
24697                 }
24698                 
24699                 
24700             };
24701         }else if(Roo.isOpera){
24702             return function(e){
24703                 var k = e.getKey();
24704                 if(k == e.TAB){
24705                     e.stopEvent();
24706                     this.win.focus();
24707                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24708                     this.deferFocus();
24709                 }
24710                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24711                     this.cleanUpPaste.defer(100, this);
24712                     return;
24713                 }
24714                 
24715             };
24716         }else if(Roo.isSafari){
24717             return function(e){
24718                 var k = e.getKey();
24719                 
24720                 if(k == e.TAB){
24721                     e.stopEvent();
24722                     this.execCmd('InsertText','\t');
24723                     this.deferFocus();
24724                     return;
24725                 }
24726                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24727                     this.cleanUpPaste.defer(100, this);
24728                     return;
24729                 }
24730                 
24731              };
24732         }
24733     }(),
24734     
24735     getAllAncestors: function()
24736     {
24737         var p = this.getSelectedNode();
24738         var a = [];
24739         if (!p) {
24740             a.push(p); // push blank onto stack..
24741             p = this.getParentElement();
24742         }
24743         
24744         
24745         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24746             a.push(p);
24747             p = p.parentNode;
24748         }
24749         a.push(this.doc.body);
24750         return a;
24751     },
24752     lastSel : false,
24753     lastSelNode : false,
24754     
24755     
24756     getSelection : function() 
24757     {
24758         this.assignDocWin();
24759         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24760     },
24761     
24762     getSelectedNode: function() 
24763     {
24764         // this may only work on Gecko!!!
24765         
24766         // should we cache this!!!!
24767         
24768         
24769         
24770          
24771         var range = this.createRange(this.getSelection()).cloneRange();
24772         
24773         if (Roo.isIE) {
24774             var parent = range.parentElement();
24775             while (true) {
24776                 var testRange = range.duplicate();
24777                 testRange.moveToElementText(parent);
24778                 if (testRange.inRange(range)) {
24779                     break;
24780                 }
24781                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24782                     break;
24783                 }
24784                 parent = parent.parentElement;
24785             }
24786             return parent;
24787         }
24788         
24789         // is ancestor a text element.
24790         var ac =  range.commonAncestorContainer;
24791         if (ac.nodeType == 3) {
24792             ac = ac.parentNode;
24793         }
24794         
24795         var ar = ac.childNodes;
24796          
24797         var nodes = [];
24798         var other_nodes = [];
24799         var has_other_nodes = false;
24800         for (var i=0;i<ar.length;i++) {
24801             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24802                 continue;
24803             }
24804             // fullly contained node.
24805             
24806             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24807                 nodes.push(ar[i]);
24808                 continue;
24809             }
24810             
24811             // probably selected..
24812             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24813                 other_nodes.push(ar[i]);
24814                 continue;
24815             }
24816             // outer..
24817             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24818                 continue;
24819             }
24820             
24821             
24822             has_other_nodes = true;
24823         }
24824         if (!nodes.length && other_nodes.length) {
24825             nodes= other_nodes;
24826         }
24827         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24828             return false;
24829         }
24830         
24831         return nodes[0];
24832     },
24833     createRange: function(sel)
24834     {
24835         // this has strange effects when using with 
24836         // top toolbar - not sure if it's a great idea.
24837         //this.editor.contentWindow.focus();
24838         if (typeof sel != "undefined") {
24839             try {
24840                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24841             } catch(e) {
24842                 return this.doc.createRange();
24843             }
24844         } else {
24845             return this.doc.createRange();
24846         }
24847     },
24848     getParentElement: function()
24849     {
24850         
24851         this.assignDocWin();
24852         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24853         
24854         var range = this.createRange(sel);
24855          
24856         try {
24857             var p = range.commonAncestorContainer;
24858             while (p.nodeType == 3) { // text node
24859                 p = p.parentNode;
24860             }
24861             return p;
24862         } catch (e) {
24863             return null;
24864         }
24865     
24866     },
24867     /***
24868      *
24869      * Range intersection.. the hard stuff...
24870      *  '-1' = before
24871      *  '0' = hits..
24872      *  '1' = after.
24873      *         [ -- selected range --- ]
24874      *   [fail]                        [fail]
24875      *
24876      *    basically..
24877      *      if end is before start or  hits it. fail.
24878      *      if start is after end or hits it fail.
24879      *
24880      *   if either hits (but other is outside. - then it's not 
24881      *   
24882      *    
24883      **/
24884     
24885     
24886     // @see http://www.thismuchiknow.co.uk/?p=64.
24887     rangeIntersectsNode : function(range, node)
24888     {
24889         var nodeRange = node.ownerDocument.createRange();
24890         try {
24891             nodeRange.selectNode(node);
24892         } catch (e) {
24893             nodeRange.selectNodeContents(node);
24894         }
24895     
24896         var rangeStartRange = range.cloneRange();
24897         rangeStartRange.collapse(true);
24898     
24899         var rangeEndRange = range.cloneRange();
24900         rangeEndRange.collapse(false);
24901     
24902         var nodeStartRange = nodeRange.cloneRange();
24903         nodeStartRange.collapse(true);
24904     
24905         var nodeEndRange = nodeRange.cloneRange();
24906         nodeEndRange.collapse(false);
24907     
24908         return rangeStartRange.compareBoundaryPoints(
24909                  Range.START_TO_START, nodeEndRange) == -1 &&
24910                rangeEndRange.compareBoundaryPoints(
24911                  Range.START_TO_START, nodeStartRange) == 1;
24912         
24913          
24914     },
24915     rangeCompareNode : function(range, node)
24916     {
24917         var nodeRange = node.ownerDocument.createRange();
24918         try {
24919             nodeRange.selectNode(node);
24920         } catch (e) {
24921             nodeRange.selectNodeContents(node);
24922         }
24923         
24924         
24925         range.collapse(true);
24926     
24927         nodeRange.collapse(true);
24928      
24929         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24930         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24931          
24932         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24933         
24934         var nodeIsBefore   =  ss == 1;
24935         var nodeIsAfter    = ee == -1;
24936         
24937         if (nodeIsBefore && nodeIsAfter) {
24938             return 0; // outer
24939         }
24940         if (!nodeIsBefore && nodeIsAfter) {
24941             return 1; //right trailed.
24942         }
24943         
24944         if (nodeIsBefore && !nodeIsAfter) {
24945             return 2;  // left trailed.
24946         }
24947         // fully contined.
24948         return 3;
24949     },
24950
24951     // private? - in a new class?
24952     cleanUpPaste :  function()
24953     {
24954         // cleans up the whole document..
24955         Roo.log('cleanuppaste');
24956         
24957         this.cleanUpChildren(this.doc.body);
24958         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24959         if (clean != this.doc.body.innerHTML) {
24960             this.doc.body.innerHTML = clean;
24961         }
24962         
24963     },
24964     
24965     cleanWordChars : function(input) {// change the chars to hex code
24966         var he = Roo.HtmlEditorCore;
24967         
24968         var output = input;
24969         Roo.each(he.swapCodes, function(sw) { 
24970             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24971             
24972             output = output.replace(swapper, sw[1]);
24973         });
24974         
24975         return output;
24976     },
24977     
24978     
24979     cleanUpChildren : function (n)
24980     {
24981         if (!n.childNodes.length) {
24982             return;
24983         }
24984         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24985            this.cleanUpChild(n.childNodes[i]);
24986         }
24987     },
24988     
24989     
24990         
24991     
24992     cleanUpChild : function (node)
24993     {
24994         var ed = this;
24995         //console.log(node);
24996         if (node.nodeName == "#text") {
24997             // clean up silly Windows -- stuff?
24998             return; 
24999         }
25000         if (node.nodeName == "#comment") {
25001             node.parentNode.removeChild(node);
25002             // clean up silly Windows -- stuff?
25003             return; 
25004         }
25005         var lcname = node.tagName.toLowerCase();
25006         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25007         // whitelist of tags..
25008         
25009         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25010             // remove node.
25011             node.parentNode.removeChild(node);
25012             return;
25013             
25014         }
25015         
25016         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25017         
25018         // spans with no attributes - just remove them..
25019         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25020             remove_keep_children = true;
25021         }
25022         
25023         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25024         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25025         
25026         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25027         //    remove_keep_children = true;
25028         //}
25029         
25030         if (remove_keep_children) {
25031             this.cleanUpChildren(node);
25032             // inserts everything just before this node...
25033             while (node.childNodes.length) {
25034                 var cn = node.childNodes[0];
25035                 node.removeChild(cn);
25036                 node.parentNode.insertBefore(cn, node);
25037             }
25038             node.parentNode.removeChild(node);
25039             return;
25040         }
25041         
25042         if (!node.attributes || !node.attributes.length) {
25043             
25044           
25045             
25046             
25047             this.cleanUpChildren(node);
25048             return;
25049         }
25050         
25051         function cleanAttr(n,v)
25052         {
25053             
25054             if (v.match(/^\./) || v.match(/^\//)) {
25055                 return;
25056             }
25057             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25058                 return;
25059             }
25060             if (v.match(/^#/)) {
25061                 return;
25062             }
25063             if (v.match(/^\{/)) { // allow template editing.
25064                 return;
25065             }
25066 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25067             node.removeAttribute(n);
25068             
25069         }
25070         
25071         var cwhite = this.cwhite;
25072         var cblack = this.cblack;
25073             
25074         function cleanStyle(n,v)
25075         {
25076             if (v.match(/expression/)) { //XSS?? should we even bother..
25077                 node.removeAttribute(n);
25078                 return;
25079             }
25080             
25081             var parts = v.split(/;/);
25082             var clean = [];
25083             
25084             Roo.each(parts, function(p) {
25085                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25086                 if (!p.length) {
25087                     return true;
25088                 }
25089                 var l = p.split(':').shift().replace(/\s+/g,'');
25090                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25091                 
25092                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25093 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25094                     //node.removeAttribute(n);
25095                     return true;
25096                 }
25097                 //Roo.log()
25098                 // only allow 'c whitelisted system attributes'
25099                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25100 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25101                     //node.removeAttribute(n);
25102                     return true;
25103                 }
25104                 
25105                 
25106                  
25107                 
25108                 clean.push(p);
25109                 return true;
25110             });
25111             if (clean.length) { 
25112                 node.setAttribute(n, clean.join(';'));
25113             } else {
25114                 node.removeAttribute(n);
25115             }
25116             
25117         }
25118         
25119         
25120         for (var i = node.attributes.length-1; i > -1 ; i--) {
25121             var a = node.attributes[i];
25122             //console.log(a);
25123             
25124             if (a.name.toLowerCase().substr(0,2)=='on')  {
25125                 node.removeAttribute(a.name);
25126                 continue;
25127             }
25128             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25129                 node.removeAttribute(a.name);
25130                 continue;
25131             }
25132             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25133                 cleanAttr(a.name,a.value); // fixme..
25134                 continue;
25135             }
25136             if (a.name == 'style') {
25137                 cleanStyle(a.name,a.value);
25138                 continue;
25139             }
25140             /// clean up MS crap..
25141             // tecnically this should be a list of valid class'es..
25142             
25143             
25144             if (a.name == 'class') {
25145                 if (a.value.match(/^Mso/)) {
25146                     node.removeAttribute('class');
25147                 }
25148                 
25149                 if (a.value.match(/^body$/)) {
25150                     node.removeAttribute('class');
25151                 }
25152                 continue;
25153             }
25154             
25155             // style cleanup!?
25156             // class cleanup?
25157             
25158         }
25159         
25160         
25161         this.cleanUpChildren(node);
25162         
25163         
25164     },
25165     
25166     /**
25167      * Clean up MS wordisms...
25168      */
25169     cleanWord : function(node)
25170     {
25171         if (!node) {
25172             this.cleanWord(this.doc.body);
25173             return;
25174         }
25175         
25176         if(
25177                 node.nodeName == 'SPAN' &&
25178                 !node.hasAttributes() &&
25179                 node.childNodes.length == 1 &&
25180                 node.firstChild.nodeName == "#text"  
25181         ) {
25182             var textNode = node.firstChild;
25183             node.removeChild(textNode);
25184             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25185                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25186             }
25187             node.parentNode.insertBefore(textNode, node);
25188             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25189                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25190             }
25191             node.parentNode.removeChild(node);
25192         }
25193         
25194         if (node.nodeName == "#text") {
25195             // clean up silly Windows -- stuff?
25196             return; 
25197         }
25198         if (node.nodeName == "#comment") {
25199             node.parentNode.removeChild(node);
25200             // clean up silly Windows -- stuff?
25201             return; 
25202         }
25203         
25204         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25205             node.parentNode.removeChild(node);
25206             return;
25207         }
25208         //Roo.log(node.tagName);
25209         // remove - but keep children..
25210         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25211             //Roo.log('-- removed');
25212             while (node.childNodes.length) {
25213                 var cn = node.childNodes[0];
25214                 node.removeChild(cn);
25215                 node.parentNode.insertBefore(cn, node);
25216                 // move node to parent - and clean it..
25217                 this.cleanWord(cn);
25218             }
25219             node.parentNode.removeChild(node);
25220             /// no need to iterate chidlren = it's got none..
25221             //this.iterateChildren(node, this.cleanWord);
25222             return;
25223         }
25224         // clean styles
25225         if (node.className.length) {
25226             
25227             var cn = node.className.split(/\W+/);
25228             var cna = [];
25229             Roo.each(cn, function(cls) {
25230                 if (cls.match(/Mso[a-zA-Z]+/)) {
25231                     return;
25232                 }
25233                 cna.push(cls);
25234             });
25235             node.className = cna.length ? cna.join(' ') : '';
25236             if (!cna.length) {
25237                 node.removeAttribute("class");
25238             }
25239         }
25240         
25241         if (node.hasAttribute("lang")) {
25242             node.removeAttribute("lang");
25243         }
25244         
25245         if (node.hasAttribute("style")) {
25246             
25247             var styles = node.getAttribute("style").split(";");
25248             var nstyle = [];
25249             Roo.each(styles, function(s) {
25250                 if (!s.match(/:/)) {
25251                     return;
25252                 }
25253                 var kv = s.split(":");
25254                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25255                     return;
25256                 }
25257                 // what ever is left... we allow.
25258                 nstyle.push(s);
25259             });
25260             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25261             if (!nstyle.length) {
25262                 node.removeAttribute('style');
25263             }
25264         }
25265         this.iterateChildren(node, this.cleanWord);
25266         
25267         
25268         
25269     },
25270     /**
25271      * iterateChildren of a Node, calling fn each time, using this as the scole..
25272      * @param {DomNode} node node to iterate children of.
25273      * @param {Function} fn method of this class to call on each item.
25274      */
25275     iterateChildren : function(node, fn)
25276     {
25277         if (!node.childNodes.length) {
25278                 return;
25279         }
25280         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25281            fn.call(this, node.childNodes[i])
25282         }
25283     },
25284     
25285     
25286     /**
25287      * cleanTableWidths.
25288      *
25289      * Quite often pasting from word etc.. results in tables with column and widths.
25290      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25291      *
25292      */
25293     cleanTableWidths : function(node)
25294     {
25295          
25296          
25297         if (!node) {
25298             this.cleanTableWidths(this.doc.body);
25299             return;
25300         }
25301         
25302         // ignore list...
25303         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25304             return; 
25305         }
25306         Roo.log(node.tagName);
25307         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25308             this.iterateChildren(node, this.cleanTableWidths);
25309             return;
25310         }
25311         if (node.hasAttribute('width')) {
25312             node.removeAttribute('width');
25313         }
25314         
25315          
25316         if (node.hasAttribute("style")) {
25317             // pretty basic...
25318             
25319             var styles = node.getAttribute("style").split(";");
25320             var nstyle = [];
25321             Roo.each(styles, function(s) {
25322                 if (!s.match(/:/)) {
25323                     return;
25324                 }
25325                 var kv = s.split(":");
25326                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25327                     return;
25328                 }
25329                 // what ever is left... we allow.
25330                 nstyle.push(s);
25331             });
25332             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25333             if (!nstyle.length) {
25334                 node.removeAttribute('style');
25335             }
25336         }
25337         
25338         this.iterateChildren(node, this.cleanTableWidths);
25339         
25340         
25341     },
25342     
25343     
25344     
25345     
25346     domToHTML : function(currentElement, depth, nopadtext) {
25347         
25348         depth = depth || 0;
25349         nopadtext = nopadtext || false;
25350     
25351         if (!currentElement) {
25352             return this.domToHTML(this.doc.body);
25353         }
25354         
25355         //Roo.log(currentElement);
25356         var j;
25357         var allText = false;
25358         var nodeName = currentElement.nodeName;
25359         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25360         
25361         if  (nodeName == '#text') {
25362             
25363             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25364         }
25365         
25366         
25367         var ret = '';
25368         if (nodeName != 'BODY') {
25369              
25370             var i = 0;
25371             // Prints the node tagName, such as <A>, <IMG>, etc
25372             if (tagName) {
25373                 var attr = [];
25374                 for(i = 0; i < currentElement.attributes.length;i++) {
25375                     // quoting?
25376                     var aname = currentElement.attributes.item(i).name;
25377                     if (!currentElement.attributes.item(i).value.length) {
25378                         continue;
25379                     }
25380                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25381                 }
25382                 
25383                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25384             } 
25385             else {
25386                 
25387                 // eack
25388             }
25389         } else {
25390             tagName = false;
25391         }
25392         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25393             return ret;
25394         }
25395         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25396             nopadtext = true;
25397         }
25398         
25399         
25400         // Traverse the tree
25401         i = 0;
25402         var currentElementChild = currentElement.childNodes.item(i);
25403         var allText = true;
25404         var innerHTML  = '';
25405         lastnode = '';
25406         while (currentElementChild) {
25407             // Formatting code (indent the tree so it looks nice on the screen)
25408             var nopad = nopadtext;
25409             if (lastnode == 'SPAN') {
25410                 nopad  = true;
25411             }
25412             // text
25413             if  (currentElementChild.nodeName == '#text') {
25414                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25415                 toadd = nopadtext ? toadd : toadd.trim();
25416                 if (!nopad && toadd.length > 80) {
25417                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25418                 }
25419                 innerHTML  += toadd;
25420                 
25421                 i++;
25422                 currentElementChild = currentElement.childNodes.item(i);
25423                 lastNode = '';
25424                 continue;
25425             }
25426             allText = false;
25427             
25428             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25429                 
25430             // Recursively traverse the tree structure of the child node
25431             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25432             lastnode = currentElementChild.nodeName;
25433             i++;
25434             currentElementChild=currentElement.childNodes.item(i);
25435         }
25436         
25437         ret += innerHTML;
25438         
25439         if (!allText) {
25440                 // The remaining code is mostly for formatting the tree
25441             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25442         }
25443         
25444         
25445         if (tagName) {
25446             ret+= "</"+tagName+">";
25447         }
25448         return ret;
25449         
25450     },
25451         
25452     applyBlacklists : function()
25453     {
25454         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25455         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25456         
25457         this.white = [];
25458         this.black = [];
25459         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25460             if (b.indexOf(tag) > -1) {
25461                 return;
25462             }
25463             this.white.push(tag);
25464             
25465         }, this);
25466         
25467         Roo.each(w, function(tag) {
25468             if (b.indexOf(tag) > -1) {
25469                 return;
25470             }
25471             if (this.white.indexOf(tag) > -1) {
25472                 return;
25473             }
25474             this.white.push(tag);
25475             
25476         }, this);
25477         
25478         
25479         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25480             if (w.indexOf(tag) > -1) {
25481                 return;
25482             }
25483             this.black.push(tag);
25484             
25485         }, this);
25486         
25487         Roo.each(b, function(tag) {
25488             if (w.indexOf(tag) > -1) {
25489                 return;
25490             }
25491             if (this.black.indexOf(tag) > -1) {
25492                 return;
25493             }
25494             this.black.push(tag);
25495             
25496         }, this);
25497         
25498         
25499         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25500         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25501         
25502         this.cwhite = [];
25503         this.cblack = [];
25504         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25505             if (b.indexOf(tag) > -1) {
25506                 return;
25507             }
25508             this.cwhite.push(tag);
25509             
25510         }, this);
25511         
25512         Roo.each(w, function(tag) {
25513             if (b.indexOf(tag) > -1) {
25514                 return;
25515             }
25516             if (this.cwhite.indexOf(tag) > -1) {
25517                 return;
25518             }
25519             this.cwhite.push(tag);
25520             
25521         }, this);
25522         
25523         
25524         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25525             if (w.indexOf(tag) > -1) {
25526                 return;
25527             }
25528             this.cblack.push(tag);
25529             
25530         }, this);
25531         
25532         Roo.each(b, function(tag) {
25533             if (w.indexOf(tag) > -1) {
25534                 return;
25535             }
25536             if (this.cblack.indexOf(tag) > -1) {
25537                 return;
25538             }
25539             this.cblack.push(tag);
25540             
25541         }, this);
25542     },
25543     
25544     setStylesheets : function(stylesheets)
25545     {
25546         if(typeof(stylesheets) == 'string'){
25547             Roo.get(this.iframe.contentDocument.head).createChild({
25548                 tag : 'link',
25549                 rel : 'stylesheet',
25550                 type : 'text/css',
25551                 href : stylesheets
25552             });
25553             
25554             return;
25555         }
25556         var _this = this;
25557      
25558         Roo.each(stylesheets, function(s) {
25559             if(!s.length){
25560                 return;
25561             }
25562             
25563             Roo.get(_this.iframe.contentDocument.head).createChild({
25564                 tag : 'link',
25565                 rel : 'stylesheet',
25566                 type : 'text/css',
25567                 href : s
25568             });
25569         });
25570
25571         
25572     },
25573     
25574     removeStylesheets : function()
25575     {
25576         var _this = this;
25577         
25578         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25579             s.remove();
25580         });
25581     },
25582     
25583     setStyle : function(style)
25584     {
25585         Roo.get(this.iframe.contentDocument.head).createChild({
25586             tag : 'style',
25587             type : 'text/css',
25588             html : style
25589         });
25590
25591         return;
25592     }
25593     
25594     // hide stuff that is not compatible
25595     /**
25596      * @event blur
25597      * @hide
25598      */
25599     /**
25600      * @event change
25601      * @hide
25602      */
25603     /**
25604      * @event focus
25605      * @hide
25606      */
25607     /**
25608      * @event specialkey
25609      * @hide
25610      */
25611     /**
25612      * @cfg {String} fieldClass @hide
25613      */
25614     /**
25615      * @cfg {String} focusClass @hide
25616      */
25617     /**
25618      * @cfg {String} autoCreate @hide
25619      */
25620     /**
25621      * @cfg {String} inputType @hide
25622      */
25623     /**
25624      * @cfg {String} invalidClass @hide
25625      */
25626     /**
25627      * @cfg {String} invalidText @hide
25628      */
25629     /**
25630      * @cfg {String} msgFx @hide
25631      */
25632     /**
25633      * @cfg {String} validateOnBlur @hide
25634      */
25635 });
25636
25637 Roo.HtmlEditorCore.white = [
25638         'area', 'br', 'img', 'input', 'hr', 'wbr',
25639         
25640        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25641        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25642        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25643        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25644        'table',   'ul',         'xmp', 
25645        
25646        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25647       'thead',   'tr', 
25648      
25649       'dir', 'menu', 'ol', 'ul', 'dl',
25650        
25651       'embed',  'object'
25652 ];
25653
25654
25655 Roo.HtmlEditorCore.black = [
25656     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25657         'applet', // 
25658         'base',   'basefont', 'bgsound', 'blink',  'body', 
25659         'frame',  'frameset', 'head',    'html',   'ilayer', 
25660         'iframe', 'layer',  'link',     'meta',    'object',   
25661         'script', 'style' ,'title',  'xml' // clean later..
25662 ];
25663 Roo.HtmlEditorCore.clean = [
25664     'script', 'style', 'title', 'xml'
25665 ];
25666 Roo.HtmlEditorCore.remove = [
25667     'font'
25668 ];
25669 // attributes..
25670
25671 Roo.HtmlEditorCore.ablack = [
25672     'on'
25673 ];
25674     
25675 Roo.HtmlEditorCore.aclean = [ 
25676     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25677 ];
25678
25679 // protocols..
25680 Roo.HtmlEditorCore.pwhite= [
25681         'http',  'https',  'mailto'
25682 ];
25683
25684 // white listed style attributes.
25685 Roo.HtmlEditorCore.cwhite= [
25686       //  'text-align', /// default is to allow most things..
25687       
25688          
25689 //        'font-size'//??
25690 ];
25691
25692 // black listed style attributes.
25693 Roo.HtmlEditorCore.cblack= [
25694       //  'font-size' -- this can be set by the project 
25695 ];
25696
25697
25698 Roo.HtmlEditorCore.swapCodes   =[ 
25699     [    8211, "--" ], 
25700     [    8212, "--" ], 
25701     [    8216,  "'" ],  
25702     [    8217, "'" ],  
25703     [    8220, '"' ],  
25704     [    8221, '"' ],  
25705     [    8226, "*" ],  
25706     [    8230, "..." ]
25707 ]; 
25708
25709     /*
25710  * - LGPL
25711  *
25712  * HtmlEditor
25713  * 
25714  */
25715
25716 /**
25717  * @class Roo.bootstrap.HtmlEditor
25718  * @extends Roo.bootstrap.TextArea
25719  * Bootstrap HtmlEditor class
25720
25721  * @constructor
25722  * Create a new HtmlEditor
25723  * @param {Object} config The config object
25724  */
25725
25726 Roo.bootstrap.HtmlEditor = function(config){
25727     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25728     if (!this.toolbars) {
25729         this.toolbars = [];
25730     }
25731     
25732     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25733     this.addEvents({
25734             /**
25735              * @event initialize
25736              * Fires when the editor is fully initialized (including the iframe)
25737              * @param {HtmlEditor} this
25738              */
25739             initialize: true,
25740             /**
25741              * @event activate
25742              * Fires when the editor is first receives the focus. Any insertion must wait
25743              * until after this event.
25744              * @param {HtmlEditor} this
25745              */
25746             activate: true,
25747              /**
25748              * @event beforesync
25749              * Fires before the textarea is updated with content from the editor iframe. Return false
25750              * to cancel the sync.
25751              * @param {HtmlEditor} this
25752              * @param {String} html
25753              */
25754             beforesync: true,
25755              /**
25756              * @event beforepush
25757              * Fires before the iframe editor is updated with content from the textarea. Return false
25758              * to cancel the push.
25759              * @param {HtmlEditor} this
25760              * @param {String} html
25761              */
25762             beforepush: true,
25763              /**
25764              * @event sync
25765              * Fires when the textarea is updated with content from the editor iframe.
25766              * @param {HtmlEditor} this
25767              * @param {String} html
25768              */
25769             sync: true,
25770              /**
25771              * @event push
25772              * Fires when the iframe editor is updated with content from the textarea.
25773              * @param {HtmlEditor} this
25774              * @param {String} html
25775              */
25776             push: true,
25777              /**
25778              * @event editmodechange
25779              * Fires when the editor switches edit modes
25780              * @param {HtmlEditor} this
25781              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25782              */
25783             editmodechange: true,
25784             /**
25785              * @event editorevent
25786              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25787              * @param {HtmlEditor} this
25788              */
25789             editorevent: true,
25790             /**
25791              * @event firstfocus
25792              * Fires when on first focus - needed by toolbars..
25793              * @param {HtmlEditor} this
25794              */
25795             firstfocus: true,
25796             /**
25797              * @event autosave
25798              * Auto save the htmlEditor value as a file into Events
25799              * @param {HtmlEditor} this
25800              */
25801             autosave: true,
25802             /**
25803              * @event savedpreview
25804              * preview the saved version of htmlEditor
25805              * @param {HtmlEditor} this
25806              */
25807             savedpreview: true
25808         });
25809 };
25810
25811
25812 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25813     
25814     
25815       /**
25816      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25817      */
25818     toolbars : false,
25819     
25820      /**
25821     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25822     */
25823     btns : [],
25824    
25825      /**
25826      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25827      *                        Roo.resizable.
25828      */
25829     resizable : false,
25830      /**
25831      * @cfg {Number} height (in pixels)
25832      */   
25833     height: 300,
25834    /**
25835      * @cfg {Number} width (in pixels)
25836      */   
25837     width: false,
25838     
25839     /**
25840      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25841      * 
25842      */
25843     stylesheets: false,
25844     
25845     // id of frame..
25846     frameId: false,
25847     
25848     // private properties
25849     validationEvent : false,
25850     deferHeight: true,
25851     initialized : false,
25852     activated : false,
25853     
25854     onFocus : Roo.emptyFn,
25855     iframePad:3,
25856     hideMode:'offsets',
25857     
25858     tbContainer : false,
25859     
25860     bodyCls : '',
25861     
25862     toolbarContainer :function() {
25863         return this.wrap.select('.x-html-editor-tb',true).first();
25864     },
25865
25866     /**
25867      * Protected method that will not generally be called directly. It
25868      * is called when the editor creates its toolbar. Override this method if you need to
25869      * add custom toolbar buttons.
25870      * @param {HtmlEditor} editor
25871      */
25872     createToolbar : function(){
25873         Roo.log('renewing');
25874         Roo.log("create toolbars");
25875         
25876         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25877         this.toolbars[0].render(this.toolbarContainer());
25878         
25879         return;
25880         
25881 //        if (!editor.toolbars || !editor.toolbars.length) {
25882 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25883 //        }
25884 //        
25885 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25886 //            editor.toolbars[i] = Roo.factory(
25887 //                    typeof(editor.toolbars[i]) == 'string' ?
25888 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25889 //                Roo.bootstrap.HtmlEditor);
25890 //            editor.toolbars[i].init(editor);
25891 //        }
25892     },
25893
25894      
25895     // private
25896     onRender : function(ct, position)
25897     {
25898        // Roo.log("Call onRender: " + this.xtype);
25899         var _t = this;
25900         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25901       
25902         this.wrap = this.inputEl().wrap({
25903             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25904         });
25905         
25906         this.editorcore.onRender(ct, position);
25907          
25908         if (this.resizable) {
25909             this.resizeEl = new Roo.Resizable(this.wrap, {
25910                 pinned : true,
25911                 wrap: true,
25912                 dynamic : true,
25913                 minHeight : this.height,
25914                 height: this.height,
25915                 handles : this.resizable,
25916                 width: this.width,
25917                 listeners : {
25918                     resize : function(r, w, h) {
25919                         _t.onResize(w,h); // -something
25920                     }
25921                 }
25922             });
25923             
25924         }
25925         this.createToolbar(this);
25926        
25927         
25928         if(!this.width && this.resizable){
25929             this.setSize(this.wrap.getSize());
25930         }
25931         if (this.resizeEl) {
25932             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25933             // should trigger onReize..
25934         }
25935         
25936     },
25937
25938     // private
25939     onResize : function(w, h)
25940     {
25941         Roo.log('resize: ' +w + ',' + h );
25942         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25943         var ew = false;
25944         var eh = false;
25945         
25946         if(this.inputEl() ){
25947             if(typeof w == 'number'){
25948                 var aw = w - this.wrap.getFrameWidth('lr');
25949                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25950                 ew = aw;
25951             }
25952             if(typeof h == 'number'){
25953                  var tbh = -11;  // fixme it needs to tool bar size!
25954                 for (var i =0; i < this.toolbars.length;i++) {
25955                     // fixme - ask toolbars for heights?
25956                     tbh += this.toolbars[i].el.getHeight();
25957                     //if (this.toolbars[i].footer) {
25958                     //    tbh += this.toolbars[i].footer.el.getHeight();
25959                     //}
25960                 }
25961               
25962                 
25963                 
25964                 
25965                 
25966                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25967                 ah -= 5; // knock a few pixes off for look..
25968                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25969                 var eh = ah;
25970             }
25971         }
25972         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25973         this.editorcore.onResize(ew,eh);
25974         
25975     },
25976
25977     /**
25978      * Toggles the editor between standard and source edit mode.
25979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25980      */
25981     toggleSourceEdit : function(sourceEditMode)
25982     {
25983         this.editorcore.toggleSourceEdit(sourceEditMode);
25984         
25985         if(this.editorcore.sourceEditMode){
25986             Roo.log('editor - showing textarea');
25987             
25988 //            Roo.log('in');
25989 //            Roo.log(this.syncValue());
25990             this.syncValue();
25991             this.inputEl().removeClass(['hide', 'x-hidden']);
25992             this.inputEl().dom.removeAttribute('tabIndex');
25993             this.inputEl().focus();
25994         }else{
25995             Roo.log('editor - hiding textarea');
25996 //            Roo.log('out')
25997 //            Roo.log(this.pushValue()); 
25998             this.pushValue();
25999             
26000             this.inputEl().addClass(['hide', 'x-hidden']);
26001             this.inputEl().dom.setAttribute('tabIndex', -1);
26002             //this.deferFocus();
26003         }
26004          
26005         if(this.resizable){
26006             this.setSize(this.wrap.getSize());
26007         }
26008         
26009         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26010     },
26011  
26012     // private (for BoxComponent)
26013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26014
26015     // private (for BoxComponent)
26016     getResizeEl : function(){
26017         return this.wrap;
26018     },
26019
26020     // private (for BoxComponent)
26021     getPositionEl : function(){
26022         return this.wrap;
26023     },
26024
26025     // private
26026     initEvents : function(){
26027         this.originalValue = this.getValue();
26028     },
26029
26030 //    /**
26031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26032 //     * @method
26033 //     */
26034 //    markInvalid : Roo.emptyFn,
26035 //    /**
26036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26037 //     * @method
26038 //     */
26039 //    clearInvalid : Roo.emptyFn,
26040
26041     setValue : function(v){
26042         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26043         this.editorcore.pushValue();
26044     },
26045
26046      
26047     // private
26048     deferFocus : function(){
26049         this.focus.defer(10, this);
26050     },
26051
26052     // doc'ed in Field
26053     focus : function(){
26054         this.editorcore.focus();
26055         
26056     },
26057       
26058
26059     // private
26060     onDestroy : function(){
26061         
26062         
26063         
26064         if(this.rendered){
26065             
26066             for (var i =0; i < this.toolbars.length;i++) {
26067                 // fixme - ask toolbars for heights?
26068                 this.toolbars[i].onDestroy();
26069             }
26070             
26071             this.wrap.dom.innerHTML = '';
26072             this.wrap.remove();
26073         }
26074     },
26075
26076     // private
26077     onFirstFocus : function(){
26078         //Roo.log("onFirstFocus");
26079         this.editorcore.onFirstFocus();
26080          for (var i =0; i < this.toolbars.length;i++) {
26081             this.toolbars[i].onFirstFocus();
26082         }
26083         
26084     },
26085     
26086     // private
26087     syncValue : function()
26088     {   
26089         this.editorcore.syncValue();
26090     },
26091     
26092     pushValue : function()
26093     {   
26094         this.editorcore.pushValue();
26095     }
26096      
26097     
26098     // hide stuff that is not compatible
26099     /**
26100      * @event blur
26101      * @hide
26102      */
26103     /**
26104      * @event change
26105      * @hide
26106      */
26107     /**
26108      * @event focus
26109      * @hide
26110      */
26111     /**
26112      * @event specialkey
26113      * @hide
26114      */
26115     /**
26116      * @cfg {String} fieldClass @hide
26117      */
26118     /**
26119      * @cfg {String} focusClass @hide
26120      */
26121     /**
26122      * @cfg {String} autoCreate @hide
26123      */
26124     /**
26125      * @cfg {String} inputType @hide
26126      */
26127      
26128     /**
26129      * @cfg {String} invalidText @hide
26130      */
26131     /**
26132      * @cfg {String} msgFx @hide
26133      */
26134     /**
26135      * @cfg {String} validateOnBlur @hide
26136      */
26137 });
26138  
26139     
26140    
26141    
26142    
26143       
26144 Roo.namespace('Roo.bootstrap.htmleditor');
26145 /**
26146  * @class Roo.bootstrap.HtmlEditorToolbar1
26147  * Basic Toolbar
26148  * 
26149  * @example
26150  * Usage:
26151  *
26152  new Roo.bootstrap.HtmlEditor({
26153     ....
26154     toolbars : [
26155         new Roo.bootstrap.HtmlEditorToolbar1({
26156             disable : { fonts: 1 , format: 1, ..., ... , ...],
26157             btns : [ .... ]
26158         })
26159     }
26160      
26161  * 
26162  * @cfg {Object} disable List of elements to disable..
26163  * @cfg {Array} btns List of additional buttons.
26164  * 
26165  * 
26166  * NEEDS Extra CSS? 
26167  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26168  */
26169  
26170 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26171 {
26172     
26173     Roo.apply(this, config);
26174     
26175     // default disabled, based on 'good practice'..
26176     this.disable = this.disable || {};
26177     Roo.applyIf(this.disable, {
26178         fontSize : true,
26179         colors : true,
26180         specialElements : true
26181     });
26182     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26183     
26184     this.editor = config.editor;
26185     this.editorcore = config.editor.editorcore;
26186     
26187     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26188     
26189     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26190     // dont call parent... till later.
26191 }
26192 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26193      
26194     bar : true,
26195     
26196     editor : false,
26197     editorcore : false,
26198     
26199     
26200     formats : [
26201         "p" ,  
26202         "h1","h2","h3","h4","h5","h6", 
26203         "pre", "code", 
26204         "abbr", "acronym", "address", "cite", "samp", "var",
26205         'div','span'
26206     ],
26207     
26208     onRender : function(ct, position)
26209     {
26210        // Roo.log("Call onRender: " + this.xtype);
26211         
26212        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26213        Roo.log(this.el);
26214        this.el.dom.style.marginBottom = '0';
26215        var _this = this;
26216        var editorcore = this.editorcore;
26217        var editor= this.editor;
26218        
26219        var children = [];
26220        var btn = function(id,cmd , toggle, handler, html){
26221        
26222             var  event = toggle ? 'toggle' : 'click';
26223        
26224             var a = {
26225                 size : 'sm',
26226                 xtype: 'Button',
26227                 xns: Roo.bootstrap,
26228                 //glyphicon : id,
26229                 fa: id,
26230                 cmd : id || cmd,
26231                 enableToggle:toggle !== false,
26232                 html : html || '',
26233                 pressed : toggle ? false : null,
26234                 listeners : {}
26235             };
26236             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26237                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26238             };
26239             children.push(a);
26240             return a;
26241        }
26242        
26243     //    var cb_box = function...
26244         
26245         var style = {
26246                 xtype: 'Button',
26247                 size : 'sm',
26248                 xns: Roo.bootstrap,
26249                 fa : 'font',
26250                 //html : 'submit'
26251                 menu : {
26252                     xtype: 'Menu',
26253                     xns: Roo.bootstrap,
26254                     items:  []
26255                 }
26256         };
26257         Roo.each(this.formats, function(f) {
26258             style.menu.items.push({
26259                 xtype :'MenuItem',
26260                 xns: Roo.bootstrap,
26261                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26262                 tagname : f,
26263                 listeners : {
26264                     click : function()
26265                     {
26266                         editorcore.insertTag(this.tagname);
26267                         editor.focus();
26268                     }
26269                 }
26270                 
26271             });
26272         });
26273         children.push(style);   
26274         
26275         btn('bold',false,true);
26276         btn('italic',false,true);
26277         btn('align-left', 'justifyleft',true);
26278         btn('align-center', 'justifycenter',true);
26279         btn('align-right' , 'justifyright',true);
26280         btn('link', false, false, function(btn) {
26281             //Roo.log("create link?");
26282             var url = prompt(this.createLinkText, this.defaultLinkValue);
26283             if(url && url != 'http:/'+'/'){
26284                 this.editorcore.relayCmd('createlink', url);
26285             }
26286         }),
26287         btn('list','insertunorderedlist',true);
26288         btn('pencil', false,true, function(btn){
26289                 Roo.log(this);
26290                 this.toggleSourceEdit(btn.pressed);
26291         });
26292         
26293         if (this.editor.btns.length > 0) {
26294             for (var i = 0; i<this.editor.btns.length; i++) {
26295                 children.push(this.editor.btns[i]);
26296             }
26297         }
26298         
26299         /*
26300         var cog = {
26301                 xtype: 'Button',
26302                 size : 'sm',
26303                 xns: Roo.bootstrap,
26304                 glyphicon : 'cog',
26305                 //html : 'submit'
26306                 menu : {
26307                     xtype: 'Menu',
26308                     xns: Roo.bootstrap,
26309                     items:  []
26310                 }
26311         };
26312         
26313         cog.menu.items.push({
26314             xtype :'MenuItem',
26315             xns: Roo.bootstrap,
26316             html : Clean styles,
26317             tagname : f,
26318             listeners : {
26319                 click : function()
26320                 {
26321                     editorcore.insertTag(this.tagname);
26322                     editor.focus();
26323                 }
26324             }
26325             
26326         });
26327        */
26328         
26329          
26330        this.xtype = 'NavSimplebar';
26331         
26332         for(var i=0;i< children.length;i++) {
26333             
26334             this.buttons.add(this.addxtypeChild(children[i]));
26335             
26336         }
26337         
26338         editor.on('editorevent', this.updateToolbar, this);
26339     },
26340     onBtnClick : function(id)
26341     {
26342        this.editorcore.relayCmd(id);
26343        this.editorcore.focus();
26344     },
26345     
26346     /**
26347      * Protected method that will not generally be called directly. It triggers
26348      * a toolbar update by reading the markup state of the current selection in the editor.
26349      */
26350     updateToolbar: function(){
26351
26352         if(!this.editorcore.activated){
26353             this.editor.onFirstFocus(); // is this neeed?
26354             return;
26355         }
26356
26357         var btns = this.buttons; 
26358         var doc = this.editorcore.doc;
26359         btns.get('bold').setActive(doc.queryCommandState('bold'));
26360         btns.get('italic').setActive(doc.queryCommandState('italic'));
26361         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26362         
26363         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26364         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26365         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26366         
26367         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26368         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26369          /*
26370         
26371         var ans = this.editorcore.getAllAncestors();
26372         if (this.formatCombo) {
26373             
26374             
26375             var store = this.formatCombo.store;
26376             this.formatCombo.setValue("");
26377             for (var i =0; i < ans.length;i++) {
26378                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26379                     // select it..
26380                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26381                     break;
26382                 }
26383             }
26384         }
26385         
26386         
26387         
26388         // hides menus... - so this cant be on a menu...
26389         Roo.bootstrap.MenuMgr.hideAll();
26390         */
26391         Roo.bootstrap.MenuMgr.hideAll();
26392         //this.editorsyncValue();
26393     },
26394     onFirstFocus: function() {
26395         this.buttons.each(function(item){
26396            item.enable();
26397         });
26398     },
26399     toggleSourceEdit : function(sourceEditMode){
26400         
26401           
26402         if(sourceEditMode){
26403             Roo.log("disabling buttons");
26404            this.buttons.each( function(item){
26405                 if(item.cmd != 'pencil'){
26406                     item.disable();
26407                 }
26408             });
26409           
26410         }else{
26411             Roo.log("enabling buttons");
26412             if(this.editorcore.initialized){
26413                 this.buttons.each( function(item){
26414                     item.enable();
26415                 });
26416             }
26417             
26418         }
26419         Roo.log("calling toggole on editor");
26420         // tell the editor that it's been pressed..
26421         this.editor.toggleSourceEdit(sourceEditMode);
26422        
26423     }
26424 });
26425
26426
26427
26428
26429  
26430 /*
26431  * - LGPL
26432  */
26433
26434 /**
26435  * @class Roo.bootstrap.Markdown
26436  * @extends Roo.bootstrap.TextArea
26437  * Bootstrap Showdown editable area
26438  * @cfg {string} content
26439  * 
26440  * @constructor
26441  * Create a new Showdown
26442  */
26443
26444 Roo.bootstrap.Markdown = function(config){
26445     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26446    
26447 };
26448
26449 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26450     
26451     editing :false,
26452     
26453     initEvents : function()
26454     {
26455         
26456         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26457         this.markdownEl = this.el.createChild({
26458             cls : 'roo-markdown-area'
26459         });
26460         this.inputEl().addClass('d-none');
26461         if (this.getValue() == '') {
26462             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26463             
26464         } else {
26465             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26466         }
26467         this.markdownEl.on('click', this.toggleTextEdit, this);
26468         this.on('blur', this.toggleTextEdit, this);
26469         this.on('specialkey', this.resizeTextArea, this);
26470     },
26471     
26472     toggleTextEdit : function()
26473     {
26474         var sh = this.markdownEl.getHeight();
26475         this.inputEl().addClass('d-none');
26476         this.markdownEl.addClass('d-none');
26477         if (!this.editing) {
26478             // show editor?
26479             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26480             this.inputEl().removeClass('d-none');
26481             this.inputEl().focus();
26482             this.editing = true;
26483             return;
26484         }
26485         // show showdown...
26486         this.updateMarkdown();
26487         this.markdownEl.removeClass('d-none');
26488         this.editing = false;
26489         return;
26490     },
26491     updateMarkdown : function()
26492     {
26493         if (this.getValue() == '') {
26494             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26495             return;
26496         }
26497  
26498         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26499     },
26500     
26501     resizeTextArea: function () {
26502         
26503         var sh = 100;
26504         Roo.log([sh, this.getValue().split("\n").length * 30]);
26505         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26506     },
26507     setValue : function(val)
26508     {
26509         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26510         if (!this.editing) {
26511             this.updateMarkdown();
26512         }
26513         
26514     },
26515     focus : function()
26516     {
26517         if (!this.editing) {
26518             this.toggleTextEdit();
26519         }
26520         
26521     }
26522
26523
26524 });
26525 /**
26526  * @class Roo.bootstrap.Table.AbstractSelectionModel
26527  * @extends Roo.util.Observable
26528  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26529  * implemented by descendant classes.  This class should not be directly instantiated.
26530  * @constructor
26531  */
26532 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26533     this.locked = false;
26534     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26535 };
26536
26537
26538 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26539     /** @ignore Called by the grid automatically. Do not call directly. */
26540     init : function(grid){
26541         this.grid = grid;
26542         this.initEvents();
26543     },
26544
26545     /**
26546      * Locks the selections.
26547      */
26548     lock : function(){
26549         this.locked = true;
26550     },
26551
26552     /**
26553      * Unlocks the selections.
26554      */
26555     unlock : function(){
26556         this.locked = false;
26557     },
26558
26559     /**
26560      * Returns true if the selections are locked.
26561      * @return {Boolean}
26562      */
26563     isLocked : function(){
26564         return this.locked;
26565     },
26566     
26567     
26568     initEvents : function ()
26569     {
26570         
26571     }
26572 });
26573 /**
26574  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26575  * @class Roo.bootstrap.Table.RowSelectionModel
26576  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26577  * It supports multiple selections and keyboard selection/navigation. 
26578  * @constructor
26579  * @param {Object} config
26580  */
26581
26582 Roo.bootstrap.Table.RowSelectionModel = function(config){
26583     Roo.apply(this, config);
26584     this.selections = new Roo.util.MixedCollection(false, function(o){
26585         return o.id;
26586     });
26587
26588     this.last = false;
26589     this.lastActive = false;
26590
26591     this.addEvents({
26592         /**
26593              * @event selectionchange
26594              * Fires when the selection changes
26595              * @param {SelectionModel} this
26596              */
26597             "selectionchange" : true,
26598         /**
26599              * @event afterselectionchange
26600              * Fires after the selection changes (eg. by key press or clicking)
26601              * @param {SelectionModel} this
26602              */
26603             "afterselectionchange" : true,
26604         /**
26605              * @event beforerowselect
26606              * Fires when a row is selected being selected, return false to cancel.
26607              * @param {SelectionModel} this
26608              * @param {Number} rowIndex The selected index
26609              * @param {Boolean} keepExisting False if other selections will be cleared
26610              */
26611             "beforerowselect" : true,
26612         /**
26613              * @event rowselect
26614              * Fires when a row is selected.
26615              * @param {SelectionModel} this
26616              * @param {Number} rowIndex The selected index
26617              * @param {Roo.data.Record} r The record
26618              */
26619             "rowselect" : true,
26620         /**
26621              * @event rowdeselect
26622              * Fires when a row is deselected.
26623              * @param {SelectionModel} this
26624              * @param {Number} rowIndex The selected index
26625              */
26626         "rowdeselect" : true
26627     });
26628     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26629     this.locked = false;
26630  };
26631
26632 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26633     /**
26634      * @cfg {Boolean} singleSelect
26635      * True to allow selection of only one row at a time (defaults to false)
26636      */
26637     singleSelect : false,
26638
26639     // private
26640     initEvents : function()
26641     {
26642
26643         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26644         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26645         //}else{ // allow click to work like normal
26646          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26647         //}
26648         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26649         this.grid.on("rowclick", this.handleMouseDown, this);
26650         
26651         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26652             "up" : function(e){
26653                 if(!e.shiftKey){
26654                     this.selectPrevious(e.shiftKey);
26655                 }else if(this.last !== false && this.lastActive !== false){
26656                     var last = this.last;
26657                     this.selectRange(this.last,  this.lastActive-1);
26658                     this.grid.getView().focusRow(this.lastActive);
26659                     if(last !== false){
26660                         this.last = last;
26661                     }
26662                 }else{
26663                     this.selectFirstRow();
26664                 }
26665                 this.fireEvent("afterselectionchange", this);
26666             },
26667             "down" : function(e){
26668                 if(!e.shiftKey){
26669                     this.selectNext(e.shiftKey);
26670                 }else if(this.last !== false && this.lastActive !== false){
26671                     var last = this.last;
26672                     this.selectRange(this.last,  this.lastActive+1);
26673                     this.grid.getView().focusRow(this.lastActive);
26674                     if(last !== false){
26675                         this.last = last;
26676                     }
26677                 }else{
26678                     this.selectFirstRow();
26679                 }
26680                 this.fireEvent("afterselectionchange", this);
26681             },
26682             scope: this
26683         });
26684         this.grid.store.on('load', function(){
26685             this.selections.clear();
26686         },this);
26687         /*
26688         var view = this.grid.view;
26689         view.on("refresh", this.onRefresh, this);
26690         view.on("rowupdated", this.onRowUpdated, this);
26691         view.on("rowremoved", this.onRemove, this);
26692         */
26693     },
26694
26695     // private
26696     onRefresh : function()
26697     {
26698         var ds = this.grid.store, i, v = this.grid.view;
26699         var s = this.selections;
26700         s.each(function(r){
26701             if((i = ds.indexOfId(r.id)) != -1){
26702                 v.onRowSelect(i);
26703             }else{
26704                 s.remove(r);
26705             }
26706         });
26707     },
26708
26709     // private
26710     onRemove : function(v, index, r){
26711         this.selections.remove(r);
26712     },
26713
26714     // private
26715     onRowUpdated : function(v, index, r){
26716         if(this.isSelected(r)){
26717             v.onRowSelect(index);
26718         }
26719     },
26720
26721     /**
26722      * Select records.
26723      * @param {Array} records The records to select
26724      * @param {Boolean} keepExisting (optional) True to keep existing selections
26725      */
26726     selectRecords : function(records, keepExisting)
26727     {
26728         if(!keepExisting){
26729             this.clearSelections();
26730         }
26731             var ds = this.grid.store;
26732         for(var i = 0, len = records.length; i < len; i++){
26733             this.selectRow(ds.indexOf(records[i]), true);
26734         }
26735     },
26736
26737     /**
26738      * Gets the number of selected rows.
26739      * @return {Number}
26740      */
26741     getCount : function(){
26742         return this.selections.length;
26743     },
26744
26745     /**
26746      * Selects the first row in the grid.
26747      */
26748     selectFirstRow : function(){
26749         this.selectRow(0);
26750     },
26751
26752     /**
26753      * Select the last row.
26754      * @param {Boolean} keepExisting (optional) True to keep existing selections
26755      */
26756     selectLastRow : function(keepExisting){
26757         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26758         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26759     },
26760
26761     /**
26762      * Selects the row immediately following the last selected row.
26763      * @param {Boolean} keepExisting (optional) True to keep existing selections
26764      */
26765     selectNext : function(keepExisting)
26766     {
26767             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26768             this.selectRow(this.last+1, keepExisting);
26769             this.grid.getView().focusRow(this.last);
26770         }
26771     },
26772
26773     /**
26774      * Selects the row that precedes the last selected row.
26775      * @param {Boolean} keepExisting (optional) True to keep existing selections
26776      */
26777     selectPrevious : function(keepExisting){
26778         if(this.last){
26779             this.selectRow(this.last-1, keepExisting);
26780             this.grid.getView().focusRow(this.last);
26781         }
26782     },
26783
26784     /**
26785      * Returns the selected records
26786      * @return {Array} Array of selected records
26787      */
26788     getSelections : function(){
26789         return [].concat(this.selections.items);
26790     },
26791
26792     /**
26793      * Returns the first selected record.
26794      * @return {Record}
26795      */
26796     getSelected : function(){
26797         return this.selections.itemAt(0);
26798     },
26799
26800
26801     /**
26802      * Clears all selections.
26803      */
26804     clearSelections : function(fast)
26805     {
26806         if(this.locked) {
26807             return;
26808         }
26809         if(fast !== true){
26810                 var ds = this.grid.store;
26811             var s = this.selections;
26812             s.each(function(r){
26813                 this.deselectRow(ds.indexOfId(r.id));
26814             }, this);
26815             s.clear();
26816         }else{
26817             this.selections.clear();
26818         }
26819         this.last = false;
26820     },
26821
26822
26823     /**
26824      * Selects all rows.
26825      */
26826     selectAll : function(){
26827         if(this.locked) {
26828             return;
26829         }
26830         this.selections.clear();
26831         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26832             this.selectRow(i, true);
26833         }
26834     },
26835
26836     /**
26837      * Returns True if there is a selection.
26838      * @return {Boolean}
26839      */
26840     hasSelection : function(){
26841         return this.selections.length > 0;
26842     },
26843
26844     /**
26845      * Returns True if the specified row is selected.
26846      * @param {Number/Record} record The record or index of the record to check
26847      * @return {Boolean}
26848      */
26849     isSelected : function(index){
26850             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26851         return (r && this.selections.key(r.id) ? true : false);
26852     },
26853
26854     /**
26855      * Returns True if the specified record id is selected.
26856      * @param {String} id The id of record to check
26857      * @return {Boolean}
26858      */
26859     isIdSelected : function(id){
26860         return (this.selections.key(id) ? true : false);
26861     },
26862
26863
26864     // private
26865     handleMouseDBClick : function(e, t){
26866         
26867     },
26868     // private
26869     handleMouseDown : function(e, t)
26870     {
26871             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26872         if(this.isLocked() || rowIndex < 0 ){
26873             return;
26874         };
26875         if(e.shiftKey && this.last !== false){
26876             var last = this.last;
26877             this.selectRange(last, rowIndex, e.ctrlKey);
26878             this.last = last; // reset the last
26879             t.focus();
26880     
26881         }else{
26882             var isSelected = this.isSelected(rowIndex);
26883             //Roo.log("select row:" + rowIndex);
26884             if(isSelected){
26885                 this.deselectRow(rowIndex);
26886             } else {
26887                         this.selectRow(rowIndex, true);
26888             }
26889     
26890             /*
26891                 if(e.button !== 0 && isSelected){
26892                 alert('rowIndex 2: ' + rowIndex);
26893                     view.focusRow(rowIndex);
26894                 }else if(e.ctrlKey && isSelected){
26895                     this.deselectRow(rowIndex);
26896                 }else if(!isSelected){
26897                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26898                     view.focusRow(rowIndex);
26899                 }
26900             */
26901         }
26902         this.fireEvent("afterselectionchange", this);
26903     },
26904     // private
26905     handleDragableRowClick :  function(grid, rowIndex, e) 
26906     {
26907         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26908             this.selectRow(rowIndex, false);
26909             grid.view.focusRow(rowIndex);
26910              this.fireEvent("afterselectionchange", this);
26911         }
26912     },
26913     
26914     /**
26915      * Selects multiple rows.
26916      * @param {Array} rows Array of the indexes of the row to select
26917      * @param {Boolean} keepExisting (optional) True to keep existing selections
26918      */
26919     selectRows : function(rows, keepExisting){
26920         if(!keepExisting){
26921             this.clearSelections();
26922         }
26923         for(var i = 0, len = rows.length; i < len; i++){
26924             this.selectRow(rows[i], true);
26925         }
26926     },
26927
26928     /**
26929      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26930      * @param {Number} startRow The index of the first row in the range
26931      * @param {Number} endRow The index of the last row in the range
26932      * @param {Boolean} keepExisting (optional) True to retain existing selections
26933      */
26934     selectRange : function(startRow, endRow, keepExisting){
26935         if(this.locked) {
26936             return;
26937         }
26938         if(!keepExisting){
26939             this.clearSelections();
26940         }
26941         if(startRow <= endRow){
26942             for(var i = startRow; i <= endRow; i++){
26943                 this.selectRow(i, true);
26944             }
26945         }else{
26946             for(var i = startRow; i >= endRow; i--){
26947                 this.selectRow(i, true);
26948             }
26949         }
26950     },
26951
26952     /**
26953      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26954      * @param {Number} startRow The index of the first row in the range
26955      * @param {Number} endRow The index of the last row in the range
26956      */
26957     deselectRange : function(startRow, endRow, preventViewNotify){
26958         if(this.locked) {
26959             return;
26960         }
26961         for(var i = startRow; i <= endRow; i++){
26962             this.deselectRow(i, preventViewNotify);
26963         }
26964     },
26965
26966     /**
26967      * Selects a row.
26968      * @param {Number} row The index of the row to select
26969      * @param {Boolean} keepExisting (optional) True to keep existing selections
26970      */
26971     selectRow : function(index, keepExisting, preventViewNotify)
26972     {
26973             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26974             return;
26975         }
26976         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26977             if(!keepExisting || this.singleSelect){
26978                 this.clearSelections();
26979             }
26980             
26981             var r = this.grid.store.getAt(index);
26982             //console.log('selectRow - record id :' + r.id);
26983             
26984             this.selections.add(r);
26985             this.last = this.lastActive = index;
26986             if(!preventViewNotify){
26987                 var proxy = new Roo.Element(
26988                                 this.grid.getRowDom(index)
26989                 );
26990                 proxy.addClass('bg-info info');
26991             }
26992             this.fireEvent("rowselect", this, index, r);
26993             this.fireEvent("selectionchange", this);
26994         }
26995     },
26996
26997     /**
26998      * Deselects a row.
26999      * @param {Number} row The index of the row to deselect
27000      */
27001     deselectRow : function(index, preventViewNotify)
27002     {
27003         if(this.locked) {
27004             return;
27005         }
27006         if(this.last == index){
27007             this.last = false;
27008         }
27009         if(this.lastActive == index){
27010             this.lastActive = false;
27011         }
27012         
27013         var r = this.grid.store.getAt(index);
27014         if (!r) {
27015             return;
27016         }
27017         
27018         this.selections.remove(r);
27019         //.console.log('deselectRow - record id :' + r.id);
27020         if(!preventViewNotify){
27021         
27022             var proxy = new Roo.Element(
27023                 this.grid.getRowDom(index)
27024             );
27025             proxy.removeClass('bg-info info');
27026         }
27027         this.fireEvent("rowdeselect", this, index);
27028         this.fireEvent("selectionchange", this);
27029     },
27030
27031     // private
27032     restoreLast : function(){
27033         if(this._last){
27034             this.last = this._last;
27035         }
27036     },
27037
27038     // private
27039     acceptsNav : function(row, col, cm){
27040         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27041     },
27042
27043     // private
27044     onEditorKey : function(field, e){
27045         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27046         if(k == e.TAB){
27047             e.stopEvent();
27048             ed.completeEdit();
27049             if(e.shiftKey){
27050                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27051             }else{
27052                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27053             }
27054         }else if(k == e.ENTER && !e.ctrlKey){
27055             e.stopEvent();
27056             ed.completeEdit();
27057             if(e.shiftKey){
27058                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27059             }else{
27060                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27061             }
27062         }else if(k == e.ESC){
27063             ed.cancelEdit();
27064         }
27065         if(newCell){
27066             g.startEditing(newCell[0], newCell[1]);
27067         }
27068     }
27069 });
27070 /*
27071  * Based on:
27072  * Ext JS Library 1.1.1
27073  * Copyright(c) 2006-2007, Ext JS, LLC.
27074  *
27075  * Originally Released Under LGPL - original licence link has changed is not relivant.
27076  *
27077  * Fork - LGPL
27078  * <script type="text/javascript">
27079  */
27080  
27081 /**
27082  * @class Roo.bootstrap.PagingToolbar
27083  * @extends Roo.bootstrap.NavSimplebar
27084  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27085  * @constructor
27086  * Create a new PagingToolbar
27087  * @param {Object} config The config object
27088  * @param {Roo.data.Store} store
27089  */
27090 Roo.bootstrap.PagingToolbar = function(config)
27091 {
27092     // old args format still supported... - xtype is prefered..
27093         // created from xtype...
27094     
27095     this.ds = config.dataSource;
27096     
27097     if (config.store && !this.ds) {
27098         this.store= Roo.factory(config.store, Roo.data);
27099         this.ds = this.store;
27100         this.ds.xmodule = this.xmodule || false;
27101     }
27102     
27103     this.toolbarItems = [];
27104     if (config.items) {
27105         this.toolbarItems = config.items;
27106     }
27107     
27108     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27109     
27110     this.cursor = 0;
27111     
27112     if (this.ds) { 
27113         this.bind(this.ds);
27114     }
27115     
27116     if (Roo.bootstrap.version == 4) {
27117         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27118     } else {
27119         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27120     }
27121     
27122 };
27123
27124 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27125     /**
27126      * @cfg {Roo.data.Store} dataSource
27127      * The underlying data store providing the paged data
27128      */
27129     /**
27130      * @cfg {String/HTMLElement/Element} container
27131      * container The id or element that will contain the toolbar
27132      */
27133     /**
27134      * @cfg {Boolean} displayInfo
27135      * True to display the displayMsg (defaults to false)
27136      */
27137     /**
27138      * @cfg {Number} pageSize
27139      * The number of records to display per page (defaults to 20)
27140      */
27141     pageSize: 20,
27142     /**
27143      * @cfg {String} displayMsg
27144      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27145      */
27146     displayMsg : 'Displaying {0} - {1} of {2}',
27147     /**
27148      * @cfg {String} emptyMsg
27149      * The message to display when no records are found (defaults to "No data to display")
27150      */
27151     emptyMsg : 'No data to display',
27152     /**
27153      * Customizable piece of the default paging text (defaults to "Page")
27154      * @type String
27155      */
27156     beforePageText : "Page",
27157     /**
27158      * Customizable piece of the default paging text (defaults to "of %0")
27159      * @type String
27160      */
27161     afterPageText : "of {0}",
27162     /**
27163      * Customizable piece of the default paging text (defaults to "First Page")
27164      * @type String
27165      */
27166     firstText : "First Page",
27167     /**
27168      * Customizable piece of the default paging text (defaults to "Previous Page")
27169      * @type String
27170      */
27171     prevText : "Previous Page",
27172     /**
27173      * Customizable piece of the default paging text (defaults to "Next Page")
27174      * @type String
27175      */
27176     nextText : "Next Page",
27177     /**
27178      * Customizable piece of the default paging text (defaults to "Last Page")
27179      * @type String
27180      */
27181     lastText : "Last Page",
27182     /**
27183      * Customizable piece of the default paging text (defaults to "Refresh")
27184      * @type String
27185      */
27186     refreshText : "Refresh",
27187
27188     buttons : false,
27189     // private
27190     onRender : function(ct, position) 
27191     {
27192         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27193         this.navgroup.parentId = this.id;
27194         this.navgroup.onRender(this.el, null);
27195         // add the buttons to the navgroup
27196         
27197         if(this.displayInfo){
27198             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27199             this.displayEl = this.el.select('.x-paging-info', true).first();
27200 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27201 //            this.displayEl = navel.el.select('span',true).first();
27202         }
27203         
27204         var _this = this;
27205         
27206         if(this.buttons){
27207             Roo.each(_this.buttons, function(e){ // this might need to use render????
27208                Roo.factory(e).render(_this.el);
27209             });
27210         }
27211             
27212         Roo.each(_this.toolbarItems, function(e) {
27213             _this.navgroup.addItem(e);
27214         });
27215         
27216         
27217         this.first = this.navgroup.addItem({
27218             tooltip: this.firstText,
27219             cls: "prev btn-outline-secondary",
27220             html : ' <i class="fa fa-step-backward"></i>',
27221             disabled: true,
27222             preventDefault: true,
27223             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27224         });
27225         
27226         this.prev =  this.navgroup.addItem({
27227             tooltip: this.prevText,
27228             cls: "prev btn-outline-secondary",
27229             html : ' <i class="fa fa-backward"></i>',
27230             disabled: true,
27231             preventDefault: true,
27232             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27233         });
27234     //this.addSeparator();
27235         
27236         
27237         var field = this.navgroup.addItem( {
27238             tagtype : 'span',
27239             cls : 'x-paging-position  btn-outline-secondary',
27240              disabled: true,
27241             html : this.beforePageText  +
27242                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27243                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27244          } ); //?? escaped?
27245         
27246         this.field = field.el.select('input', true).first();
27247         this.field.on("keydown", this.onPagingKeydown, this);
27248         this.field.on("focus", function(){this.dom.select();});
27249     
27250     
27251         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27252         //this.field.setHeight(18);
27253         //this.addSeparator();
27254         this.next = this.navgroup.addItem({
27255             tooltip: this.nextText,
27256             cls: "next btn-outline-secondary",
27257             html : ' <i class="fa fa-forward"></i>',
27258             disabled: true,
27259             preventDefault: true,
27260             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27261         });
27262         this.last = this.navgroup.addItem({
27263             tooltip: this.lastText,
27264             html : ' <i class="fa fa-step-forward"></i>',
27265             cls: "next btn-outline-secondary",
27266             disabled: true,
27267             preventDefault: true,
27268             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27269         });
27270     //this.addSeparator();
27271         this.loading = this.navgroup.addItem({
27272             tooltip: this.refreshText,
27273             cls: "btn-outline-secondary",
27274             html : ' <i class="fa fa-refresh"></i>',
27275             preventDefault: true,
27276             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27277         });
27278         
27279     },
27280
27281     // private
27282     updateInfo : function(){
27283         if(this.displayEl){
27284             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27285             var msg = count == 0 ?
27286                 this.emptyMsg :
27287                 String.format(
27288                     this.displayMsg,
27289                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27290                 );
27291             this.displayEl.update(msg);
27292         }
27293     },
27294
27295     // private
27296     onLoad : function(ds, r, o)
27297     {
27298         this.cursor = o.params.start ? o.params.start : 0;
27299         
27300         var d = this.getPageData(),
27301             ap = d.activePage,
27302             ps = d.pages;
27303         
27304         
27305         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27306         this.field.dom.value = ap;
27307         this.first.setDisabled(ap == 1);
27308         this.prev.setDisabled(ap == 1);
27309         this.next.setDisabled(ap == ps);
27310         this.last.setDisabled(ap == ps);
27311         this.loading.enable();
27312         this.updateInfo();
27313     },
27314
27315     // private
27316     getPageData : function(){
27317         var total = this.ds.getTotalCount();
27318         return {
27319             total : total,
27320             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27321             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27322         };
27323     },
27324
27325     // private
27326     onLoadError : function(){
27327         this.loading.enable();
27328     },
27329
27330     // private
27331     onPagingKeydown : function(e){
27332         var k = e.getKey();
27333         var d = this.getPageData();
27334         if(k == e.RETURN){
27335             var v = this.field.dom.value, pageNum;
27336             if(!v || isNaN(pageNum = parseInt(v, 10))){
27337                 this.field.dom.value = d.activePage;
27338                 return;
27339             }
27340             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27341             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27342             e.stopEvent();
27343         }
27344         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))
27345         {
27346           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27347           this.field.dom.value = pageNum;
27348           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27349           e.stopEvent();
27350         }
27351         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27352         {
27353           var v = this.field.dom.value, pageNum; 
27354           var increment = (e.shiftKey) ? 10 : 1;
27355           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27356                 increment *= -1;
27357           }
27358           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27359             this.field.dom.value = d.activePage;
27360             return;
27361           }
27362           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27363           {
27364             this.field.dom.value = parseInt(v, 10) + increment;
27365             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27366             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27367           }
27368           e.stopEvent();
27369         }
27370     },
27371
27372     // private
27373     beforeLoad : function(){
27374         if(this.loading){
27375             this.loading.disable();
27376         }
27377     },
27378
27379     // private
27380     onClick : function(which){
27381         
27382         var ds = this.ds;
27383         if (!ds) {
27384             return;
27385         }
27386         
27387         switch(which){
27388             case "first":
27389                 ds.load({params:{start: 0, limit: this.pageSize}});
27390             break;
27391             case "prev":
27392                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27393             break;
27394             case "next":
27395                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27396             break;
27397             case "last":
27398                 var total = ds.getTotalCount();
27399                 var extra = total % this.pageSize;
27400                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27401                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27402             break;
27403             case "refresh":
27404                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27405             break;
27406         }
27407     },
27408
27409     /**
27410      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27411      * @param {Roo.data.Store} store The data store to unbind
27412      */
27413     unbind : function(ds){
27414         ds.un("beforeload", this.beforeLoad, this);
27415         ds.un("load", this.onLoad, this);
27416         ds.un("loadexception", this.onLoadError, this);
27417         ds.un("remove", this.updateInfo, this);
27418         ds.un("add", this.updateInfo, this);
27419         this.ds = undefined;
27420     },
27421
27422     /**
27423      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27424      * @param {Roo.data.Store} store The data store to bind
27425      */
27426     bind : function(ds){
27427         ds.on("beforeload", this.beforeLoad, this);
27428         ds.on("load", this.onLoad, this);
27429         ds.on("loadexception", this.onLoadError, this);
27430         ds.on("remove", this.updateInfo, this);
27431         ds.on("add", this.updateInfo, this);
27432         this.ds = ds;
27433     }
27434 });/*
27435  * - LGPL
27436  *
27437  * element
27438  * 
27439  */
27440
27441 /**
27442  * @class Roo.bootstrap.MessageBar
27443  * @extends Roo.bootstrap.Component
27444  * Bootstrap MessageBar class
27445  * @cfg {String} html contents of the MessageBar
27446  * @cfg {String} weight (info | success | warning | danger) default info
27447  * @cfg {String} beforeClass insert the bar before the given class
27448  * @cfg {Boolean} closable (true | false) default false
27449  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27450  * 
27451  * @constructor
27452  * Create a new Element
27453  * @param {Object} config The config object
27454  */
27455
27456 Roo.bootstrap.MessageBar = function(config){
27457     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27458 };
27459
27460 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27461     
27462     html: '',
27463     weight: 'info',
27464     closable: false,
27465     fixed: false,
27466     beforeClass: 'bootstrap-sticky-wrap',
27467     
27468     getAutoCreate : function(){
27469         
27470         var cfg = {
27471             tag: 'div',
27472             cls: 'alert alert-dismissable alert-' + this.weight,
27473             cn: [
27474                 {
27475                     tag: 'span',
27476                     cls: 'message',
27477                     html: this.html || ''
27478                 }
27479             ]
27480         };
27481         
27482         if(this.fixed){
27483             cfg.cls += ' alert-messages-fixed';
27484         }
27485         
27486         if(this.closable){
27487             cfg.cn.push({
27488                 tag: 'button',
27489                 cls: 'close',
27490                 html: 'x'
27491             });
27492         }
27493         
27494         return cfg;
27495     },
27496     
27497     onRender : function(ct, position)
27498     {
27499         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27500         
27501         if(!this.el){
27502             var cfg = Roo.apply({},  this.getAutoCreate());
27503             cfg.id = Roo.id();
27504             
27505             if (this.cls) {
27506                 cfg.cls += ' ' + this.cls;
27507             }
27508             if (this.style) {
27509                 cfg.style = this.style;
27510             }
27511             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27512             
27513             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27514         }
27515         
27516         this.el.select('>button.close').on('click', this.hide, this);
27517         
27518     },
27519     
27520     show : function()
27521     {
27522         if (!this.rendered) {
27523             this.render();
27524         }
27525         
27526         this.el.show();
27527         
27528         this.fireEvent('show', this);
27529         
27530     },
27531     
27532     hide : function()
27533     {
27534         if (!this.rendered) {
27535             this.render();
27536         }
27537         
27538         this.el.hide();
27539         
27540         this.fireEvent('hide', this);
27541     },
27542     
27543     update : function()
27544     {
27545 //        var e = this.el.dom.firstChild;
27546 //        
27547 //        if(this.closable){
27548 //            e = e.nextSibling;
27549 //        }
27550 //        
27551 //        e.data = this.html || '';
27552
27553         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27554     }
27555    
27556 });
27557
27558  
27559
27560      /*
27561  * - LGPL
27562  *
27563  * Graph
27564  * 
27565  */
27566
27567
27568 /**
27569  * @class Roo.bootstrap.Graph
27570  * @extends Roo.bootstrap.Component
27571  * Bootstrap Graph class
27572 > Prameters
27573  -sm {number} sm 4
27574  -md {number} md 5
27575  @cfg {String} graphtype  bar | vbar | pie
27576  @cfg {number} g_x coodinator | centre x (pie)
27577  @cfg {number} g_y coodinator | centre y (pie)
27578  @cfg {number} g_r radius (pie)
27579  @cfg {number} g_height height of the chart (respected by all elements in the set)
27580  @cfg {number} g_width width of the chart (respected by all elements in the set)
27581  @cfg {Object} title The title of the chart
27582     
27583  -{Array}  values
27584  -opts (object) options for the chart 
27585      o {
27586      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27587      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27588      o vgutter (number)
27589      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.
27590      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27591      o to
27592      o stretch (boolean)
27593      o }
27594  -opts (object) options for the pie
27595      o{
27596      o cut
27597      o startAngle (number)
27598      o endAngle (number)
27599      } 
27600  *
27601  * @constructor
27602  * Create a new Input
27603  * @param {Object} config The config object
27604  */
27605
27606 Roo.bootstrap.Graph = function(config){
27607     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27608     
27609     this.addEvents({
27610         // img events
27611         /**
27612          * @event click
27613          * The img click event for the img.
27614          * @param {Roo.EventObject} e
27615          */
27616         "click" : true
27617     });
27618 };
27619
27620 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27621     
27622     sm: 4,
27623     md: 5,
27624     graphtype: 'bar',
27625     g_height: 250,
27626     g_width: 400,
27627     g_x: 50,
27628     g_y: 50,
27629     g_r: 30,
27630     opts:{
27631         //g_colors: this.colors,
27632         g_type: 'soft',
27633         g_gutter: '20%'
27634
27635     },
27636     title : false,
27637
27638     getAutoCreate : function(){
27639         
27640         var cfg = {
27641             tag: 'div',
27642             html : null
27643         };
27644         
27645         
27646         return  cfg;
27647     },
27648
27649     onRender : function(ct,position){
27650         
27651         
27652         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27653         
27654         if (typeof(Raphael) == 'undefined') {
27655             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27656             return;
27657         }
27658         
27659         this.raphael = Raphael(this.el.dom);
27660         
27661                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27662                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27663                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27664                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27665                 /*
27666                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27667                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27668                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27669                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27670                 
27671                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27672                 r.barchart(330, 10, 300, 220, data1);
27673                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27674                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27675                 */
27676                 
27677                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27678                 // r.barchart(30, 30, 560, 250,  xdata, {
27679                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27680                 //     axis : "0 0 1 1",
27681                 //     axisxlabels :  xdata
27682                 //     //yvalues : cols,
27683                    
27684                 // });
27685 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27686 //        
27687 //        this.load(null,xdata,{
27688 //                axis : "0 0 1 1",
27689 //                axisxlabels :  xdata
27690 //                });
27691
27692     },
27693
27694     load : function(graphtype,xdata,opts)
27695     {
27696         this.raphael.clear();
27697         if(!graphtype) {
27698             graphtype = this.graphtype;
27699         }
27700         if(!opts){
27701             opts = this.opts;
27702         }
27703         var r = this.raphael,
27704             fin = function () {
27705                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27706             },
27707             fout = function () {
27708                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27709             },
27710             pfin = function() {
27711                 this.sector.stop();
27712                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27713
27714                 if (this.label) {
27715                     this.label[0].stop();
27716                     this.label[0].attr({ r: 7.5 });
27717                     this.label[1].attr({ "font-weight": 800 });
27718                 }
27719             },
27720             pfout = function() {
27721                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27722
27723                 if (this.label) {
27724                     this.label[0].animate({ r: 5 }, 500, "bounce");
27725                     this.label[1].attr({ "font-weight": 400 });
27726                 }
27727             };
27728
27729         switch(graphtype){
27730             case 'bar':
27731                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27732                 break;
27733             case 'hbar':
27734                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27735                 break;
27736             case 'pie':
27737 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27738 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27739 //            
27740                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27741                 
27742                 break;
27743
27744         }
27745         
27746         if(this.title){
27747             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27748         }
27749         
27750     },
27751     
27752     setTitle: function(o)
27753     {
27754         this.title = o;
27755     },
27756     
27757     initEvents: function() {
27758         
27759         if(!this.href){
27760             this.el.on('click', this.onClick, this);
27761         }
27762     },
27763     
27764     onClick : function(e)
27765     {
27766         Roo.log('img onclick');
27767         this.fireEvent('click', this, e);
27768     }
27769    
27770 });
27771
27772  
27773 /*
27774  * - LGPL
27775  *
27776  * numberBox
27777  * 
27778  */
27779 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27780
27781 /**
27782  * @class Roo.bootstrap.dash.NumberBox
27783  * @extends Roo.bootstrap.Component
27784  * Bootstrap NumberBox class
27785  * @cfg {String} headline Box headline
27786  * @cfg {String} content Box content
27787  * @cfg {String} icon Box icon
27788  * @cfg {String} footer Footer text
27789  * @cfg {String} fhref Footer href
27790  * 
27791  * @constructor
27792  * Create a new NumberBox
27793  * @param {Object} config The config object
27794  */
27795
27796
27797 Roo.bootstrap.dash.NumberBox = function(config){
27798     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27799     
27800 };
27801
27802 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27803     
27804     headline : '',
27805     content : '',
27806     icon : '',
27807     footer : '',
27808     fhref : '',
27809     ficon : '',
27810     
27811     getAutoCreate : function(){
27812         
27813         var cfg = {
27814             tag : 'div',
27815             cls : 'small-box ',
27816             cn : [
27817                 {
27818                     tag : 'div',
27819                     cls : 'inner',
27820                     cn :[
27821                         {
27822                             tag : 'h3',
27823                             cls : 'roo-headline',
27824                             html : this.headline
27825                         },
27826                         {
27827                             tag : 'p',
27828                             cls : 'roo-content',
27829                             html : this.content
27830                         }
27831                     ]
27832                 }
27833             ]
27834         };
27835         
27836         if(this.icon){
27837             cfg.cn.push({
27838                 tag : 'div',
27839                 cls : 'icon',
27840                 cn :[
27841                     {
27842                         tag : 'i',
27843                         cls : 'ion ' + this.icon
27844                     }
27845                 ]
27846             });
27847         }
27848         
27849         if(this.footer){
27850             var footer = {
27851                 tag : 'a',
27852                 cls : 'small-box-footer',
27853                 href : this.fhref || '#',
27854                 html : this.footer
27855             };
27856             
27857             cfg.cn.push(footer);
27858             
27859         }
27860         
27861         return  cfg;
27862     },
27863
27864     onRender : function(ct,position){
27865         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27866
27867
27868        
27869                 
27870     },
27871
27872     setHeadline: function (value)
27873     {
27874         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27875     },
27876     
27877     setFooter: function (value, href)
27878     {
27879         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27880         
27881         if(href){
27882             this.el.select('a.small-box-footer',true).first().attr('href', href);
27883         }
27884         
27885     },
27886
27887     setContent: function (value)
27888     {
27889         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27890     },
27891
27892     initEvents: function() 
27893     {   
27894         
27895     }
27896     
27897 });
27898
27899  
27900 /*
27901  * - LGPL
27902  *
27903  * TabBox
27904  * 
27905  */
27906 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27907
27908 /**
27909  * @class Roo.bootstrap.dash.TabBox
27910  * @extends Roo.bootstrap.Component
27911  * Bootstrap TabBox class
27912  * @cfg {String} title Title of the TabBox
27913  * @cfg {String} icon Icon of the TabBox
27914  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27915  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27916  * 
27917  * @constructor
27918  * Create a new TabBox
27919  * @param {Object} config The config object
27920  */
27921
27922
27923 Roo.bootstrap.dash.TabBox = function(config){
27924     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27925     this.addEvents({
27926         // raw events
27927         /**
27928          * @event addpane
27929          * When a pane is added
27930          * @param {Roo.bootstrap.dash.TabPane} pane
27931          */
27932         "addpane" : true,
27933         /**
27934          * @event activatepane
27935          * When a pane is activated
27936          * @param {Roo.bootstrap.dash.TabPane} pane
27937          */
27938         "activatepane" : true
27939         
27940          
27941     });
27942     
27943     this.panes = [];
27944 };
27945
27946 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27947
27948     title : '',
27949     icon : false,
27950     showtabs : true,
27951     tabScrollable : false,
27952     
27953     getChildContainer : function()
27954     {
27955         return this.el.select('.tab-content', true).first();
27956     },
27957     
27958     getAutoCreate : function(){
27959         
27960         var header = {
27961             tag: 'li',
27962             cls: 'pull-left header',
27963             html: this.title,
27964             cn : []
27965         };
27966         
27967         if(this.icon){
27968             header.cn.push({
27969                 tag: 'i',
27970                 cls: 'fa ' + this.icon
27971             });
27972         }
27973         
27974         var h = {
27975             tag: 'ul',
27976             cls: 'nav nav-tabs pull-right',
27977             cn: [
27978                 header
27979             ]
27980         };
27981         
27982         if(this.tabScrollable){
27983             h = {
27984                 tag: 'div',
27985                 cls: 'tab-header',
27986                 cn: [
27987                     {
27988                         tag: 'ul',
27989                         cls: 'nav nav-tabs pull-right',
27990                         cn: [
27991                             header
27992                         ]
27993                     }
27994                 ]
27995             };
27996         }
27997         
27998         var cfg = {
27999             tag: 'div',
28000             cls: 'nav-tabs-custom',
28001             cn: [
28002                 h,
28003                 {
28004                     tag: 'div',
28005                     cls: 'tab-content no-padding',
28006                     cn: []
28007                 }
28008             ]
28009         };
28010
28011         return  cfg;
28012     },
28013     initEvents : function()
28014     {
28015         //Roo.log('add add pane handler');
28016         this.on('addpane', this.onAddPane, this);
28017     },
28018      /**
28019      * Updates the box title
28020      * @param {String} html to set the title to.
28021      */
28022     setTitle : function(value)
28023     {
28024         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28025     },
28026     onAddPane : function(pane)
28027     {
28028         this.panes.push(pane);
28029         //Roo.log('addpane');
28030         //Roo.log(pane);
28031         // tabs are rendere left to right..
28032         if(!this.showtabs){
28033             return;
28034         }
28035         
28036         var ctr = this.el.select('.nav-tabs', true).first();
28037          
28038          
28039         var existing = ctr.select('.nav-tab',true);
28040         var qty = existing.getCount();;
28041         
28042         
28043         var tab = ctr.createChild({
28044             tag : 'li',
28045             cls : 'nav-tab' + (qty ? '' : ' active'),
28046             cn : [
28047                 {
28048                     tag : 'a',
28049                     href:'#',
28050                     html : pane.title
28051                 }
28052             ]
28053         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28054         pane.tab = tab;
28055         
28056         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28057         if (!qty) {
28058             pane.el.addClass('active');
28059         }
28060         
28061                 
28062     },
28063     onTabClick : function(ev,un,ob,pane)
28064     {
28065         //Roo.log('tab - prev default');
28066         ev.preventDefault();
28067         
28068         
28069         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28070         pane.tab.addClass('active');
28071         //Roo.log(pane.title);
28072         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28073         // technically we should have a deactivate event.. but maybe add later.
28074         // and it should not de-activate the selected tab...
28075         this.fireEvent('activatepane', pane);
28076         pane.el.addClass('active');
28077         pane.fireEvent('activate');
28078         
28079         
28080     },
28081     
28082     getActivePane : function()
28083     {
28084         var r = false;
28085         Roo.each(this.panes, function(p) {
28086             if(p.el.hasClass('active')){
28087                 r = p;
28088                 return false;
28089             }
28090             
28091             return;
28092         });
28093         
28094         return r;
28095     }
28096     
28097     
28098 });
28099
28100  
28101 /*
28102  * - LGPL
28103  *
28104  * Tab pane
28105  * 
28106  */
28107 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28108 /**
28109  * @class Roo.bootstrap.TabPane
28110  * @extends Roo.bootstrap.Component
28111  * Bootstrap TabPane class
28112  * @cfg {Boolean} active (false | true) Default false
28113  * @cfg {String} title title of panel
28114
28115  * 
28116  * @constructor
28117  * Create a new TabPane
28118  * @param {Object} config The config object
28119  */
28120
28121 Roo.bootstrap.dash.TabPane = function(config){
28122     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28123     
28124     this.addEvents({
28125         // raw events
28126         /**
28127          * @event activate
28128          * When a pane is activated
28129          * @param {Roo.bootstrap.dash.TabPane} pane
28130          */
28131         "activate" : true
28132          
28133     });
28134 };
28135
28136 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28137     
28138     active : false,
28139     title : '',
28140     
28141     // the tabBox that this is attached to.
28142     tab : false,
28143      
28144     getAutoCreate : function() 
28145     {
28146         var cfg = {
28147             tag: 'div',
28148             cls: 'tab-pane'
28149         };
28150         
28151         if(this.active){
28152             cfg.cls += ' active';
28153         }
28154         
28155         return cfg;
28156     },
28157     initEvents  : function()
28158     {
28159         //Roo.log('trigger add pane handler');
28160         this.parent().fireEvent('addpane', this)
28161     },
28162     
28163      /**
28164      * Updates the tab title 
28165      * @param {String} html to set the title to.
28166      */
28167     setTitle: function(str)
28168     {
28169         if (!this.tab) {
28170             return;
28171         }
28172         this.title = str;
28173         this.tab.select('a', true).first().dom.innerHTML = str;
28174         
28175     }
28176     
28177     
28178     
28179 });
28180
28181  
28182
28183
28184  /*
28185  * - LGPL
28186  *
28187  * menu
28188  * 
28189  */
28190 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28191
28192 /**
28193  * @class Roo.bootstrap.menu.Menu
28194  * @extends Roo.bootstrap.Component
28195  * Bootstrap Menu class - container for Menu
28196  * @cfg {String} html Text of the menu
28197  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28198  * @cfg {String} icon Font awesome icon
28199  * @cfg {String} pos Menu align to (top | bottom) default bottom
28200  * 
28201  * 
28202  * @constructor
28203  * Create a new Menu
28204  * @param {Object} config The config object
28205  */
28206
28207
28208 Roo.bootstrap.menu.Menu = function(config){
28209     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28210     
28211     this.addEvents({
28212         /**
28213          * @event beforeshow
28214          * Fires before this menu is displayed
28215          * @param {Roo.bootstrap.menu.Menu} this
28216          */
28217         beforeshow : true,
28218         /**
28219          * @event beforehide
28220          * Fires before this menu is hidden
28221          * @param {Roo.bootstrap.menu.Menu} this
28222          */
28223         beforehide : true,
28224         /**
28225          * @event show
28226          * Fires after this menu is displayed
28227          * @param {Roo.bootstrap.menu.Menu} this
28228          */
28229         show : true,
28230         /**
28231          * @event hide
28232          * Fires after this menu is hidden
28233          * @param {Roo.bootstrap.menu.Menu} this
28234          */
28235         hide : true,
28236         /**
28237          * @event click
28238          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28239          * @param {Roo.bootstrap.menu.Menu} this
28240          * @param {Roo.EventObject} e
28241          */
28242         click : true
28243     });
28244     
28245 };
28246
28247 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28248     
28249     submenu : false,
28250     html : '',
28251     weight : 'default',
28252     icon : false,
28253     pos : 'bottom',
28254     
28255     
28256     getChildContainer : function() {
28257         if(this.isSubMenu){
28258             return this.el;
28259         }
28260         
28261         return this.el.select('ul.dropdown-menu', true).first();  
28262     },
28263     
28264     getAutoCreate : function()
28265     {
28266         var text = [
28267             {
28268                 tag : 'span',
28269                 cls : 'roo-menu-text',
28270                 html : this.html
28271             }
28272         ];
28273         
28274         if(this.icon){
28275             text.unshift({
28276                 tag : 'i',
28277                 cls : 'fa ' + this.icon
28278             })
28279         }
28280         
28281         
28282         var cfg = {
28283             tag : 'div',
28284             cls : 'btn-group',
28285             cn : [
28286                 {
28287                     tag : 'button',
28288                     cls : 'dropdown-button btn btn-' + this.weight,
28289                     cn : text
28290                 },
28291                 {
28292                     tag : 'button',
28293                     cls : 'dropdown-toggle btn btn-' + this.weight,
28294                     cn : [
28295                         {
28296                             tag : 'span',
28297                             cls : 'caret'
28298                         }
28299                     ]
28300                 },
28301                 {
28302                     tag : 'ul',
28303                     cls : 'dropdown-menu'
28304                 }
28305             ]
28306             
28307         };
28308         
28309         if(this.pos == 'top'){
28310             cfg.cls += ' dropup';
28311         }
28312         
28313         if(this.isSubMenu){
28314             cfg = {
28315                 tag : 'ul',
28316                 cls : 'dropdown-menu'
28317             }
28318         }
28319         
28320         return cfg;
28321     },
28322     
28323     onRender : function(ct, position)
28324     {
28325         this.isSubMenu = ct.hasClass('dropdown-submenu');
28326         
28327         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28328     },
28329     
28330     initEvents : function() 
28331     {
28332         if(this.isSubMenu){
28333             return;
28334         }
28335         
28336         this.hidden = true;
28337         
28338         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28339         this.triggerEl.on('click', this.onTriggerPress, this);
28340         
28341         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28342         this.buttonEl.on('click', this.onClick, this);
28343         
28344     },
28345     
28346     list : function()
28347     {
28348         if(this.isSubMenu){
28349             return this.el;
28350         }
28351         
28352         return this.el.select('ul.dropdown-menu', true).first();
28353     },
28354     
28355     onClick : function(e)
28356     {
28357         this.fireEvent("click", this, e);
28358     },
28359     
28360     onTriggerPress  : function(e)
28361     {   
28362         if (this.isVisible()) {
28363             this.hide();
28364         } else {
28365             this.show();
28366         }
28367     },
28368     
28369     isVisible : function(){
28370         return !this.hidden;
28371     },
28372     
28373     show : function()
28374     {
28375         this.fireEvent("beforeshow", this);
28376         
28377         this.hidden = false;
28378         this.el.addClass('open');
28379         
28380         Roo.get(document).on("mouseup", this.onMouseUp, this);
28381         
28382         this.fireEvent("show", this);
28383         
28384         
28385     },
28386     
28387     hide : function()
28388     {
28389         this.fireEvent("beforehide", this);
28390         
28391         this.hidden = true;
28392         this.el.removeClass('open');
28393         
28394         Roo.get(document).un("mouseup", this.onMouseUp);
28395         
28396         this.fireEvent("hide", this);
28397     },
28398     
28399     onMouseUp : function()
28400     {
28401         this.hide();
28402     }
28403     
28404 });
28405
28406  
28407  /*
28408  * - LGPL
28409  *
28410  * menu item
28411  * 
28412  */
28413 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28414
28415 /**
28416  * @class Roo.bootstrap.menu.Item
28417  * @extends Roo.bootstrap.Component
28418  * Bootstrap MenuItem class
28419  * @cfg {Boolean} submenu (true | false) default false
28420  * @cfg {String} html text of the item
28421  * @cfg {String} href the link
28422  * @cfg {Boolean} disable (true | false) default false
28423  * @cfg {Boolean} preventDefault (true | false) default true
28424  * @cfg {String} icon Font awesome icon
28425  * @cfg {String} pos Submenu align to (left | right) default right 
28426  * 
28427  * 
28428  * @constructor
28429  * Create a new Item
28430  * @param {Object} config The config object
28431  */
28432
28433
28434 Roo.bootstrap.menu.Item = function(config){
28435     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28436     this.addEvents({
28437         /**
28438          * @event mouseover
28439          * Fires when the mouse is hovering over this menu
28440          * @param {Roo.bootstrap.menu.Item} this
28441          * @param {Roo.EventObject} e
28442          */
28443         mouseover : true,
28444         /**
28445          * @event mouseout
28446          * Fires when the mouse exits this menu
28447          * @param {Roo.bootstrap.menu.Item} this
28448          * @param {Roo.EventObject} e
28449          */
28450         mouseout : true,
28451         // raw events
28452         /**
28453          * @event click
28454          * The raw click event for the entire grid.
28455          * @param {Roo.EventObject} e
28456          */
28457         click : true
28458     });
28459 };
28460
28461 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28462     
28463     submenu : false,
28464     href : '',
28465     html : '',
28466     preventDefault: true,
28467     disable : false,
28468     icon : false,
28469     pos : 'right',
28470     
28471     getAutoCreate : function()
28472     {
28473         var text = [
28474             {
28475                 tag : 'span',
28476                 cls : 'roo-menu-item-text',
28477                 html : this.html
28478             }
28479         ];
28480         
28481         if(this.icon){
28482             text.unshift({
28483                 tag : 'i',
28484                 cls : 'fa ' + this.icon
28485             })
28486         }
28487         
28488         var cfg = {
28489             tag : 'li',
28490             cn : [
28491                 {
28492                     tag : 'a',
28493                     href : this.href || '#',
28494                     cn : text
28495                 }
28496             ]
28497         };
28498         
28499         if(this.disable){
28500             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28501         }
28502         
28503         if(this.submenu){
28504             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28505             
28506             if(this.pos == 'left'){
28507                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28508             }
28509         }
28510         
28511         return cfg;
28512     },
28513     
28514     initEvents : function() 
28515     {
28516         this.el.on('mouseover', this.onMouseOver, this);
28517         this.el.on('mouseout', this.onMouseOut, this);
28518         
28519         this.el.select('a', true).first().on('click', this.onClick, this);
28520         
28521     },
28522     
28523     onClick : function(e)
28524     {
28525         if(this.preventDefault){
28526             e.preventDefault();
28527         }
28528         
28529         this.fireEvent("click", this, e);
28530     },
28531     
28532     onMouseOver : function(e)
28533     {
28534         if(this.submenu && this.pos == 'left'){
28535             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28536         }
28537         
28538         this.fireEvent("mouseover", this, e);
28539     },
28540     
28541     onMouseOut : function(e)
28542     {
28543         this.fireEvent("mouseout", this, e);
28544     }
28545 });
28546
28547  
28548
28549  /*
28550  * - LGPL
28551  *
28552  * menu separator
28553  * 
28554  */
28555 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28556
28557 /**
28558  * @class Roo.bootstrap.menu.Separator
28559  * @extends Roo.bootstrap.Component
28560  * Bootstrap Separator class
28561  * 
28562  * @constructor
28563  * Create a new Separator
28564  * @param {Object} config The config object
28565  */
28566
28567
28568 Roo.bootstrap.menu.Separator = function(config){
28569     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28570 };
28571
28572 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28573     
28574     getAutoCreate : function(){
28575         var cfg = {
28576             tag : 'li',
28577             cls: 'divider'
28578         };
28579         
28580         return cfg;
28581     }
28582    
28583 });
28584
28585  
28586
28587  /*
28588  * - LGPL
28589  *
28590  * Tooltip
28591  * 
28592  */
28593
28594 /**
28595  * @class Roo.bootstrap.Tooltip
28596  * Bootstrap Tooltip class
28597  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28598  * to determine which dom element triggers the tooltip.
28599  * 
28600  * It needs to add support for additional attributes like tooltip-position
28601  * 
28602  * @constructor
28603  * Create a new Toolti
28604  * @param {Object} config The config object
28605  */
28606
28607 Roo.bootstrap.Tooltip = function(config){
28608     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28609     
28610     this.alignment = Roo.bootstrap.Tooltip.alignment;
28611     
28612     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28613         this.alignment = config.alignment;
28614     }
28615     
28616 };
28617
28618 Roo.apply(Roo.bootstrap.Tooltip, {
28619     /**
28620      * @function init initialize tooltip monitoring.
28621      * @static
28622      */
28623     currentEl : false,
28624     currentTip : false,
28625     currentRegion : false,
28626     
28627     //  init : delay?
28628     
28629     init : function()
28630     {
28631         Roo.get(document).on('mouseover', this.enter ,this);
28632         Roo.get(document).on('mouseout', this.leave, this);
28633          
28634         
28635         this.currentTip = new Roo.bootstrap.Tooltip();
28636     },
28637     
28638     enter : function(ev)
28639     {
28640         var dom = ev.getTarget();
28641         
28642         //Roo.log(['enter',dom]);
28643         var el = Roo.fly(dom);
28644         if (this.currentEl) {
28645             //Roo.log(dom);
28646             //Roo.log(this.currentEl);
28647             //Roo.log(this.currentEl.contains(dom));
28648             if (this.currentEl == el) {
28649                 return;
28650             }
28651             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28652                 return;
28653             }
28654
28655         }
28656         
28657         if (this.currentTip.el) {
28658             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28659         }    
28660         //Roo.log(ev);
28661         
28662         if(!el || el.dom == document){
28663             return;
28664         }
28665         
28666         var bindEl = el;
28667         
28668         // you can not look for children, as if el is the body.. then everythign is the child..
28669         if (!el.attr('tooltip')) { //
28670             if (!el.select("[tooltip]").elements.length) {
28671                 return;
28672             }
28673             // is the mouse over this child...?
28674             bindEl = el.select("[tooltip]").first();
28675             var xy = ev.getXY();
28676             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28677                 //Roo.log("not in region.");
28678                 return;
28679             }
28680             //Roo.log("child element over..");
28681             
28682         }
28683         this.currentEl = bindEl;
28684         this.currentTip.bind(bindEl);
28685         this.currentRegion = Roo.lib.Region.getRegion(dom);
28686         this.currentTip.enter();
28687         
28688     },
28689     leave : function(ev)
28690     {
28691         var dom = ev.getTarget();
28692         //Roo.log(['leave',dom]);
28693         if (!this.currentEl) {
28694             return;
28695         }
28696         
28697         
28698         if (dom != this.currentEl.dom) {
28699             return;
28700         }
28701         var xy = ev.getXY();
28702         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28703             return;
28704         }
28705         // only activate leave if mouse cursor is outside... bounding box..
28706         
28707         
28708         
28709         
28710         if (this.currentTip) {
28711             this.currentTip.leave();
28712         }
28713         //Roo.log('clear currentEl');
28714         this.currentEl = false;
28715         
28716         
28717     },
28718     alignment : {
28719         'left' : ['r-l', [-2,0], 'right'],
28720         'right' : ['l-r', [2,0], 'left'],
28721         'bottom' : ['t-b', [0,2], 'top'],
28722         'top' : [ 'b-t', [0,-2], 'bottom']
28723     }
28724     
28725 });
28726
28727
28728 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28729     
28730     
28731     bindEl : false,
28732     
28733     delay : null, // can be { show : 300 , hide: 500}
28734     
28735     timeout : null,
28736     
28737     hoverState : null, //???
28738     
28739     placement : 'bottom', 
28740     
28741     alignment : false,
28742     
28743     getAutoCreate : function(){
28744     
28745         var cfg = {
28746            cls : 'tooltip',   
28747            role : 'tooltip',
28748            cn : [
28749                 {
28750                     cls : 'tooltip-arrow arrow'
28751                 },
28752                 {
28753                     cls : 'tooltip-inner'
28754                 }
28755            ]
28756         };
28757         
28758         return cfg;
28759     },
28760     bind : function(el)
28761     {
28762         this.bindEl = el;
28763     },
28764     
28765     initEvents : function()
28766     {
28767         this.arrowEl = this.el.select('.arrow', true).first();
28768         this.innerEl = this.el.select('.tooltip-inner', true).first();
28769     },
28770     
28771     enter : function () {
28772        
28773         if (this.timeout != null) {
28774             clearTimeout(this.timeout);
28775         }
28776         
28777         this.hoverState = 'in';
28778          //Roo.log("enter - show");
28779         if (!this.delay || !this.delay.show) {
28780             this.show();
28781             return;
28782         }
28783         var _t = this;
28784         this.timeout = setTimeout(function () {
28785             if (_t.hoverState == 'in') {
28786                 _t.show();
28787             }
28788         }, this.delay.show);
28789     },
28790     leave : function()
28791     {
28792         clearTimeout(this.timeout);
28793     
28794         this.hoverState = 'out';
28795          if (!this.delay || !this.delay.hide) {
28796             this.hide();
28797             return;
28798         }
28799        
28800         var _t = this;
28801         this.timeout = setTimeout(function () {
28802             //Roo.log("leave - timeout");
28803             
28804             if (_t.hoverState == 'out') {
28805                 _t.hide();
28806                 Roo.bootstrap.Tooltip.currentEl = false;
28807             }
28808         }, delay);
28809     },
28810     
28811     show : function (msg)
28812     {
28813         if (!this.el) {
28814             this.render(document.body);
28815         }
28816         // set content.
28817         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28818         
28819         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28820         
28821         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28822         
28823         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28824                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28825         
28826         var placement = typeof this.placement == 'function' ?
28827             this.placement.call(this, this.el, on_el) :
28828             this.placement;
28829             
28830         var autoToken = /\s?auto?\s?/i;
28831         var autoPlace = autoToken.test(placement);
28832         if (autoPlace) {
28833             placement = placement.replace(autoToken, '') || 'top';
28834         }
28835         
28836         //this.el.detach()
28837         //this.el.setXY([0,0]);
28838         this.el.show();
28839         //this.el.dom.style.display='block';
28840         
28841         //this.el.appendTo(on_el);
28842         
28843         var p = this.getPosition();
28844         var box = this.el.getBox();
28845         
28846         if (autoPlace) {
28847             // fixme..
28848         }
28849         
28850         var align = this.alignment[placement];
28851         
28852         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28853         
28854         if(placement == 'top' || placement == 'bottom'){
28855             if(xy[0] < 0){
28856                 placement = 'right';
28857             }
28858             
28859             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28860                 placement = 'left';
28861             }
28862             
28863             var scroll = Roo.select('body', true).first().getScroll();
28864             
28865             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28866                 placement = 'top';
28867             }
28868             
28869             align = this.alignment[placement];
28870             
28871             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28872             
28873         }
28874         
28875         this.el.alignTo(this.bindEl, align[0],align[1]);
28876         //var arrow = this.el.select('.arrow',true).first();
28877         //arrow.set(align[2], 
28878         
28879         this.el.addClass(placement);
28880         this.el.addClass("bs-tooltip-"+ placement);
28881         
28882         this.el.addClass('in fade show');
28883         
28884         this.hoverState = null;
28885         
28886         if (this.el.hasClass('fade')) {
28887             // fade it?
28888         }
28889         
28890         
28891         
28892         
28893         
28894     },
28895     hide : function()
28896     {
28897          
28898         if (!this.el) {
28899             return;
28900         }
28901         //this.el.setXY([0,0]);
28902         this.el.removeClass(['show', 'in']);
28903         //this.el.hide();
28904         
28905     }
28906     
28907 });
28908  
28909
28910  /*
28911  * - LGPL
28912  *
28913  * Location Picker
28914  * 
28915  */
28916
28917 /**
28918  * @class Roo.bootstrap.LocationPicker
28919  * @extends Roo.bootstrap.Component
28920  * Bootstrap LocationPicker class
28921  * @cfg {Number} latitude Position when init default 0
28922  * @cfg {Number} longitude Position when init default 0
28923  * @cfg {Number} zoom default 15
28924  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28925  * @cfg {Boolean} mapTypeControl default false
28926  * @cfg {Boolean} disableDoubleClickZoom default false
28927  * @cfg {Boolean} scrollwheel default true
28928  * @cfg {Boolean} streetViewControl default false
28929  * @cfg {Number} radius default 0
28930  * @cfg {String} locationName
28931  * @cfg {Boolean} draggable default true
28932  * @cfg {Boolean} enableAutocomplete default false
28933  * @cfg {Boolean} enableReverseGeocode default true
28934  * @cfg {String} markerTitle
28935  * 
28936  * @constructor
28937  * Create a new LocationPicker
28938  * @param {Object} config The config object
28939  */
28940
28941
28942 Roo.bootstrap.LocationPicker = function(config){
28943     
28944     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28945     
28946     this.addEvents({
28947         /**
28948          * @event initial
28949          * Fires when the picker initialized.
28950          * @param {Roo.bootstrap.LocationPicker} this
28951          * @param {Google Location} location
28952          */
28953         initial : true,
28954         /**
28955          * @event positionchanged
28956          * Fires when the picker position changed.
28957          * @param {Roo.bootstrap.LocationPicker} this
28958          * @param {Google Location} location
28959          */
28960         positionchanged : true,
28961         /**
28962          * @event resize
28963          * Fires when the map resize.
28964          * @param {Roo.bootstrap.LocationPicker} this
28965          */
28966         resize : true,
28967         /**
28968          * @event show
28969          * Fires when the map show.
28970          * @param {Roo.bootstrap.LocationPicker} this
28971          */
28972         show : true,
28973         /**
28974          * @event hide
28975          * Fires when the map hide.
28976          * @param {Roo.bootstrap.LocationPicker} this
28977          */
28978         hide : true,
28979         /**
28980          * @event mapClick
28981          * Fires when click the map.
28982          * @param {Roo.bootstrap.LocationPicker} this
28983          * @param {Map event} e
28984          */
28985         mapClick : true,
28986         /**
28987          * @event mapRightClick
28988          * Fires when right click the map.
28989          * @param {Roo.bootstrap.LocationPicker} this
28990          * @param {Map event} e
28991          */
28992         mapRightClick : true,
28993         /**
28994          * @event markerClick
28995          * Fires when click the marker.
28996          * @param {Roo.bootstrap.LocationPicker} this
28997          * @param {Map event} e
28998          */
28999         markerClick : true,
29000         /**
29001          * @event markerRightClick
29002          * Fires when right click the marker.
29003          * @param {Roo.bootstrap.LocationPicker} this
29004          * @param {Map event} e
29005          */
29006         markerRightClick : true,
29007         /**
29008          * @event OverlayViewDraw
29009          * Fires when OverlayView Draw
29010          * @param {Roo.bootstrap.LocationPicker} this
29011          */
29012         OverlayViewDraw : true,
29013         /**
29014          * @event OverlayViewOnAdd
29015          * Fires when OverlayView Draw
29016          * @param {Roo.bootstrap.LocationPicker} this
29017          */
29018         OverlayViewOnAdd : true,
29019         /**
29020          * @event OverlayViewOnRemove
29021          * Fires when OverlayView Draw
29022          * @param {Roo.bootstrap.LocationPicker} this
29023          */
29024         OverlayViewOnRemove : true,
29025         /**
29026          * @event OverlayViewShow
29027          * Fires when OverlayView Draw
29028          * @param {Roo.bootstrap.LocationPicker} this
29029          * @param {Pixel} cpx
29030          */
29031         OverlayViewShow : true,
29032         /**
29033          * @event OverlayViewHide
29034          * Fires when OverlayView Draw
29035          * @param {Roo.bootstrap.LocationPicker} this
29036          */
29037         OverlayViewHide : true,
29038         /**
29039          * @event loadexception
29040          * Fires when load google lib failed.
29041          * @param {Roo.bootstrap.LocationPicker} this
29042          */
29043         loadexception : true
29044     });
29045         
29046 };
29047
29048 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29049     
29050     gMapContext: false,
29051     
29052     latitude: 0,
29053     longitude: 0,
29054     zoom: 15,
29055     mapTypeId: false,
29056     mapTypeControl: false,
29057     disableDoubleClickZoom: false,
29058     scrollwheel: true,
29059     streetViewControl: false,
29060     radius: 0,
29061     locationName: '',
29062     draggable: true,
29063     enableAutocomplete: false,
29064     enableReverseGeocode: true,
29065     markerTitle: '',
29066     
29067     getAutoCreate: function()
29068     {
29069
29070         var cfg = {
29071             tag: 'div',
29072             cls: 'roo-location-picker'
29073         };
29074         
29075         return cfg
29076     },
29077     
29078     initEvents: function(ct, position)
29079     {       
29080         if(!this.el.getWidth() || this.isApplied()){
29081             return;
29082         }
29083         
29084         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29085         
29086         this.initial();
29087     },
29088     
29089     initial: function()
29090     {
29091         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29092             this.fireEvent('loadexception', this);
29093             return;
29094         }
29095         
29096         if(!this.mapTypeId){
29097             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29098         }
29099         
29100         this.gMapContext = this.GMapContext();
29101         
29102         this.initOverlayView();
29103         
29104         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29105         
29106         var _this = this;
29107                 
29108         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29109             _this.setPosition(_this.gMapContext.marker.position);
29110         });
29111         
29112         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29113             _this.fireEvent('mapClick', this, event);
29114             
29115         });
29116
29117         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29118             _this.fireEvent('mapRightClick', this, event);
29119             
29120         });
29121         
29122         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29123             _this.fireEvent('markerClick', this, event);
29124             
29125         });
29126
29127         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29128             _this.fireEvent('markerRightClick', this, event);
29129             
29130         });
29131         
29132         this.setPosition(this.gMapContext.location);
29133         
29134         this.fireEvent('initial', this, this.gMapContext.location);
29135     },
29136     
29137     initOverlayView: function()
29138     {
29139         var _this = this;
29140         
29141         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29142             
29143             draw: function()
29144             {
29145                 _this.fireEvent('OverlayViewDraw', _this);
29146             },
29147             
29148             onAdd: function()
29149             {
29150                 _this.fireEvent('OverlayViewOnAdd', _this);
29151             },
29152             
29153             onRemove: function()
29154             {
29155                 _this.fireEvent('OverlayViewOnRemove', _this);
29156             },
29157             
29158             show: function(cpx)
29159             {
29160                 _this.fireEvent('OverlayViewShow', _this, cpx);
29161             },
29162             
29163             hide: function()
29164             {
29165                 _this.fireEvent('OverlayViewHide', _this);
29166             }
29167             
29168         });
29169     },
29170     
29171     fromLatLngToContainerPixel: function(event)
29172     {
29173         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29174     },
29175     
29176     isApplied: function() 
29177     {
29178         return this.getGmapContext() == false ? false : true;
29179     },
29180     
29181     getGmapContext: function() 
29182     {
29183         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29184     },
29185     
29186     GMapContext: function() 
29187     {
29188         var position = new google.maps.LatLng(this.latitude, this.longitude);
29189         
29190         var _map = new google.maps.Map(this.el.dom, {
29191             center: position,
29192             zoom: this.zoom,
29193             mapTypeId: this.mapTypeId,
29194             mapTypeControl: this.mapTypeControl,
29195             disableDoubleClickZoom: this.disableDoubleClickZoom,
29196             scrollwheel: this.scrollwheel,
29197             streetViewControl: this.streetViewControl,
29198             locationName: this.locationName,
29199             draggable: this.draggable,
29200             enableAutocomplete: this.enableAutocomplete,
29201             enableReverseGeocode: this.enableReverseGeocode
29202         });
29203         
29204         var _marker = new google.maps.Marker({
29205             position: position,
29206             map: _map,
29207             title: this.markerTitle,
29208             draggable: this.draggable
29209         });
29210         
29211         return {
29212             map: _map,
29213             marker: _marker,
29214             circle: null,
29215             location: position,
29216             radius: this.radius,
29217             locationName: this.locationName,
29218             addressComponents: {
29219                 formatted_address: null,
29220                 addressLine1: null,
29221                 addressLine2: null,
29222                 streetName: null,
29223                 streetNumber: null,
29224                 city: null,
29225                 district: null,
29226                 state: null,
29227                 stateOrProvince: null
29228             },
29229             settings: this,
29230             domContainer: this.el.dom,
29231             geodecoder: new google.maps.Geocoder()
29232         };
29233     },
29234     
29235     drawCircle: function(center, radius, options) 
29236     {
29237         if (this.gMapContext.circle != null) {
29238             this.gMapContext.circle.setMap(null);
29239         }
29240         if (radius > 0) {
29241             radius *= 1;
29242             options = Roo.apply({}, options, {
29243                 strokeColor: "#0000FF",
29244                 strokeOpacity: .35,
29245                 strokeWeight: 2,
29246                 fillColor: "#0000FF",
29247                 fillOpacity: .2
29248             });
29249             
29250             options.map = this.gMapContext.map;
29251             options.radius = radius;
29252             options.center = center;
29253             this.gMapContext.circle = new google.maps.Circle(options);
29254             return this.gMapContext.circle;
29255         }
29256         
29257         return null;
29258     },
29259     
29260     setPosition: function(location) 
29261     {
29262         this.gMapContext.location = location;
29263         this.gMapContext.marker.setPosition(location);
29264         this.gMapContext.map.panTo(location);
29265         this.drawCircle(location, this.gMapContext.radius, {});
29266         
29267         var _this = this;
29268         
29269         if (this.gMapContext.settings.enableReverseGeocode) {
29270             this.gMapContext.geodecoder.geocode({
29271                 latLng: this.gMapContext.location
29272             }, function(results, status) {
29273                 
29274                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29275                     _this.gMapContext.locationName = results[0].formatted_address;
29276                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29277                     
29278                     _this.fireEvent('positionchanged', this, location);
29279                 }
29280             });
29281             
29282             return;
29283         }
29284         
29285         this.fireEvent('positionchanged', this, location);
29286     },
29287     
29288     resize: function()
29289     {
29290         google.maps.event.trigger(this.gMapContext.map, "resize");
29291         
29292         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29293         
29294         this.fireEvent('resize', this);
29295     },
29296     
29297     setPositionByLatLng: function(latitude, longitude)
29298     {
29299         this.setPosition(new google.maps.LatLng(latitude, longitude));
29300     },
29301     
29302     getCurrentPosition: function() 
29303     {
29304         return {
29305             latitude: this.gMapContext.location.lat(),
29306             longitude: this.gMapContext.location.lng()
29307         };
29308     },
29309     
29310     getAddressName: function() 
29311     {
29312         return this.gMapContext.locationName;
29313     },
29314     
29315     getAddressComponents: function() 
29316     {
29317         return this.gMapContext.addressComponents;
29318     },
29319     
29320     address_component_from_google_geocode: function(address_components) 
29321     {
29322         var result = {};
29323         
29324         for (var i = 0; i < address_components.length; i++) {
29325             var component = address_components[i];
29326             if (component.types.indexOf("postal_code") >= 0) {
29327                 result.postalCode = component.short_name;
29328             } else if (component.types.indexOf("street_number") >= 0) {
29329                 result.streetNumber = component.short_name;
29330             } else if (component.types.indexOf("route") >= 0) {
29331                 result.streetName = component.short_name;
29332             } else if (component.types.indexOf("neighborhood") >= 0) {
29333                 result.city = component.short_name;
29334             } else if (component.types.indexOf("locality") >= 0) {
29335                 result.city = component.short_name;
29336             } else if (component.types.indexOf("sublocality") >= 0) {
29337                 result.district = component.short_name;
29338             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29339                 result.stateOrProvince = component.short_name;
29340             } else if (component.types.indexOf("country") >= 0) {
29341                 result.country = component.short_name;
29342             }
29343         }
29344         
29345         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29346         result.addressLine2 = "";
29347         return result;
29348     },
29349     
29350     setZoomLevel: function(zoom)
29351     {
29352         this.gMapContext.map.setZoom(zoom);
29353     },
29354     
29355     show: function()
29356     {
29357         if(!this.el){
29358             return;
29359         }
29360         
29361         this.el.show();
29362         
29363         this.resize();
29364         
29365         this.fireEvent('show', this);
29366     },
29367     
29368     hide: function()
29369     {
29370         if(!this.el){
29371             return;
29372         }
29373         
29374         this.el.hide();
29375         
29376         this.fireEvent('hide', this);
29377     }
29378     
29379 });
29380
29381 Roo.apply(Roo.bootstrap.LocationPicker, {
29382     
29383     OverlayView : function(map, options)
29384     {
29385         options = options || {};
29386         
29387         this.setMap(map);
29388     }
29389     
29390     
29391 });/**
29392  * @class Roo.bootstrap.Alert
29393  * @extends Roo.bootstrap.Component
29394  * Bootstrap Alert class - shows an alert area box
29395  * eg
29396  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29397   Enter a valid email address
29398 </div>
29399  * @licence LGPL
29400  * @cfg {String} title The title of alert
29401  * @cfg {String} html The content of alert
29402  * @cfg {String} weight (  success | info | warning | danger )
29403  * @cfg {String} faicon font-awesomeicon
29404  * 
29405  * @constructor
29406  * Create a new alert
29407  * @param {Object} config The config object
29408  */
29409
29410
29411 Roo.bootstrap.Alert = function(config){
29412     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29413     
29414 };
29415
29416 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29417     
29418     title: '',
29419     html: '',
29420     weight: false,
29421     faicon: false,
29422     
29423     getAutoCreate : function()
29424     {
29425         
29426         var cfg = {
29427             tag : 'div',
29428             cls : 'alert',
29429             cn : [
29430                 {
29431                     tag : 'i',
29432                     cls : 'roo-alert-icon'
29433                     
29434                 },
29435                 {
29436                     tag : 'b',
29437                     cls : 'roo-alert-title',
29438                     html : this.title
29439                 },
29440                 {
29441                     tag : 'span',
29442                     cls : 'roo-alert-text',
29443                     html : this.html
29444                 }
29445             ]
29446         };
29447         
29448         if(this.faicon){
29449             cfg.cn[0].cls += ' fa ' + this.faicon;
29450         }
29451         
29452         if(this.weight){
29453             cfg.cls += ' alert-' + this.weight;
29454         }
29455         
29456         return cfg;
29457     },
29458     
29459     initEvents: function() 
29460     {
29461         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29462     },
29463     
29464     setTitle : function(str)
29465     {
29466         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29467     },
29468     
29469     setText : function(str)
29470     {
29471         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29472     },
29473     
29474     setWeight : function(weight)
29475     {
29476         if(this.weight){
29477             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29478         }
29479         
29480         this.weight = weight;
29481         
29482         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29483     },
29484     
29485     setIcon : function(icon)
29486     {
29487         if(this.faicon){
29488             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29489         }
29490         
29491         this.faicon = icon;
29492         
29493         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29494     },
29495     
29496     hide: function() 
29497     {
29498         this.el.hide();   
29499     },
29500     
29501     show: function() 
29502     {  
29503         this.el.show();   
29504     }
29505     
29506 });
29507
29508  
29509 /*
29510 * Licence: LGPL
29511 */
29512
29513 /**
29514  * @class Roo.bootstrap.UploadCropbox
29515  * @extends Roo.bootstrap.Component
29516  * Bootstrap UploadCropbox class
29517  * @cfg {String} emptyText show when image has been loaded
29518  * @cfg {String} rotateNotify show when image too small to rotate
29519  * @cfg {Number} errorTimeout default 3000
29520  * @cfg {Number} minWidth default 300
29521  * @cfg {Number} minHeight default 300
29522  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29523  * @cfg {Boolean} isDocument (true|false) default false
29524  * @cfg {String} url action url
29525  * @cfg {String} paramName default 'imageUpload'
29526  * @cfg {String} method default POST
29527  * @cfg {Boolean} loadMask (true|false) default true
29528  * @cfg {Boolean} loadingText default 'Loading...'
29529  * 
29530  * @constructor
29531  * Create a new UploadCropbox
29532  * @param {Object} config The config object
29533  */
29534
29535 Roo.bootstrap.UploadCropbox = function(config){
29536     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29537     
29538     this.addEvents({
29539         /**
29540          * @event beforeselectfile
29541          * Fire before select file
29542          * @param {Roo.bootstrap.UploadCropbox} this
29543          */
29544         "beforeselectfile" : true,
29545         /**
29546          * @event initial
29547          * Fire after initEvent
29548          * @param {Roo.bootstrap.UploadCropbox} this
29549          */
29550         "initial" : true,
29551         /**
29552          * @event crop
29553          * Fire after initEvent
29554          * @param {Roo.bootstrap.UploadCropbox} this
29555          * @param {String} data
29556          */
29557         "crop" : true,
29558         /**
29559          * @event prepare
29560          * Fire when preparing the file data
29561          * @param {Roo.bootstrap.UploadCropbox} this
29562          * @param {Object} file
29563          */
29564         "prepare" : true,
29565         /**
29566          * @event exception
29567          * Fire when get exception
29568          * @param {Roo.bootstrap.UploadCropbox} this
29569          * @param {XMLHttpRequest} xhr
29570          */
29571         "exception" : true,
29572         /**
29573          * @event beforeloadcanvas
29574          * Fire before load the canvas
29575          * @param {Roo.bootstrap.UploadCropbox} this
29576          * @param {String} src
29577          */
29578         "beforeloadcanvas" : true,
29579         /**
29580          * @event trash
29581          * Fire when trash image
29582          * @param {Roo.bootstrap.UploadCropbox} this
29583          */
29584         "trash" : true,
29585         /**
29586          * @event download
29587          * Fire when download the image
29588          * @param {Roo.bootstrap.UploadCropbox} this
29589          */
29590         "download" : true,
29591         /**
29592          * @event footerbuttonclick
29593          * Fire when footerbuttonclick
29594          * @param {Roo.bootstrap.UploadCropbox} this
29595          * @param {String} type
29596          */
29597         "footerbuttonclick" : true,
29598         /**
29599          * @event resize
29600          * Fire when resize
29601          * @param {Roo.bootstrap.UploadCropbox} this
29602          */
29603         "resize" : true,
29604         /**
29605          * @event rotate
29606          * Fire when rotate the image
29607          * @param {Roo.bootstrap.UploadCropbox} this
29608          * @param {String} pos
29609          */
29610         "rotate" : true,
29611         /**
29612          * @event inspect
29613          * Fire when inspect the file
29614          * @param {Roo.bootstrap.UploadCropbox} this
29615          * @param {Object} file
29616          */
29617         "inspect" : true,
29618         /**
29619          * @event upload
29620          * Fire when xhr upload the file
29621          * @param {Roo.bootstrap.UploadCropbox} this
29622          * @param {Object} data
29623          */
29624         "upload" : true,
29625         /**
29626          * @event arrange
29627          * Fire when arrange the file data
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          * @param {Object} formData
29630          */
29631         "arrange" : true
29632     });
29633     
29634     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29635 };
29636
29637 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29638     
29639     emptyText : 'Click to upload image',
29640     rotateNotify : 'Image is too small to rotate',
29641     errorTimeout : 3000,
29642     scale : 0,
29643     baseScale : 1,
29644     rotate : 0,
29645     dragable : false,
29646     pinching : false,
29647     mouseX : 0,
29648     mouseY : 0,
29649     cropData : false,
29650     minWidth : 300,
29651     minHeight : 300,
29652     file : false,
29653     exif : {},
29654     baseRotate : 1,
29655     cropType : 'image/jpeg',
29656     buttons : false,
29657     canvasLoaded : false,
29658     isDocument : false,
29659     method : 'POST',
29660     paramName : 'imageUpload',
29661     loadMask : true,
29662     loadingText : 'Loading...',
29663     maskEl : false,
29664     
29665     getAutoCreate : function()
29666     {
29667         var cfg = {
29668             tag : 'div',
29669             cls : 'roo-upload-cropbox',
29670             cn : [
29671                 {
29672                     tag : 'input',
29673                     cls : 'roo-upload-cropbox-selector',
29674                     type : 'file'
29675                 },
29676                 {
29677                     tag : 'div',
29678                     cls : 'roo-upload-cropbox-body',
29679                     style : 'cursor:pointer',
29680                     cn : [
29681                         {
29682                             tag : 'div',
29683                             cls : 'roo-upload-cropbox-preview'
29684                         },
29685                         {
29686                             tag : 'div',
29687                             cls : 'roo-upload-cropbox-thumb'
29688                         },
29689                         {
29690                             tag : 'div',
29691                             cls : 'roo-upload-cropbox-empty-notify',
29692                             html : this.emptyText
29693                         },
29694                         {
29695                             tag : 'div',
29696                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29697                             html : this.rotateNotify
29698                         }
29699                     ]
29700                 },
29701                 {
29702                     tag : 'div',
29703                     cls : 'roo-upload-cropbox-footer',
29704                     cn : {
29705                         tag : 'div',
29706                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29707                         cn : []
29708                     }
29709                 }
29710             ]
29711         };
29712         
29713         return cfg;
29714     },
29715     
29716     onRender : function(ct, position)
29717     {
29718         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29719         
29720         if (this.buttons.length) {
29721             
29722             Roo.each(this.buttons, function(bb) {
29723                 
29724                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29725                 
29726                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29727                 
29728             }, this);
29729         }
29730         
29731         if(this.loadMask){
29732             this.maskEl = this.el;
29733         }
29734     },
29735     
29736     initEvents : function()
29737     {
29738         this.urlAPI = (window.createObjectURL && window) || 
29739                                 (window.URL && URL.revokeObjectURL && URL) || 
29740                                 (window.webkitURL && webkitURL);
29741                         
29742         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29743         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29744         
29745         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29746         this.selectorEl.hide();
29747         
29748         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29749         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29750         
29751         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29752         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29753         this.thumbEl.hide();
29754         
29755         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29756         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29757         
29758         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29759         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29760         this.errorEl.hide();
29761         
29762         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29763         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29764         this.footerEl.hide();
29765         
29766         this.setThumbBoxSize();
29767         
29768         this.bind();
29769         
29770         this.resize();
29771         
29772         this.fireEvent('initial', this);
29773     },
29774
29775     bind : function()
29776     {
29777         var _this = this;
29778         
29779         window.addEventListener("resize", function() { _this.resize(); } );
29780         
29781         this.bodyEl.on('click', this.beforeSelectFile, this);
29782         
29783         if(Roo.isTouch){
29784             this.bodyEl.on('touchstart', this.onTouchStart, this);
29785             this.bodyEl.on('touchmove', this.onTouchMove, this);
29786             this.bodyEl.on('touchend', this.onTouchEnd, this);
29787         }
29788         
29789         if(!Roo.isTouch){
29790             this.bodyEl.on('mousedown', this.onMouseDown, this);
29791             this.bodyEl.on('mousemove', this.onMouseMove, this);
29792             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29793             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29794             Roo.get(document).on('mouseup', this.onMouseUp, this);
29795         }
29796         
29797         this.selectorEl.on('change', this.onFileSelected, this);
29798     },
29799     
29800     reset : function()
29801     {    
29802         this.scale = 0;
29803         this.baseScale = 1;
29804         this.rotate = 0;
29805         this.baseRotate = 1;
29806         this.dragable = false;
29807         this.pinching = false;
29808         this.mouseX = 0;
29809         this.mouseY = 0;
29810         this.cropData = false;
29811         this.notifyEl.dom.innerHTML = this.emptyText;
29812         
29813         this.selectorEl.dom.value = '';
29814         
29815     },
29816     
29817     resize : function()
29818     {
29819         if(this.fireEvent('resize', this) != false){
29820             this.setThumbBoxPosition();
29821             this.setCanvasPosition();
29822         }
29823     },
29824     
29825     onFooterButtonClick : function(e, el, o, type)
29826     {
29827         switch (type) {
29828             case 'rotate-left' :
29829                 this.onRotateLeft(e);
29830                 break;
29831             case 'rotate-right' :
29832                 this.onRotateRight(e);
29833                 break;
29834             case 'picture' :
29835                 this.beforeSelectFile(e);
29836                 break;
29837             case 'trash' :
29838                 this.trash(e);
29839                 break;
29840             case 'crop' :
29841                 this.crop(e);
29842                 break;
29843             case 'download' :
29844                 this.download(e);
29845                 break;
29846             default :
29847                 break;
29848         }
29849         
29850         this.fireEvent('footerbuttonclick', this, type);
29851     },
29852     
29853     beforeSelectFile : function(e)
29854     {
29855         e.preventDefault();
29856         
29857         if(this.fireEvent('beforeselectfile', this) != false){
29858             this.selectorEl.dom.click();
29859         }
29860     },
29861     
29862     onFileSelected : function(e)
29863     {
29864         e.preventDefault();
29865         
29866         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29867             return;
29868         }
29869         
29870         var file = this.selectorEl.dom.files[0];
29871         
29872         if(this.fireEvent('inspect', this, file) != false){
29873             this.prepare(file);
29874         }
29875         
29876     },
29877     
29878     trash : function(e)
29879     {
29880         this.fireEvent('trash', this);
29881     },
29882     
29883     download : function(e)
29884     {
29885         this.fireEvent('download', this);
29886     },
29887     
29888     loadCanvas : function(src)
29889     {   
29890         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29891             
29892             this.reset();
29893             
29894             this.imageEl = document.createElement('img');
29895             
29896             var _this = this;
29897             
29898             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29899             
29900             this.imageEl.src = src;
29901         }
29902     },
29903     
29904     onLoadCanvas : function()
29905     {   
29906         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29907         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29908         
29909         this.bodyEl.un('click', this.beforeSelectFile, this);
29910         
29911         this.notifyEl.hide();
29912         this.thumbEl.show();
29913         this.footerEl.show();
29914         
29915         this.baseRotateLevel();
29916         
29917         if(this.isDocument){
29918             this.setThumbBoxSize();
29919         }
29920         
29921         this.setThumbBoxPosition();
29922         
29923         this.baseScaleLevel();
29924         
29925         this.draw();
29926         
29927         this.resize();
29928         
29929         this.canvasLoaded = true;
29930         
29931         if(this.loadMask){
29932             this.maskEl.unmask();
29933         }
29934         
29935     },
29936     
29937     setCanvasPosition : function()
29938     {   
29939         if(!this.canvasEl){
29940             return;
29941         }
29942         
29943         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29944         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29945         
29946         this.previewEl.setLeft(pw);
29947         this.previewEl.setTop(ph);
29948         
29949     },
29950     
29951     onMouseDown : function(e)
29952     {   
29953         e.stopEvent();
29954         
29955         this.dragable = true;
29956         this.pinching = false;
29957         
29958         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29959             this.dragable = false;
29960             return;
29961         }
29962         
29963         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29964         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29965         
29966     },
29967     
29968     onMouseMove : function(e)
29969     {   
29970         e.stopEvent();
29971         
29972         if(!this.canvasLoaded){
29973             return;
29974         }
29975         
29976         if (!this.dragable){
29977             return;
29978         }
29979         
29980         var minX = Math.ceil(this.thumbEl.getLeft(true));
29981         var minY = Math.ceil(this.thumbEl.getTop(true));
29982         
29983         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29984         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29985         
29986         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29987         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29988         
29989         x = x - this.mouseX;
29990         y = y - this.mouseY;
29991         
29992         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29993         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29994         
29995         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29996         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29997         
29998         this.previewEl.setLeft(bgX);
29999         this.previewEl.setTop(bgY);
30000         
30001         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30002         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30003     },
30004     
30005     onMouseUp : function(e)
30006     {   
30007         e.stopEvent();
30008         
30009         this.dragable = false;
30010     },
30011     
30012     onMouseWheel : function(e)
30013     {   
30014         e.stopEvent();
30015         
30016         this.startScale = this.scale;
30017         
30018         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30019         
30020         if(!this.zoomable()){
30021             this.scale = this.startScale;
30022             return;
30023         }
30024         
30025         this.draw();
30026         
30027         return;
30028     },
30029     
30030     zoomable : function()
30031     {
30032         var minScale = this.thumbEl.getWidth() / this.minWidth;
30033         
30034         if(this.minWidth < this.minHeight){
30035             minScale = this.thumbEl.getHeight() / this.minHeight;
30036         }
30037         
30038         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30039         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30040         
30041         if(
30042                 this.isDocument &&
30043                 (this.rotate == 0 || this.rotate == 180) && 
30044                 (
30045                     width > this.imageEl.OriginWidth || 
30046                     height > this.imageEl.OriginHeight ||
30047                     (width < this.minWidth && height < this.minHeight)
30048                 )
30049         ){
30050             return false;
30051         }
30052         
30053         if(
30054                 this.isDocument &&
30055                 (this.rotate == 90 || this.rotate == 270) && 
30056                 (
30057                     width > this.imageEl.OriginWidth || 
30058                     height > this.imageEl.OriginHeight ||
30059                     (width < this.minHeight && height < this.minWidth)
30060                 )
30061         ){
30062             return false;
30063         }
30064         
30065         if(
30066                 !this.isDocument &&
30067                 (this.rotate == 0 || this.rotate == 180) && 
30068                 (
30069                     width < this.minWidth || 
30070                     width > this.imageEl.OriginWidth || 
30071                     height < this.minHeight || 
30072                     height > this.imageEl.OriginHeight
30073                 )
30074         ){
30075             return false;
30076         }
30077         
30078         if(
30079                 !this.isDocument &&
30080                 (this.rotate == 90 || this.rotate == 270) && 
30081                 (
30082                     width < this.minHeight || 
30083                     width > this.imageEl.OriginWidth || 
30084                     height < this.minWidth || 
30085                     height > this.imageEl.OriginHeight
30086                 )
30087         ){
30088             return false;
30089         }
30090         
30091         return true;
30092         
30093     },
30094     
30095     onRotateLeft : function(e)
30096     {   
30097         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30098             
30099             var minScale = this.thumbEl.getWidth() / this.minWidth;
30100             
30101             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30102             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30103             
30104             this.startScale = this.scale;
30105             
30106             while (this.getScaleLevel() < minScale){
30107             
30108                 this.scale = this.scale + 1;
30109                 
30110                 if(!this.zoomable()){
30111                     break;
30112                 }
30113                 
30114                 if(
30115                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30116                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30117                 ){
30118                     continue;
30119                 }
30120                 
30121                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30122
30123                 this.draw();
30124                 
30125                 return;
30126             }
30127             
30128             this.scale = this.startScale;
30129             
30130             this.onRotateFail();
30131             
30132             return false;
30133         }
30134         
30135         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30136
30137         if(this.isDocument){
30138             this.setThumbBoxSize();
30139             this.setThumbBoxPosition();
30140             this.setCanvasPosition();
30141         }
30142         
30143         this.draw();
30144         
30145         this.fireEvent('rotate', this, 'left');
30146         
30147     },
30148     
30149     onRotateRight : function(e)
30150     {
30151         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30152             
30153             var minScale = this.thumbEl.getWidth() / this.minWidth;
30154         
30155             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30156             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30157             
30158             this.startScale = this.scale;
30159             
30160             while (this.getScaleLevel() < minScale){
30161             
30162                 this.scale = this.scale + 1;
30163                 
30164                 if(!this.zoomable()){
30165                     break;
30166                 }
30167                 
30168                 if(
30169                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30170                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30171                 ){
30172                     continue;
30173                 }
30174                 
30175                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30176
30177                 this.draw();
30178                 
30179                 return;
30180             }
30181             
30182             this.scale = this.startScale;
30183             
30184             this.onRotateFail();
30185             
30186             return false;
30187         }
30188         
30189         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30190
30191         if(this.isDocument){
30192             this.setThumbBoxSize();
30193             this.setThumbBoxPosition();
30194             this.setCanvasPosition();
30195         }
30196         
30197         this.draw();
30198         
30199         this.fireEvent('rotate', this, 'right');
30200     },
30201     
30202     onRotateFail : function()
30203     {
30204         this.errorEl.show(true);
30205         
30206         var _this = this;
30207         
30208         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30209     },
30210     
30211     draw : function()
30212     {
30213         this.previewEl.dom.innerHTML = '';
30214         
30215         var canvasEl = document.createElement("canvas");
30216         
30217         var contextEl = canvasEl.getContext("2d");
30218         
30219         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30220         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30221         var center = this.imageEl.OriginWidth / 2;
30222         
30223         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30224             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30225             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30226             center = this.imageEl.OriginHeight / 2;
30227         }
30228         
30229         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30230         
30231         contextEl.translate(center, center);
30232         contextEl.rotate(this.rotate * Math.PI / 180);
30233
30234         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30235         
30236         this.canvasEl = document.createElement("canvas");
30237         
30238         this.contextEl = this.canvasEl.getContext("2d");
30239         
30240         switch (this.rotate) {
30241             case 0 :
30242                 
30243                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30244                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30245                 
30246                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30247                 
30248                 break;
30249             case 90 : 
30250                 
30251                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30252                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30253                 
30254                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30255                     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);
30256                     break;
30257                 }
30258                 
30259                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30260                 
30261                 break;
30262             case 180 :
30263                 
30264                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30265                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30266                 
30267                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30268                     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);
30269                     break;
30270                 }
30271                 
30272                 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);
30273                 
30274                 break;
30275             case 270 :
30276                 
30277                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30278                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30279         
30280                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30281                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30282                     break;
30283                 }
30284                 
30285                 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);
30286                 
30287                 break;
30288             default : 
30289                 break;
30290         }
30291         
30292         this.previewEl.appendChild(this.canvasEl);
30293         
30294         this.setCanvasPosition();
30295     },
30296     
30297     crop : function()
30298     {
30299         if(!this.canvasLoaded){
30300             return;
30301         }
30302         
30303         var imageCanvas = document.createElement("canvas");
30304         
30305         var imageContext = imageCanvas.getContext("2d");
30306         
30307         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30308         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30309         
30310         var center = imageCanvas.width / 2;
30311         
30312         imageContext.translate(center, center);
30313         
30314         imageContext.rotate(this.rotate * Math.PI / 180);
30315         
30316         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30317         
30318         var canvas = document.createElement("canvas");
30319         
30320         var context = canvas.getContext("2d");
30321                 
30322         canvas.width = this.minWidth;
30323         canvas.height = this.minHeight;
30324
30325         switch (this.rotate) {
30326             case 0 :
30327                 
30328                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30329                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30330                 
30331                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30332                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30333                 
30334                 var targetWidth = this.minWidth - 2 * x;
30335                 var targetHeight = this.minHeight - 2 * y;
30336                 
30337                 var scale = 1;
30338                 
30339                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30340                     scale = targetWidth / width;
30341                 }
30342                 
30343                 if(x > 0 && y == 0){
30344                     scale = targetHeight / height;
30345                 }
30346                 
30347                 if(x > 0 && y > 0){
30348                     scale = targetWidth / width;
30349                     
30350                     if(width < height){
30351                         scale = targetHeight / height;
30352                     }
30353                 }
30354                 
30355                 context.scale(scale, scale);
30356                 
30357                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30358                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30359
30360                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30361                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30362
30363                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30364                 
30365                 break;
30366             case 90 : 
30367                 
30368                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30369                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30370                 
30371                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30372                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30373                 
30374                 var targetWidth = this.minWidth - 2 * x;
30375                 var targetHeight = this.minHeight - 2 * y;
30376                 
30377                 var scale = 1;
30378                 
30379                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30380                     scale = targetWidth / width;
30381                 }
30382                 
30383                 if(x > 0 && y == 0){
30384                     scale = targetHeight / height;
30385                 }
30386                 
30387                 if(x > 0 && y > 0){
30388                     scale = targetWidth / width;
30389                     
30390                     if(width < height){
30391                         scale = targetHeight / height;
30392                     }
30393                 }
30394                 
30395                 context.scale(scale, scale);
30396                 
30397                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30398                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30399
30400                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30401                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30402                 
30403                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30404                 
30405                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30406                 
30407                 break;
30408             case 180 :
30409                 
30410                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30411                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30412                 
30413                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30414                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30415                 
30416                 var targetWidth = this.minWidth - 2 * x;
30417                 var targetHeight = this.minHeight - 2 * y;
30418                 
30419                 var scale = 1;
30420                 
30421                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30422                     scale = targetWidth / width;
30423                 }
30424                 
30425                 if(x > 0 && y == 0){
30426                     scale = targetHeight / height;
30427                 }
30428                 
30429                 if(x > 0 && y > 0){
30430                     scale = targetWidth / width;
30431                     
30432                     if(width < height){
30433                         scale = targetHeight / height;
30434                     }
30435                 }
30436                 
30437                 context.scale(scale, scale);
30438                 
30439                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30440                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30441
30442                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30443                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30444
30445                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30446                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30447                 
30448                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30449                 
30450                 break;
30451             case 270 :
30452                 
30453                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30454                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30455                 
30456                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30457                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30458                 
30459                 var targetWidth = this.minWidth - 2 * x;
30460                 var targetHeight = this.minHeight - 2 * y;
30461                 
30462                 var scale = 1;
30463                 
30464                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30465                     scale = targetWidth / width;
30466                 }
30467                 
30468                 if(x > 0 && y == 0){
30469                     scale = targetHeight / height;
30470                 }
30471                 
30472                 if(x > 0 && y > 0){
30473                     scale = targetWidth / width;
30474                     
30475                     if(width < height){
30476                         scale = targetHeight / height;
30477                     }
30478                 }
30479                 
30480                 context.scale(scale, scale);
30481                 
30482                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30483                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30484
30485                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30486                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30487                 
30488                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30489                 
30490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30491                 
30492                 break;
30493             default : 
30494                 break;
30495         }
30496         
30497         this.cropData = canvas.toDataURL(this.cropType);
30498         
30499         if(this.fireEvent('crop', this, this.cropData) !== false){
30500             this.process(this.file, this.cropData);
30501         }
30502         
30503         return;
30504         
30505     },
30506     
30507     setThumbBoxSize : function()
30508     {
30509         var width, height;
30510         
30511         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30512             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30513             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30514             
30515             this.minWidth = width;
30516             this.minHeight = height;
30517             
30518             if(this.rotate == 90 || this.rotate == 270){
30519                 this.minWidth = height;
30520                 this.minHeight = width;
30521             }
30522         }
30523         
30524         height = 300;
30525         width = Math.ceil(this.minWidth * height / this.minHeight);
30526         
30527         if(this.minWidth > this.minHeight){
30528             width = 300;
30529             height = Math.ceil(this.minHeight * width / this.minWidth);
30530         }
30531         
30532         this.thumbEl.setStyle({
30533             width : width + 'px',
30534             height : height + 'px'
30535         });
30536
30537         return;
30538             
30539     },
30540     
30541     setThumbBoxPosition : function()
30542     {
30543         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30544         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30545         
30546         this.thumbEl.setLeft(x);
30547         this.thumbEl.setTop(y);
30548         
30549     },
30550     
30551     baseRotateLevel : function()
30552     {
30553         this.baseRotate = 1;
30554         
30555         if(
30556                 typeof(this.exif) != 'undefined' &&
30557                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30558                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30559         ){
30560             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30561         }
30562         
30563         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30564         
30565     },
30566     
30567     baseScaleLevel : function()
30568     {
30569         var width, height;
30570         
30571         if(this.isDocument){
30572             
30573             if(this.baseRotate == 6 || this.baseRotate == 8){
30574             
30575                 height = this.thumbEl.getHeight();
30576                 this.baseScale = height / this.imageEl.OriginWidth;
30577
30578                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30579                     width = this.thumbEl.getWidth();
30580                     this.baseScale = width / this.imageEl.OriginHeight;
30581                 }
30582
30583                 return;
30584             }
30585
30586             height = this.thumbEl.getHeight();
30587             this.baseScale = height / this.imageEl.OriginHeight;
30588
30589             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30590                 width = this.thumbEl.getWidth();
30591                 this.baseScale = width / this.imageEl.OriginWidth;
30592             }
30593
30594             return;
30595         }
30596         
30597         if(this.baseRotate == 6 || this.baseRotate == 8){
30598             
30599             width = this.thumbEl.getHeight();
30600             this.baseScale = width / this.imageEl.OriginHeight;
30601             
30602             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30603                 height = this.thumbEl.getWidth();
30604                 this.baseScale = height / this.imageEl.OriginHeight;
30605             }
30606             
30607             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30608                 height = this.thumbEl.getWidth();
30609                 this.baseScale = height / this.imageEl.OriginHeight;
30610                 
30611                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30612                     width = this.thumbEl.getHeight();
30613                     this.baseScale = width / this.imageEl.OriginWidth;
30614                 }
30615             }
30616             
30617             return;
30618         }
30619         
30620         width = this.thumbEl.getWidth();
30621         this.baseScale = width / this.imageEl.OriginWidth;
30622         
30623         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30624             height = this.thumbEl.getHeight();
30625             this.baseScale = height / this.imageEl.OriginHeight;
30626         }
30627         
30628         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30629             
30630             height = this.thumbEl.getHeight();
30631             this.baseScale = height / this.imageEl.OriginHeight;
30632             
30633             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30634                 width = this.thumbEl.getWidth();
30635                 this.baseScale = width / this.imageEl.OriginWidth;
30636             }
30637             
30638         }
30639         
30640         return;
30641     },
30642     
30643     getScaleLevel : function()
30644     {
30645         return this.baseScale * Math.pow(1.1, this.scale);
30646     },
30647     
30648     onTouchStart : function(e)
30649     {
30650         if(!this.canvasLoaded){
30651             this.beforeSelectFile(e);
30652             return;
30653         }
30654         
30655         var touches = e.browserEvent.touches;
30656         
30657         if(!touches){
30658             return;
30659         }
30660         
30661         if(touches.length == 1){
30662             this.onMouseDown(e);
30663             return;
30664         }
30665         
30666         if(touches.length != 2){
30667             return;
30668         }
30669         
30670         var coords = [];
30671         
30672         for(var i = 0, finger; finger = touches[i]; i++){
30673             coords.push(finger.pageX, finger.pageY);
30674         }
30675         
30676         var x = Math.pow(coords[0] - coords[2], 2);
30677         var y = Math.pow(coords[1] - coords[3], 2);
30678         
30679         this.startDistance = Math.sqrt(x + y);
30680         
30681         this.startScale = this.scale;
30682         
30683         this.pinching = true;
30684         this.dragable = false;
30685         
30686     },
30687     
30688     onTouchMove : function(e)
30689     {
30690         if(!this.pinching && !this.dragable){
30691             return;
30692         }
30693         
30694         var touches = e.browserEvent.touches;
30695         
30696         if(!touches){
30697             return;
30698         }
30699         
30700         if(this.dragable){
30701             this.onMouseMove(e);
30702             return;
30703         }
30704         
30705         var coords = [];
30706         
30707         for(var i = 0, finger; finger = touches[i]; i++){
30708             coords.push(finger.pageX, finger.pageY);
30709         }
30710         
30711         var x = Math.pow(coords[0] - coords[2], 2);
30712         var y = Math.pow(coords[1] - coords[3], 2);
30713         
30714         this.endDistance = Math.sqrt(x + y);
30715         
30716         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30717         
30718         if(!this.zoomable()){
30719             this.scale = this.startScale;
30720             return;
30721         }
30722         
30723         this.draw();
30724         
30725     },
30726     
30727     onTouchEnd : function(e)
30728     {
30729         this.pinching = false;
30730         this.dragable = false;
30731         
30732     },
30733     
30734     process : function(file, crop)
30735     {
30736         if(this.loadMask){
30737             this.maskEl.mask(this.loadingText);
30738         }
30739         
30740         this.xhr = new XMLHttpRequest();
30741         
30742         file.xhr = this.xhr;
30743
30744         this.xhr.open(this.method, this.url, true);
30745         
30746         var headers = {
30747             "Accept": "application/json",
30748             "Cache-Control": "no-cache",
30749             "X-Requested-With": "XMLHttpRequest"
30750         };
30751         
30752         for (var headerName in headers) {
30753             var headerValue = headers[headerName];
30754             if (headerValue) {
30755                 this.xhr.setRequestHeader(headerName, headerValue);
30756             }
30757         }
30758         
30759         var _this = this;
30760         
30761         this.xhr.onload = function()
30762         {
30763             _this.xhrOnLoad(_this.xhr);
30764         }
30765         
30766         this.xhr.onerror = function()
30767         {
30768             _this.xhrOnError(_this.xhr);
30769         }
30770         
30771         var formData = new FormData();
30772
30773         formData.append('returnHTML', 'NO');
30774         
30775         if(crop){
30776             formData.append('crop', crop);
30777         }
30778         
30779         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30780             formData.append(this.paramName, file, file.name);
30781         }
30782         
30783         if(typeof(file.filename) != 'undefined'){
30784             formData.append('filename', file.filename);
30785         }
30786         
30787         if(typeof(file.mimetype) != 'undefined'){
30788             formData.append('mimetype', file.mimetype);
30789         }
30790         
30791         if(this.fireEvent('arrange', this, formData) != false){
30792             this.xhr.send(formData);
30793         };
30794     },
30795     
30796     xhrOnLoad : function(xhr)
30797     {
30798         if(this.loadMask){
30799             this.maskEl.unmask();
30800         }
30801         
30802         if (xhr.readyState !== 4) {
30803             this.fireEvent('exception', this, xhr);
30804             return;
30805         }
30806
30807         var response = Roo.decode(xhr.responseText);
30808         
30809         if(!response.success){
30810             this.fireEvent('exception', this, xhr);
30811             return;
30812         }
30813         
30814         var response = Roo.decode(xhr.responseText);
30815         
30816         this.fireEvent('upload', this, response);
30817         
30818     },
30819     
30820     xhrOnError : function()
30821     {
30822         if(this.loadMask){
30823             this.maskEl.unmask();
30824         }
30825         
30826         Roo.log('xhr on error');
30827         
30828         var response = Roo.decode(xhr.responseText);
30829           
30830         Roo.log(response);
30831         
30832     },
30833     
30834     prepare : function(file)
30835     {   
30836         if(this.loadMask){
30837             this.maskEl.mask(this.loadingText);
30838         }
30839         
30840         this.file = false;
30841         this.exif = {};
30842         
30843         if(typeof(file) === 'string'){
30844             this.loadCanvas(file);
30845             return;
30846         }
30847         
30848         if(!file || !this.urlAPI){
30849             return;
30850         }
30851         
30852         this.file = file;
30853         this.cropType = file.type;
30854         
30855         var _this = this;
30856         
30857         if(this.fireEvent('prepare', this, this.file) != false){
30858             
30859             var reader = new FileReader();
30860             
30861             reader.onload = function (e) {
30862                 if (e.target.error) {
30863                     Roo.log(e.target.error);
30864                     return;
30865                 }
30866                 
30867                 var buffer = e.target.result,
30868                     dataView = new DataView(buffer),
30869                     offset = 2,
30870                     maxOffset = dataView.byteLength - 4,
30871                     markerBytes,
30872                     markerLength;
30873                 
30874                 if (dataView.getUint16(0) === 0xffd8) {
30875                     while (offset < maxOffset) {
30876                         markerBytes = dataView.getUint16(offset);
30877                         
30878                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30879                             markerLength = dataView.getUint16(offset + 2) + 2;
30880                             if (offset + markerLength > dataView.byteLength) {
30881                                 Roo.log('Invalid meta data: Invalid segment size.');
30882                                 break;
30883                             }
30884                             
30885                             if(markerBytes == 0xffe1){
30886                                 _this.parseExifData(
30887                                     dataView,
30888                                     offset,
30889                                     markerLength
30890                                 );
30891                             }
30892                             
30893                             offset += markerLength;
30894                             
30895                             continue;
30896                         }
30897                         
30898                         break;
30899                     }
30900                     
30901                 }
30902                 
30903                 var url = _this.urlAPI.createObjectURL(_this.file);
30904                 
30905                 _this.loadCanvas(url);
30906                 
30907                 return;
30908             }
30909             
30910             reader.readAsArrayBuffer(this.file);
30911             
30912         }
30913         
30914     },
30915     
30916     parseExifData : function(dataView, offset, length)
30917     {
30918         var tiffOffset = offset + 10,
30919             littleEndian,
30920             dirOffset;
30921     
30922         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30923             // No Exif data, might be XMP data instead
30924             return;
30925         }
30926         
30927         // Check for the ASCII code for "Exif" (0x45786966):
30928         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30929             // No Exif data, might be XMP data instead
30930             return;
30931         }
30932         if (tiffOffset + 8 > dataView.byteLength) {
30933             Roo.log('Invalid Exif data: Invalid segment size.');
30934             return;
30935         }
30936         // Check for the two null bytes:
30937         if (dataView.getUint16(offset + 8) !== 0x0000) {
30938             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30939             return;
30940         }
30941         // Check the byte alignment:
30942         switch (dataView.getUint16(tiffOffset)) {
30943         case 0x4949:
30944             littleEndian = true;
30945             break;
30946         case 0x4D4D:
30947             littleEndian = false;
30948             break;
30949         default:
30950             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30951             return;
30952         }
30953         // Check for the TIFF tag marker (0x002A):
30954         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30955             Roo.log('Invalid Exif data: Missing TIFF marker.');
30956             return;
30957         }
30958         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30959         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30960         
30961         this.parseExifTags(
30962             dataView,
30963             tiffOffset,
30964             tiffOffset + dirOffset,
30965             littleEndian
30966         );
30967     },
30968     
30969     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30970     {
30971         var tagsNumber,
30972             dirEndOffset,
30973             i;
30974         if (dirOffset + 6 > dataView.byteLength) {
30975             Roo.log('Invalid Exif data: Invalid directory offset.');
30976             return;
30977         }
30978         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30979         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30980         if (dirEndOffset + 4 > dataView.byteLength) {
30981             Roo.log('Invalid Exif data: Invalid directory size.');
30982             return;
30983         }
30984         for (i = 0; i < tagsNumber; i += 1) {
30985             this.parseExifTag(
30986                 dataView,
30987                 tiffOffset,
30988                 dirOffset + 2 + 12 * i, // tag offset
30989                 littleEndian
30990             );
30991         }
30992         // Return the offset to the next directory:
30993         return dataView.getUint32(dirEndOffset, littleEndian);
30994     },
30995     
30996     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30997     {
30998         var tag = dataView.getUint16(offset, littleEndian);
30999         
31000         this.exif[tag] = this.getExifValue(
31001             dataView,
31002             tiffOffset,
31003             offset,
31004             dataView.getUint16(offset + 2, littleEndian), // tag type
31005             dataView.getUint32(offset + 4, littleEndian), // tag length
31006             littleEndian
31007         );
31008     },
31009     
31010     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31011     {
31012         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31013             tagSize,
31014             dataOffset,
31015             values,
31016             i,
31017             str,
31018             c;
31019     
31020         if (!tagType) {
31021             Roo.log('Invalid Exif data: Invalid tag type.');
31022             return;
31023         }
31024         
31025         tagSize = tagType.size * length;
31026         // Determine if the value is contained in the dataOffset bytes,
31027         // or if the value at the dataOffset is a pointer to the actual data:
31028         dataOffset = tagSize > 4 ?
31029                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31030         if (dataOffset + tagSize > dataView.byteLength) {
31031             Roo.log('Invalid Exif data: Invalid data offset.');
31032             return;
31033         }
31034         if (length === 1) {
31035             return tagType.getValue(dataView, dataOffset, littleEndian);
31036         }
31037         values = [];
31038         for (i = 0; i < length; i += 1) {
31039             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31040         }
31041         
31042         if (tagType.ascii) {
31043             str = '';
31044             // Concatenate the chars:
31045             for (i = 0; i < values.length; i += 1) {
31046                 c = values[i];
31047                 // Ignore the terminating NULL byte(s):
31048                 if (c === '\u0000') {
31049                     break;
31050                 }
31051                 str += c;
31052             }
31053             return str;
31054         }
31055         return values;
31056     }
31057     
31058 });
31059
31060 Roo.apply(Roo.bootstrap.UploadCropbox, {
31061     tags : {
31062         'Orientation': 0x0112
31063     },
31064     
31065     Orientation: {
31066             1: 0, //'top-left',
31067 //            2: 'top-right',
31068             3: 180, //'bottom-right',
31069 //            4: 'bottom-left',
31070 //            5: 'left-top',
31071             6: 90, //'right-top',
31072 //            7: 'right-bottom',
31073             8: 270 //'left-bottom'
31074     },
31075     
31076     exifTagTypes : {
31077         // byte, 8-bit unsigned int:
31078         1: {
31079             getValue: function (dataView, dataOffset) {
31080                 return dataView.getUint8(dataOffset);
31081             },
31082             size: 1
31083         },
31084         // ascii, 8-bit byte:
31085         2: {
31086             getValue: function (dataView, dataOffset) {
31087                 return String.fromCharCode(dataView.getUint8(dataOffset));
31088             },
31089             size: 1,
31090             ascii: true
31091         },
31092         // short, 16 bit int:
31093         3: {
31094             getValue: function (dataView, dataOffset, littleEndian) {
31095                 return dataView.getUint16(dataOffset, littleEndian);
31096             },
31097             size: 2
31098         },
31099         // long, 32 bit int:
31100         4: {
31101             getValue: function (dataView, dataOffset, littleEndian) {
31102                 return dataView.getUint32(dataOffset, littleEndian);
31103             },
31104             size: 4
31105         },
31106         // rational = two long values, first is numerator, second is denominator:
31107         5: {
31108             getValue: function (dataView, dataOffset, littleEndian) {
31109                 return dataView.getUint32(dataOffset, littleEndian) /
31110                     dataView.getUint32(dataOffset + 4, littleEndian);
31111             },
31112             size: 8
31113         },
31114         // slong, 32 bit signed int:
31115         9: {
31116             getValue: function (dataView, dataOffset, littleEndian) {
31117                 return dataView.getInt32(dataOffset, littleEndian);
31118             },
31119             size: 4
31120         },
31121         // srational, two slongs, first is numerator, second is denominator:
31122         10: {
31123             getValue: function (dataView, dataOffset, littleEndian) {
31124                 return dataView.getInt32(dataOffset, littleEndian) /
31125                     dataView.getInt32(dataOffset + 4, littleEndian);
31126             },
31127             size: 8
31128         }
31129     },
31130     
31131     footer : {
31132         STANDARD : [
31133             {
31134                 tag : 'div',
31135                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31136                 action : 'rotate-left',
31137                 cn : [
31138                     {
31139                         tag : 'button',
31140                         cls : 'btn btn-default',
31141                         html : '<i class="fa fa-undo"></i>'
31142                     }
31143                 ]
31144             },
31145             {
31146                 tag : 'div',
31147                 cls : 'btn-group roo-upload-cropbox-picture',
31148                 action : 'picture',
31149                 cn : [
31150                     {
31151                         tag : 'button',
31152                         cls : 'btn btn-default',
31153                         html : '<i class="fa fa-picture-o"></i>'
31154                     }
31155                 ]
31156             },
31157             {
31158                 tag : 'div',
31159                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31160                 action : 'rotate-right',
31161                 cn : [
31162                     {
31163                         tag : 'button',
31164                         cls : 'btn btn-default',
31165                         html : '<i class="fa fa-repeat"></i>'
31166                     }
31167                 ]
31168             }
31169         ],
31170         DOCUMENT : [
31171             {
31172                 tag : 'div',
31173                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31174                 action : 'rotate-left',
31175                 cn : [
31176                     {
31177                         tag : 'button',
31178                         cls : 'btn btn-default',
31179                         html : '<i class="fa fa-undo"></i>'
31180                     }
31181                 ]
31182             },
31183             {
31184                 tag : 'div',
31185                 cls : 'btn-group roo-upload-cropbox-download',
31186                 action : 'download',
31187                 cn : [
31188                     {
31189                         tag : 'button',
31190                         cls : 'btn btn-default',
31191                         html : '<i class="fa fa-download"></i>'
31192                     }
31193                 ]
31194             },
31195             {
31196                 tag : 'div',
31197                 cls : 'btn-group roo-upload-cropbox-crop',
31198                 action : 'crop',
31199                 cn : [
31200                     {
31201                         tag : 'button',
31202                         cls : 'btn btn-default',
31203                         html : '<i class="fa fa-crop"></i>'
31204                     }
31205                 ]
31206             },
31207             {
31208                 tag : 'div',
31209                 cls : 'btn-group roo-upload-cropbox-trash',
31210                 action : 'trash',
31211                 cn : [
31212                     {
31213                         tag : 'button',
31214                         cls : 'btn btn-default',
31215                         html : '<i class="fa fa-trash"></i>'
31216                     }
31217                 ]
31218             },
31219             {
31220                 tag : 'div',
31221                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31222                 action : 'rotate-right',
31223                 cn : [
31224                     {
31225                         tag : 'button',
31226                         cls : 'btn btn-default',
31227                         html : '<i class="fa fa-repeat"></i>'
31228                     }
31229                 ]
31230             }
31231         ],
31232         ROTATOR : [
31233             {
31234                 tag : 'div',
31235                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31236                 action : 'rotate-left',
31237                 cn : [
31238                     {
31239                         tag : 'button',
31240                         cls : 'btn btn-default',
31241                         html : '<i class="fa fa-undo"></i>'
31242                     }
31243                 ]
31244             },
31245             {
31246                 tag : 'div',
31247                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31248                 action : 'rotate-right',
31249                 cn : [
31250                     {
31251                         tag : 'button',
31252                         cls : 'btn btn-default',
31253                         html : '<i class="fa fa-repeat"></i>'
31254                     }
31255                 ]
31256             }
31257         ]
31258     }
31259 });
31260
31261 /*
31262 * Licence: LGPL
31263 */
31264
31265 /**
31266  * @class Roo.bootstrap.DocumentManager
31267  * @extends Roo.bootstrap.Component
31268  * Bootstrap DocumentManager class
31269  * @cfg {String} paramName default 'imageUpload'
31270  * @cfg {String} toolTipName default 'filename'
31271  * @cfg {String} method default POST
31272  * @cfg {String} url action url
31273  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31274  * @cfg {Boolean} multiple multiple upload default true
31275  * @cfg {Number} thumbSize default 300
31276  * @cfg {String} fieldLabel
31277  * @cfg {Number} labelWidth default 4
31278  * @cfg {String} labelAlign (left|top) default left
31279  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31280 * @cfg {Number} labellg set the width of label (1-12)
31281  * @cfg {Number} labelmd set the width of label (1-12)
31282  * @cfg {Number} labelsm set the width of label (1-12)
31283  * @cfg {Number} labelxs set the width of label (1-12)
31284  * 
31285  * @constructor
31286  * Create a new DocumentManager
31287  * @param {Object} config The config object
31288  */
31289
31290 Roo.bootstrap.DocumentManager = function(config){
31291     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31292     
31293     this.files = [];
31294     this.delegates = [];
31295     
31296     this.addEvents({
31297         /**
31298          * @event initial
31299          * Fire when initial the DocumentManager
31300          * @param {Roo.bootstrap.DocumentManager} this
31301          */
31302         "initial" : true,
31303         /**
31304          * @event inspect
31305          * inspect selected file
31306          * @param {Roo.bootstrap.DocumentManager} this
31307          * @param {File} file
31308          */
31309         "inspect" : true,
31310         /**
31311          * @event exception
31312          * Fire when xhr load exception
31313          * @param {Roo.bootstrap.DocumentManager} this
31314          * @param {XMLHttpRequest} xhr
31315          */
31316         "exception" : true,
31317         /**
31318          * @event afterupload
31319          * Fire when xhr load exception
31320          * @param {Roo.bootstrap.DocumentManager} this
31321          * @param {XMLHttpRequest} xhr
31322          */
31323         "afterupload" : true,
31324         /**
31325          * @event prepare
31326          * prepare the form data
31327          * @param {Roo.bootstrap.DocumentManager} this
31328          * @param {Object} formData
31329          */
31330         "prepare" : true,
31331         /**
31332          * @event remove
31333          * Fire when remove the file
31334          * @param {Roo.bootstrap.DocumentManager} this
31335          * @param {Object} file
31336          */
31337         "remove" : true,
31338         /**
31339          * @event refresh
31340          * Fire after refresh the file
31341          * @param {Roo.bootstrap.DocumentManager} this
31342          */
31343         "refresh" : true,
31344         /**
31345          * @event click
31346          * Fire after click the image
31347          * @param {Roo.bootstrap.DocumentManager} this
31348          * @param {Object} file
31349          */
31350         "click" : true,
31351         /**
31352          * @event edit
31353          * Fire when upload a image and editable set to true
31354          * @param {Roo.bootstrap.DocumentManager} this
31355          * @param {Object} file
31356          */
31357         "edit" : true,
31358         /**
31359          * @event beforeselectfile
31360          * Fire before select file
31361          * @param {Roo.bootstrap.DocumentManager} this
31362          */
31363         "beforeselectfile" : true,
31364         /**
31365          * @event process
31366          * Fire before process file
31367          * @param {Roo.bootstrap.DocumentManager} this
31368          * @param {Object} file
31369          */
31370         "process" : true,
31371         /**
31372          * @event previewrendered
31373          * Fire when preview rendered
31374          * @param {Roo.bootstrap.DocumentManager} this
31375          * @param {Object} file
31376          */
31377         "previewrendered" : true,
31378         /**
31379          */
31380         "previewResize" : true
31381         
31382     });
31383 };
31384
31385 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31386     
31387     boxes : 0,
31388     inputName : '',
31389     thumbSize : 300,
31390     multiple : true,
31391     files : false,
31392     method : 'POST',
31393     url : '',
31394     paramName : 'imageUpload',
31395     toolTipName : 'filename',
31396     fieldLabel : '',
31397     labelWidth : 4,
31398     labelAlign : 'left',
31399     editable : true,
31400     delegates : false,
31401     xhr : false, 
31402     
31403     labellg : 0,
31404     labelmd : 0,
31405     labelsm : 0,
31406     labelxs : 0,
31407     
31408     getAutoCreate : function()
31409     {   
31410         var managerWidget = {
31411             tag : 'div',
31412             cls : 'roo-document-manager',
31413             cn : [
31414                 {
31415                     tag : 'input',
31416                     cls : 'roo-document-manager-selector',
31417                     type : 'file'
31418                 },
31419                 {
31420                     tag : 'div',
31421                     cls : 'roo-document-manager-uploader',
31422                     cn : [
31423                         {
31424                             tag : 'div',
31425                             cls : 'roo-document-manager-upload-btn',
31426                             html : '<i class="fa fa-plus"></i>'
31427                         }
31428                     ]
31429                     
31430                 }
31431             ]
31432         };
31433         
31434         var content = [
31435             {
31436                 tag : 'div',
31437                 cls : 'column col-md-12',
31438                 cn : managerWidget
31439             }
31440         ];
31441         
31442         if(this.fieldLabel.length){
31443             
31444             content = [
31445                 {
31446                     tag : 'div',
31447                     cls : 'column col-md-12',
31448                     html : this.fieldLabel
31449                 },
31450                 {
31451                     tag : 'div',
31452                     cls : 'column col-md-12',
31453                     cn : managerWidget
31454                 }
31455             ];
31456
31457             if(this.labelAlign == 'left'){
31458                 content = [
31459                     {
31460                         tag : 'div',
31461                         cls : 'column',
31462                         html : this.fieldLabel
31463                     },
31464                     {
31465                         tag : 'div',
31466                         cls : 'column',
31467                         cn : managerWidget
31468                     }
31469                 ];
31470                 
31471                 if(this.labelWidth > 12){
31472                     content[0].style = "width: " + this.labelWidth + 'px';
31473                 }
31474
31475                 if(this.labelWidth < 13 && this.labelmd == 0){
31476                     this.labelmd = this.labelWidth;
31477                 }
31478
31479                 if(this.labellg > 0){
31480                     content[0].cls += ' col-lg-' + this.labellg;
31481                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31482                 }
31483
31484                 if(this.labelmd > 0){
31485                     content[0].cls += ' col-md-' + this.labelmd;
31486                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31487                 }
31488
31489                 if(this.labelsm > 0){
31490                     content[0].cls += ' col-sm-' + this.labelsm;
31491                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31492                 }
31493
31494                 if(this.labelxs > 0){
31495                     content[0].cls += ' col-xs-' + this.labelxs;
31496                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31497                 }
31498                 
31499             }
31500         }
31501         
31502         var cfg = {
31503             tag : 'div',
31504             cls : 'row clearfix',
31505             cn : content
31506         };
31507         
31508         return cfg;
31509         
31510     },
31511     
31512     initEvents : function()
31513     {
31514         this.managerEl = this.el.select('.roo-document-manager', true).first();
31515         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31516         
31517         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31518         this.selectorEl.hide();
31519         
31520         if(this.multiple){
31521             this.selectorEl.attr('multiple', 'multiple');
31522         }
31523         
31524         this.selectorEl.on('change', this.onFileSelected, this);
31525         
31526         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31527         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31528         
31529         this.uploader.on('click', this.onUploaderClick, this);
31530         
31531         this.renderProgressDialog();
31532         
31533         var _this = this;
31534         
31535         window.addEventListener("resize", function() { _this.refresh(); } );
31536         
31537         this.fireEvent('initial', this);
31538     },
31539     
31540     renderProgressDialog : function()
31541     {
31542         var _this = this;
31543         
31544         this.progressDialog = new Roo.bootstrap.Modal({
31545             cls : 'roo-document-manager-progress-dialog',
31546             allow_close : false,
31547             animate : false,
31548             title : '',
31549             buttons : [
31550                 {
31551                     name  :'cancel',
31552                     weight : 'danger',
31553                     html : 'Cancel'
31554                 }
31555             ], 
31556             listeners : { 
31557                 btnclick : function() {
31558                     _this.uploadCancel();
31559                     this.hide();
31560                 }
31561             }
31562         });
31563          
31564         this.progressDialog.render(Roo.get(document.body));
31565          
31566         this.progress = new Roo.bootstrap.Progress({
31567             cls : 'roo-document-manager-progress',
31568             active : true,
31569             striped : true
31570         });
31571         
31572         this.progress.render(this.progressDialog.getChildContainer());
31573         
31574         this.progressBar = new Roo.bootstrap.ProgressBar({
31575             cls : 'roo-document-manager-progress-bar',
31576             aria_valuenow : 0,
31577             aria_valuemin : 0,
31578             aria_valuemax : 12,
31579             panel : 'success'
31580         });
31581         
31582         this.progressBar.render(this.progress.getChildContainer());
31583     },
31584     
31585     onUploaderClick : function(e)
31586     {
31587         e.preventDefault();
31588      
31589         if(this.fireEvent('beforeselectfile', this) != false){
31590             this.selectorEl.dom.click();
31591         }
31592         
31593     },
31594     
31595     onFileSelected : function(e)
31596     {
31597         e.preventDefault();
31598         
31599         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31600             return;
31601         }
31602         
31603         Roo.each(this.selectorEl.dom.files, function(file){
31604             if(this.fireEvent('inspect', this, file) != false){
31605                 this.files.push(file);
31606             }
31607         }, this);
31608         
31609         this.queue();
31610         
31611     },
31612     
31613     queue : function()
31614     {
31615         this.selectorEl.dom.value = '';
31616         
31617         if(!this.files || !this.files.length){
31618             return;
31619         }
31620         
31621         if(this.boxes > 0 && this.files.length > this.boxes){
31622             this.files = this.files.slice(0, this.boxes);
31623         }
31624         
31625         this.uploader.show();
31626         
31627         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31628             this.uploader.hide();
31629         }
31630         
31631         var _this = this;
31632         
31633         var files = [];
31634         
31635         var docs = [];
31636         
31637         Roo.each(this.files, function(file){
31638             
31639             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31640                 var f = this.renderPreview(file);
31641                 files.push(f);
31642                 return;
31643             }
31644             
31645             if(file.type.indexOf('image') != -1){
31646                 this.delegates.push(
31647                     (function(){
31648                         _this.process(file);
31649                     }).createDelegate(this)
31650                 );
31651         
31652                 return;
31653             }
31654             
31655             docs.push(
31656                 (function(){
31657                     _this.process(file);
31658                 }).createDelegate(this)
31659             );
31660             
31661         }, this);
31662         
31663         this.files = files;
31664         
31665         this.delegates = this.delegates.concat(docs);
31666         
31667         if(!this.delegates.length){
31668             this.refresh();
31669             return;
31670         }
31671         
31672         this.progressBar.aria_valuemax = this.delegates.length;
31673         
31674         this.arrange();
31675         
31676         return;
31677     },
31678     
31679     arrange : function()
31680     {
31681         if(!this.delegates.length){
31682             this.progressDialog.hide();
31683             this.refresh();
31684             return;
31685         }
31686         
31687         var delegate = this.delegates.shift();
31688         
31689         this.progressDialog.show();
31690         
31691         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31692         
31693         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31694         
31695         delegate();
31696     },
31697     
31698     refresh : function()
31699     {
31700         this.uploader.show();
31701         
31702         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31703             this.uploader.hide();
31704         }
31705         
31706         Roo.isTouch ? this.closable(false) : this.closable(true);
31707         
31708         this.fireEvent('refresh', this);
31709     },
31710     
31711     onRemove : function(e, el, o)
31712     {
31713         e.preventDefault();
31714         
31715         this.fireEvent('remove', this, o);
31716         
31717     },
31718     
31719     remove : function(o)
31720     {
31721         var files = [];
31722         
31723         Roo.each(this.files, function(file){
31724             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31725                 files.push(file);
31726                 return;
31727             }
31728
31729             o.target.remove();
31730
31731         }, this);
31732         
31733         this.files = files;
31734         
31735         this.refresh();
31736     },
31737     
31738     clear : function()
31739     {
31740         Roo.each(this.files, function(file){
31741             if(!file.target){
31742                 return;
31743             }
31744             
31745             file.target.remove();
31746
31747         }, this);
31748         
31749         this.files = [];
31750         
31751         this.refresh();
31752     },
31753     
31754     onClick : function(e, el, o)
31755     {
31756         e.preventDefault();
31757         
31758         this.fireEvent('click', this, o);
31759         
31760     },
31761     
31762     closable : function(closable)
31763     {
31764         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31765             
31766             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31767             
31768             if(closable){
31769                 el.show();
31770                 return;
31771             }
31772             
31773             el.hide();
31774             
31775         }, this);
31776     },
31777     
31778     xhrOnLoad : function(xhr)
31779     {
31780         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31781             el.remove();
31782         }, this);
31783         
31784         if (xhr.readyState !== 4) {
31785             this.arrange();
31786             this.fireEvent('exception', this, xhr);
31787             return;
31788         }
31789
31790         var response = Roo.decode(xhr.responseText);
31791         
31792         if(!response.success){
31793             this.arrange();
31794             this.fireEvent('exception', this, xhr);
31795             return;
31796         }
31797         
31798         var file = this.renderPreview(response.data);
31799         
31800         this.files.push(file);
31801         
31802         this.arrange();
31803         
31804         this.fireEvent('afterupload', this, xhr);
31805         
31806     },
31807     
31808     xhrOnError : function(xhr)
31809     {
31810         Roo.log('xhr on error');
31811         
31812         var response = Roo.decode(xhr.responseText);
31813           
31814         Roo.log(response);
31815         
31816         this.arrange();
31817     },
31818     
31819     process : function(file)
31820     {
31821         if(this.fireEvent('process', this, file) !== false){
31822             if(this.editable && file.type.indexOf('image') != -1){
31823                 this.fireEvent('edit', this, file);
31824                 return;
31825             }
31826
31827             this.uploadStart(file, false);
31828
31829             return;
31830         }
31831         
31832     },
31833     
31834     uploadStart : function(file, crop)
31835     {
31836         this.xhr = new XMLHttpRequest();
31837         
31838         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31839             this.arrange();
31840             return;
31841         }
31842         
31843         file.xhr = this.xhr;
31844             
31845         this.managerEl.createChild({
31846             tag : 'div',
31847             cls : 'roo-document-manager-loading',
31848             cn : [
31849                 {
31850                     tag : 'div',
31851                     tooltip : file.name,
31852                     cls : 'roo-document-manager-thumb',
31853                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31854                 }
31855             ]
31856
31857         });
31858
31859         this.xhr.open(this.method, this.url, true);
31860         
31861         var headers = {
31862             "Accept": "application/json",
31863             "Cache-Control": "no-cache",
31864             "X-Requested-With": "XMLHttpRequest"
31865         };
31866         
31867         for (var headerName in headers) {
31868             var headerValue = headers[headerName];
31869             if (headerValue) {
31870                 this.xhr.setRequestHeader(headerName, headerValue);
31871             }
31872         }
31873         
31874         var _this = this;
31875         
31876         this.xhr.onload = function()
31877         {
31878             _this.xhrOnLoad(_this.xhr);
31879         }
31880         
31881         this.xhr.onerror = function()
31882         {
31883             _this.xhrOnError(_this.xhr);
31884         }
31885         
31886         var formData = new FormData();
31887
31888         formData.append('returnHTML', 'NO');
31889         
31890         if(crop){
31891             formData.append('crop', crop);
31892         }
31893         
31894         formData.append(this.paramName, file, file.name);
31895         
31896         var options = {
31897             file : file, 
31898             manually : false
31899         };
31900         
31901         if(this.fireEvent('prepare', this, formData, options) != false){
31902             
31903             if(options.manually){
31904                 return;
31905             }
31906             
31907             this.xhr.send(formData);
31908             return;
31909         };
31910         
31911         this.uploadCancel();
31912     },
31913     
31914     uploadCancel : function()
31915     {
31916         if (this.xhr) {
31917             this.xhr.abort();
31918         }
31919         
31920         this.delegates = [];
31921         
31922         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31923             el.remove();
31924         }, this);
31925         
31926         this.arrange();
31927     },
31928     
31929     renderPreview : function(file)
31930     {
31931         if(typeof(file.target) != 'undefined' && file.target){
31932             return file;
31933         }
31934         
31935         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31936         
31937         var previewEl = this.managerEl.createChild({
31938             tag : 'div',
31939             cls : 'roo-document-manager-preview',
31940             cn : [
31941                 {
31942                     tag : 'div',
31943                     tooltip : file[this.toolTipName],
31944                     cls : 'roo-document-manager-thumb',
31945                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31946                 },
31947                 {
31948                     tag : 'button',
31949                     cls : 'close',
31950                     html : '<i class="fa fa-times-circle"></i>'
31951                 }
31952             ]
31953         });
31954
31955         var close = previewEl.select('button.close', true).first();
31956
31957         close.on('click', this.onRemove, this, file);
31958
31959         file.target = previewEl;
31960
31961         var image = previewEl.select('img', true).first();
31962         
31963         var _this = this;
31964         
31965         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31966         
31967         image.on('click', this.onClick, this, file);
31968         
31969         this.fireEvent('previewrendered', this, file);
31970         
31971         return file;
31972         
31973     },
31974     
31975     onPreviewLoad : function(file, image)
31976     {
31977         if(typeof(file.target) == 'undefined' || !file.target){
31978             return;
31979         }
31980         
31981         var width = image.dom.naturalWidth || image.dom.width;
31982         var height = image.dom.naturalHeight || image.dom.height;
31983         
31984         if(!this.previewResize) {
31985             return;
31986         }
31987         
31988         if(width > height){
31989             file.target.addClass('wide');
31990             return;
31991         }
31992         
31993         file.target.addClass('tall');
31994         return;
31995         
31996     },
31997     
31998     uploadFromSource : function(file, crop)
31999     {
32000         this.xhr = new XMLHttpRequest();
32001         
32002         this.managerEl.createChild({
32003             tag : 'div',
32004             cls : 'roo-document-manager-loading',
32005             cn : [
32006                 {
32007                     tag : 'div',
32008                     tooltip : file.name,
32009                     cls : 'roo-document-manager-thumb',
32010                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32011                 }
32012             ]
32013
32014         });
32015
32016         this.xhr.open(this.method, this.url, true);
32017         
32018         var headers = {
32019             "Accept": "application/json",
32020             "Cache-Control": "no-cache",
32021             "X-Requested-With": "XMLHttpRequest"
32022         };
32023         
32024         for (var headerName in headers) {
32025             var headerValue = headers[headerName];
32026             if (headerValue) {
32027                 this.xhr.setRequestHeader(headerName, headerValue);
32028             }
32029         }
32030         
32031         var _this = this;
32032         
32033         this.xhr.onload = function()
32034         {
32035             _this.xhrOnLoad(_this.xhr);
32036         }
32037         
32038         this.xhr.onerror = function()
32039         {
32040             _this.xhrOnError(_this.xhr);
32041         }
32042         
32043         var formData = new FormData();
32044
32045         formData.append('returnHTML', 'NO');
32046         
32047         formData.append('crop', crop);
32048         
32049         if(typeof(file.filename) != 'undefined'){
32050             formData.append('filename', file.filename);
32051         }
32052         
32053         if(typeof(file.mimetype) != 'undefined'){
32054             formData.append('mimetype', file.mimetype);
32055         }
32056         
32057         Roo.log(formData);
32058         
32059         if(this.fireEvent('prepare', this, formData) != false){
32060             this.xhr.send(formData);
32061         };
32062     }
32063 });
32064
32065 /*
32066 * Licence: LGPL
32067 */
32068
32069 /**
32070  * @class Roo.bootstrap.DocumentViewer
32071  * @extends Roo.bootstrap.Component
32072  * Bootstrap DocumentViewer class
32073  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32074  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32075  * 
32076  * @constructor
32077  * Create a new DocumentViewer
32078  * @param {Object} config The config object
32079  */
32080
32081 Roo.bootstrap.DocumentViewer = function(config){
32082     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32083     
32084     this.addEvents({
32085         /**
32086          * @event initial
32087          * Fire after initEvent
32088          * @param {Roo.bootstrap.DocumentViewer} this
32089          */
32090         "initial" : true,
32091         /**
32092          * @event click
32093          * Fire after click
32094          * @param {Roo.bootstrap.DocumentViewer} this
32095          */
32096         "click" : true,
32097         /**
32098          * @event download
32099          * Fire after download button
32100          * @param {Roo.bootstrap.DocumentViewer} this
32101          */
32102         "download" : true,
32103         /**
32104          * @event trash
32105          * Fire after trash button
32106          * @param {Roo.bootstrap.DocumentViewer} this
32107          */
32108         "trash" : true
32109         
32110     });
32111 };
32112
32113 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32114     
32115     showDownload : true,
32116     
32117     showTrash : true,
32118     
32119     getAutoCreate : function()
32120     {
32121         var cfg = {
32122             tag : 'div',
32123             cls : 'roo-document-viewer',
32124             cn : [
32125                 {
32126                     tag : 'div',
32127                     cls : 'roo-document-viewer-body',
32128                     cn : [
32129                         {
32130                             tag : 'div',
32131                             cls : 'roo-document-viewer-thumb',
32132                             cn : [
32133                                 {
32134                                     tag : 'img',
32135                                     cls : 'roo-document-viewer-image'
32136                                 }
32137                             ]
32138                         }
32139                     ]
32140                 },
32141                 {
32142                     tag : 'div',
32143                     cls : 'roo-document-viewer-footer',
32144                     cn : {
32145                         tag : 'div',
32146                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32147                         cn : [
32148                             {
32149                                 tag : 'div',
32150                                 cls : 'btn-group roo-document-viewer-download',
32151                                 cn : [
32152                                     {
32153                                         tag : 'button',
32154                                         cls : 'btn btn-default',
32155                                         html : '<i class="fa fa-download"></i>'
32156                                     }
32157                                 ]
32158                             },
32159                             {
32160                                 tag : 'div',
32161                                 cls : 'btn-group roo-document-viewer-trash',
32162                                 cn : [
32163                                     {
32164                                         tag : 'button',
32165                                         cls : 'btn btn-default',
32166                                         html : '<i class="fa fa-trash"></i>'
32167                                     }
32168                                 ]
32169                             }
32170                         ]
32171                     }
32172                 }
32173             ]
32174         };
32175         
32176         return cfg;
32177     },
32178     
32179     initEvents : function()
32180     {
32181         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32182         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32183         
32184         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32185         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32186         
32187         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32188         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32189         
32190         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32191         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32192         
32193         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32194         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32195         
32196         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32197         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32198         
32199         this.bodyEl.on('click', this.onClick, this);
32200         this.downloadBtn.on('click', this.onDownload, this);
32201         this.trashBtn.on('click', this.onTrash, this);
32202         
32203         this.downloadBtn.hide();
32204         this.trashBtn.hide();
32205         
32206         if(this.showDownload){
32207             this.downloadBtn.show();
32208         }
32209         
32210         if(this.showTrash){
32211             this.trashBtn.show();
32212         }
32213         
32214         if(!this.showDownload && !this.showTrash) {
32215             this.footerEl.hide();
32216         }
32217         
32218     },
32219     
32220     initial : function()
32221     {
32222         this.fireEvent('initial', this);
32223         
32224     },
32225     
32226     onClick : function(e)
32227     {
32228         e.preventDefault();
32229         
32230         this.fireEvent('click', this);
32231     },
32232     
32233     onDownload : function(e)
32234     {
32235         e.preventDefault();
32236         
32237         this.fireEvent('download', this);
32238     },
32239     
32240     onTrash : function(e)
32241     {
32242         e.preventDefault();
32243         
32244         this.fireEvent('trash', this);
32245     }
32246     
32247 });
32248 /*
32249  * - LGPL
32250  *
32251  * nav progress bar
32252  * 
32253  */
32254
32255 /**
32256  * @class Roo.bootstrap.NavProgressBar
32257  * @extends Roo.bootstrap.Component
32258  * Bootstrap NavProgressBar class
32259  * 
32260  * @constructor
32261  * Create a new nav progress bar
32262  * @param {Object} config The config object
32263  */
32264
32265 Roo.bootstrap.NavProgressBar = function(config){
32266     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32267
32268     this.bullets = this.bullets || [];
32269    
32270 //    Roo.bootstrap.NavProgressBar.register(this);
32271      this.addEvents({
32272         /**
32273              * @event changed
32274              * Fires when the active item changes
32275              * @param {Roo.bootstrap.NavProgressBar} this
32276              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32277              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32278          */
32279         'changed': true
32280      });
32281     
32282 };
32283
32284 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32285     
32286     bullets : [],
32287     barItems : [],
32288     
32289     getAutoCreate : function()
32290     {
32291         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32292         
32293         cfg = {
32294             tag : 'div',
32295             cls : 'roo-navigation-bar-group',
32296             cn : [
32297                 {
32298                     tag : 'div',
32299                     cls : 'roo-navigation-top-bar'
32300                 },
32301                 {
32302                     tag : 'div',
32303                     cls : 'roo-navigation-bullets-bar',
32304                     cn : [
32305                         {
32306                             tag : 'ul',
32307                             cls : 'roo-navigation-bar'
32308                         }
32309                     ]
32310                 },
32311                 
32312                 {
32313                     tag : 'div',
32314                     cls : 'roo-navigation-bottom-bar'
32315                 }
32316             ]
32317             
32318         };
32319         
32320         return cfg;
32321         
32322     },
32323     
32324     initEvents: function() 
32325     {
32326         
32327     },
32328     
32329     onRender : function(ct, position) 
32330     {
32331         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32332         
32333         if(this.bullets.length){
32334             Roo.each(this.bullets, function(b){
32335                this.addItem(b);
32336             }, this);
32337         }
32338         
32339         this.format();
32340         
32341     },
32342     
32343     addItem : function(cfg)
32344     {
32345         var item = new Roo.bootstrap.NavProgressItem(cfg);
32346         
32347         item.parentId = this.id;
32348         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32349         
32350         if(cfg.html){
32351             var top = new Roo.bootstrap.Element({
32352                 tag : 'div',
32353                 cls : 'roo-navigation-bar-text'
32354             });
32355             
32356             var bottom = new Roo.bootstrap.Element({
32357                 tag : 'div',
32358                 cls : 'roo-navigation-bar-text'
32359             });
32360             
32361             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32362             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32363             
32364             var topText = new Roo.bootstrap.Element({
32365                 tag : 'span',
32366                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32367             });
32368             
32369             var bottomText = new Roo.bootstrap.Element({
32370                 tag : 'span',
32371                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32372             });
32373             
32374             topText.onRender(top.el, null);
32375             bottomText.onRender(bottom.el, null);
32376             
32377             item.topEl = top;
32378             item.bottomEl = bottom;
32379         }
32380         
32381         this.barItems.push(item);
32382         
32383         return item;
32384     },
32385     
32386     getActive : function()
32387     {
32388         var active = false;
32389         
32390         Roo.each(this.barItems, function(v){
32391             
32392             if (!v.isActive()) {
32393                 return;
32394             }
32395             
32396             active = v;
32397             return false;
32398             
32399         });
32400         
32401         return active;
32402     },
32403     
32404     setActiveItem : function(item)
32405     {
32406         var prev = false;
32407         
32408         Roo.each(this.barItems, function(v){
32409             if (v.rid == item.rid) {
32410                 return ;
32411             }
32412             
32413             if (v.isActive()) {
32414                 v.setActive(false);
32415                 prev = v;
32416             }
32417         });
32418
32419         item.setActive(true);
32420         
32421         this.fireEvent('changed', this, item, prev);
32422     },
32423     
32424     getBarItem: function(rid)
32425     {
32426         var ret = false;
32427         
32428         Roo.each(this.barItems, function(e) {
32429             if (e.rid != rid) {
32430                 return;
32431             }
32432             
32433             ret =  e;
32434             return false;
32435         });
32436         
32437         return ret;
32438     },
32439     
32440     indexOfItem : function(item)
32441     {
32442         var index = false;
32443         
32444         Roo.each(this.barItems, function(v, i){
32445             
32446             if (v.rid != item.rid) {
32447                 return;
32448             }
32449             
32450             index = i;
32451             return false
32452         });
32453         
32454         return index;
32455     },
32456     
32457     setActiveNext : function()
32458     {
32459         var i = this.indexOfItem(this.getActive());
32460         
32461         if (i > this.barItems.length) {
32462             return;
32463         }
32464         
32465         this.setActiveItem(this.barItems[i+1]);
32466     },
32467     
32468     setActivePrev : function()
32469     {
32470         var i = this.indexOfItem(this.getActive());
32471         
32472         if (i  < 1) {
32473             return;
32474         }
32475         
32476         this.setActiveItem(this.barItems[i-1]);
32477     },
32478     
32479     format : function()
32480     {
32481         if(!this.barItems.length){
32482             return;
32483         }
32484      
32485         var width = 100 / this.barItems.length;
32486         
32487         Roo.each(this.barItems, function(i){
32488             i.el.setStyle('width', width + '%');
32489             i.topEl.el.setStyle('width', width + '%');
32490             i.bottomEl.el.setStyle('width', width + '%');
32491         }, this);
32492         
32493     }
32494     
32495 });
32496 /*
32497  * - LGPL
32498  *
32499  * Nav Progress Item
32500  * 
32501  */
32502
32503 /**
32504  * @class Roo.bootstrap.NavProgressItem
32505  * @extends Roo.bootstrap.Component
32506  * Bootstrap NavProgressItem class
32507  * @cfg {String} rid the reference id
32508  * @cfg {Boolean} active (true|false) Is item active default false
32509  * @cfg {Boolean} disabled (true|false) Is item active default false
32510  * @cfg {String} html
32511  * @cfg {String} position (top|bottom) text position default bottom
32512  * @cfg {String} icon show icon instead of number
32513  * 
32514  * @constructor
32515  * Create a new NavProgressItem
32516  * @param {Object} config The config object
32517  */
32518 Roo.bootstrap.NavProgressItem = function(config){
32519     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32520     this.addEvents({
32521         // raw events
32522         /**
32523          * @event click
32524          * The raw click event for the entire grid.
32525          * @param {Roo.bootstrap.NavProgressItem} this
32526          * @param {Roo.EventObject} e
32527          */
32528         "click" : true
32529     });
32530    
32531 };
32532
32533 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32534     
32535     rid : '',
32536     active : false,
32537     disabled : false,
32538     html : '',
32539     position : 'bottom',
32540     icon : false,
32541     
32542     getAutoCreate : function()
32543     {
32544         var iconCls = 'roo-navigation-bar-item-icon';
32545         
32546         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32547         
32548         var cfg = {
32549             tag: 'li',
32550             cls: 'roo-navigation-bar-item',
32551             cn : [
32552                 {
32553                     tag : 'i',
32554                     cls : iconCls
32555                 }
32556             ]
32557         };
32558         
32559         if(this.active){
32560             cfg.cls += ' active';
32561         }
32562         if(this.disabled){
32563             cfg.cls += ' disabled';
32564         }
32565         
32566         return cfg;
32567     },
32568     
32569     disable : function()
32570     {
32571         this.setDisabled(true);
32572     },
32573     
32574     enable : function()
32575     {
32576         this.setDisabled(false);
32577     },
32578     
32579     initEvents: function() 
32580     {
32581         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32582         
32583         this.iconEl.on('click', this.onClick, this);
32584     },
32585     
32586     onClick : function(e)
32587     {
32588         e.preventDefault();
32589         
32590         if(this.disabled){
32591             return;
32592         }
32593         
32594         if(this.fireEvent('click', this, e) === false){
32595             return;
32596         };
32597         
32598         this.parent().setActiveItem(this);
32599     },
32600     
32601     isActive: function () 
32602     {
32603         return this.active;
32604     },
32605     
32606     setActive : function(state)
32607     {
32608         if(this.active == state){
32609             return;
32610         }
32611         
32612         this.active = state;
32613         
32614         if (state) {
32615             this.el.addClass('active');
32616             return;
32617         }
32618         
32619         this.el.removeClass('active');
32620         
32621         return;
32622     },
32623     
32624     setDisabled : function(state)
32625     {
32626         if(this.disabled == state){
32627             return;
32628         }
32629         
32630         this.disabled = state;
32631         
32632         if (state) {
32633             this.el.addClass('disabled');
32634             return;
32635         }
32636         
32637         this.el.removeClass('disabled');
32638     },
32639     
32640     tooltipEl : function()
32641     {
32642         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32643     }
32644 });
32645  
32646
32647  /*
32648  * - LGPL
32649  *
32650  * FieldLabel
32651  * 
32652  */
32653
32654 /**
32655  * @class Roo.bootstrap.FieldLabel
32656  * @extends Roo.bootstrap.Component
32657  * Bootstrap FieldLabel class
32658  * @cfg {String} html contents of the element
32659  * @cfg {String} tag tag of the element default label
32660  * @cfg {String} cls class of the element
32661  * @cfg {String} target label target 
32662  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32663  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32664  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32665  * @cfg {String} iconTooltip default "This field is required"
32666  * @cfg {String} indicatorpos (left|right) default left
32667  * 
32668  * @constructor
32669  * Create a new FieldLabel
32670  * @param {Object} config The config object
32671  */
32672
32673 Roo.bootstrap.FieldLabel = function(config){
32674     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32675     
32676     this.addEvents({
32677             /**
32678              * @event invalid
32679              * Fires after the field has been marked as invalid.
32680              * @param {Roo.form.FieldLabel} this
32681              * @param {String} msg The validation message
32682              */
32683             invalid : true,
32684             /**
32685              * @event valid
32686              * Fires after the field has been validated with no errors.
32687              * @param {Roo.form.FieldLabel} this
32688              */
32689             valid : true
32690         });
32691 };
32692
32693 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32694     
32695     tag: 'label',
32696     cls: '',
32697     html: '',
32698     target: '',
32699     allowBlank : true,
32700     invalidClass : 'has-warning',
32701     validClass : 'has-success',
32702     iconTooltip : 'This field is required',
32703     indicatorpos : 'left',
32704     
32705     getAutoCreate : function(){
32706         
32707         var cls = "";
32708         if (!this.allowBlank) {
32709             cls  = "visible";
32710         }
32711         
32712         var cfg = {
32713             tag : this.tag,
32714             cls : 'roo-bootstrap-field-label ' + this.cls,
32715             for : this.target,
32716             cn : [
32717                 {
32718                     tag : 'i',
32719                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32720                     tooltip : this.iconTooltip
32721                 },
32722                 {
32723                     tag : 'span',
32724                     html : this.html
32725                 }
32726             ] 
32727         };
32728         
32729         if(this.indicatorpos == 'right'){
32730             var cfg = {
32731                 tag : this.tag,
32732                 cls : 'roo-bootstrap-field-label ' + this.cls,
32733                 for : this.target,
32734                 cn : [
32735                     {
32736                         tag : 'span',
32737                         html : this.html
32738                     },
32739                     {
32740                         tag : 'i',
32741                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32742                         tooltip : this.iconTooltip
32743                     }
32744                 ] 
32745             };
32746         }
32747         
32748         return cfg;
32749     },
32750     
32751     initEvents: function() 
32752     {
32753         Roo.bootstrap.Element.superclass.initEvents.call(this);
32754         
32755         this.indicator = this.indicatorEl();
32756         
32757         if(this.indicator){
32758             this.indicator.removeClass('visible');
32759             this.indicator.addClass('invisible');
32760         }
32761         
32762         Roo.bootstrap.FieldLabel.register(this);
32763     },
32764     
32765     indicatorEl : function()
32766     {
32767         var indicator = this.el.select('i.roo-required-indicator',true).first();
32768         
32769         if(!indicator){
32770             return false;
32771         }
32772         
32773         return indicator;
32774         
32775     },
32776     
32777     /**
32778      * Mark this field as valid
32779      */
32780     markValid : function()
32781     {
32782         if(this.indicator){
32783             this.indicator.removeClass('visible');
32784             this.indicator.addClass('invisible');
32785         }
32786         if (Roo.bootstrap.version == 3) {
32787             this.el.removeClass(this.invalidClass);
32788             this.el.addClass(this.validClass);
32789         } else {
32790             this.el.removeClass('is-invalid');
32791             this.el.addClass('is-valid');
32792         }
32793         
32794         
32795         this.fireEvent('valid', this);
32796     },
32797     
32798     /**
32799      * Mark this field as invalid
32800      * @param {String} msg The validation message
32801      */
32802     markInvalid : function(msg)
32803     {
32804         if(this.indicator){
32805             this.indicator.removeClass('invisible');
32806             this.indicator.addClass('visible');
32807         }
32808           if (Roo.bootstrap.version == 3) {
32809             this.el.removeClass(this.validClass);
32810             this.el.addClass(this.invalidClass);
32811         } else {
32812             this.el.removeClass('is-valid');
32813             this.el.addClass('is-invalid');
32814         }
32815         
32816         
32817         this.fireEvent('invalid', this, msg);
32818     }
32819     
32820    
32821 });
32822
32823 Roo.apply(Roo.bootstrap.FieldLabel, {
32824     
32825     groups: {},
32826     
32827      /**
32828     * register a FieldLabel Group
32829     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32830     */
32831     register : function(label)
32832     {
32833         if(this.groups.hasOwnProperty(label.target)){
32834             return;
32835         }
32836      
32837         this.groups[label.target] = label;
32838         
32839     },
32840     /**
32841     * fetch a FieldLabel Group based on the target
32842     * @param {string} target
32843     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32844     */
32845     get: function(target) {
32846         if (typeof(this.groups[target]) == 'undefined') {
32847             return false;
32848         }
32849         
32850         return this.groups[target] ;
32851     }
32852 });
32853
32854  
32855
32856  /*
32857  * - LGPL
32858  *
32859  * page DateSplitField.
32860  * 
32861  */
32862
32863
32864 /**
32865  * @class Roo.bootstrap.DateSplitField
32866  * @extends Roo.bootstrap.Component
32867  * Bootstrap DateSplitField class
32868  * @cfg {string} fieldLabel - the label associated
32869  * @cfg {Number} labelWidth set the width of label (0-12)
32870  * @cfg {String} labelAlign (top|left)
32871  * @cfg {Boolean} dayAllowBlank (true|false) default false
32872  * @cfg {Boolean} monthAllowBlank (true|false) default false
32873  * @cfg {Boolean} yearAllowBlank (true|false) default false
32874  * @cfg {string} dayPlaceholder 
32875  * @cfg {string} monthPlaceholder
32876  * @cfg {string} yearPlaceholder
32877  * @cfg {string} dayFormat default 'd'
32878  * @cfg {string} monthFormat default 'm'
32879  * @cfg {string} yearFormat default 'Y'
32880  * @cfg {Number} labellg set the width of label (1-12)
32881  * @cfg {Number} labelmd set the width of label (1-12)
32882  * @cfg {Number} labelsm set the width of label (1-12)
32883  * @cfg {Number} labelxs set the width of label (1-12)
32884
32885  *     
32886  * @constructor
32887  * Create a new DateSplitField
32888  * @param {Object} config The config object
32889  */
32890
32891 Roo.bootstrap.DateSplitField = function(config){
32892     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32893     
32894     this.addEvents({
32895         // raw events
32896          /**
32897          * @event years
32898          * getting the data of years
32899          * @param {Roo.bootstrap.DateSplitField} this
32900          * @param {Object} years
32901          */
32902         "years" : true,
32903         /**
32904          * @event days
32905          * getting the data of days
32906          * @param {Roo.bootstrap.DateSplitField} this
32907          * @param {Object} days
32908          */
32909         "days" : true,
32910         /**
32911          * @event invalid
32912          * Fires after the field has been marked as invalid.
32913          * @param {Roo.form.Field} this
32914          * @param {String} msg The validation message
32915          */
32916         invalid : true,
32917        /**
32918          * @event valid
32919          * Fires after the field has been validated with no errors.
32920          * @param {Roo.form.Field} this
32921          */
32922         valid : true
32923     });
32924 };
32925
32926 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32927     
32928     fieldLabel : '',
32929     labelAlign : 'top',
32930     labelWidth : 3,
32931     dayAllowBlank : false,
32932     monthAllowBlank : false,
32933     yearAllowBlank : false,
32934     dayPlaceholder : '',
32935     monthPlaceholder : '',
32936     yearPlaceholder : '',
32937     dayFormat : 'd',
32938     monthFormat : 'm',
32939     yearFormat : 'Y',
32940     isFormField : true,
32941     labellg : 0,
32942     labelmd : 0,
32943     labelsm : 0,
32944     labelxs : 0,
32945     
32946     getAutoCreate : function()
32947     {
32948         var cfg = {
32949             tag : 'div',
32950             cls : 'row roo-date-split-field-group',
32951             cn : [
32952                 {
32953                     tag : 'input',
32954                     type : 'hidden',
32955                     cls : 'form-hidden-field roo-date-split-field-group-value',
32956                     name : this.name
32957                 }
32958             ]
32959         };
32960         
32961         var labelCls = 'col-md-12';
32962         var contentCls = 'col-md-4';
32963         
32964         if(this.fieldLabel){
32965             
32966             var label = {
32967                 tag : 'div',
32968                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32969                 cn : [
32970                     {
32971                         tag : 'label',
32972                         html : this.fieldLabel
32973                     }
32974                 ]
32975             };
32976             
32977             if(this.labelAlign == 'left'){
32978             
32979                 if(this.labelWidth > 12){
32980                     label.style = "width: " + this.labelWidth + 'px';
32981                 }
32982
32983                 if(this.labelWidth < 13 && this.labelmd == 0){
32984                     this.labelmd = this.labelWidth;
32985                 }
32986
32987                 if(this.labellg > 0){
32988                     labelCls = ' col-lg-' + this.labellg;
32989                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32990                 }
32991
32992                 if(this.labelmd > 0){
32993                     labelCls = ' col-md-' + this.labelmd;
32994                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32995                 }
32996
32997                 if(this.labelsm > 0){
32998                     labelCls = ' col-sm-' + this.labelsm;
32999                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33000                 }
33001
33002                 if(this.labelxs > 0){
33003                     labelCls = ' col-xs-' + this.labelxs;
33004                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33005                 }
33006             }
33007             
33008             label.cls += ' ' + labelCls;
33009             
33010             cfg.cn.push(label);
33011         }
33012         
33013         Roo.each(['day', 'month', 'year'], function(t){
33014             cfg.cn.push({
33015                 tag : 'div',
33016                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33017             });
33018         }, this);
33019         
33020         return cfg;
33021     },
33022     
33023     inputEl: function ()
33024     {
33025         return this.el.select('.roo-date-split-field-group-value', true).first();
33026     },
33027     
33028     onRender : function(ct, position) 
33029     {
33030         var _this = this;
33031         
33032         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33033         
33034         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33035         
33036         this.dayField = new Roo.bootstrap.ComboBox({
33037             allowBlank : this.dayAllowBlank,
33038             alwaysQuery : true,
33039             displayField : 'value',
33040             editable : false,
33041             fieldLabel : '',
33042             forceSelection : true,
33043             mode : 'local',
33044             placeholder : this.dayPlaceholder,
33045             selectOnFocus : true,
33046             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33047             triggerAction : 'all',
33048             typeAhead : true,
33049             valueField : 'value',
33050             store : new Roo.data.SimpleStore({
33051                 data : (function() {    
33052                     var days = [];
33053                     _this.fireEvent('days', _this, days);
33054                     return days;
33055                 })(),
33056                 fields : [ 'value' ]
33057             }),
33058             listeners : {
33059                 select : function (_self, record, index)
33060                 {
33061                     _this.setValue(_this.getValue());
33062                 }
33063             }
33064         });
33065
33066         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33067         
33068         this.monthField = new Roo.bootstrap.MonthField({
33069             after : '<i class=\"fa fa-calendar\"></i>',
33070             allowBlank : this.monthAllowBlank,
33071             placeholder : this.monthPlaceholder,
33072             readOnly : true,
33073             listeners : {
33074                 render : function (_self)
33075                 {
33076                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33077                         e.preventDefault();
33078                         _self.focus();
33079                     });
33080                 },
33081                 select : function (_self, oldvalue, newvalue)
33082                 {
33083                     _this.setValue(_this.getValue());
33084                 }
33085             }
33086         });
33087         
33088         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33089         
33090         this.yearField = new Roo.bootstrap.ComboBox({
33091             allowBlank : this.yearAllowBlank,
33092             alwaysQuery : true,
33093             displayField : 'value',
33094             editable : false,
33095             fieldLabel : '',
33096             forceSelection : true,
33097             mode : 'local',
33098             placeholder : this.yearPlaceholder,
33099             selectOnFocus : true,
33100             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33101             triggerAction : 'all',
33102             typeAhead : true,
33103             valueField : 'value',
33104             store : new Roo.data.SimpleStore({
33105                 data : (function() {
33106                     var years = [];
33107                     _this.fireEvent('years', _this, years);
33108                     return years;
33109                 })(),
33110                 fields : [ 'value' ]
33111             }),
33112             listeners : {
33113                 select : function (_self, record, index)
33114                 {
33115                     _this.setValue(_this.getValue());
33116                 }
33117             }
33118         });
33119
33120         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33121     },
33122     
33123     setValue : function(v, format)
33124     {
33125         this.inputEl.dom.value = v;
33126         
33127         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33128         
33129         var d = Date.parseDate(v, f);
33130         
33131         if(!d){
33132             this.validate();
33133             return;
33134         }
33135         
33136         this.setDay(d.format(this.dayFormat));
33137         this.setMonth(d.format(this.monthFormat));
33138         this.setYear(d.format(this.yearFormat));
33139         
33140         this.validate();
33141         
33142         return;
33143     },
33144     
33145     setDay : function(v)
33146     {
33147         this.dayField.setValue(v);
33148         this.inputEl.dom.value = this.getValue();
33149         this.validate();
33150         return;
33151     },
33152     
33153     setMonth : function(v)
33154     {
33155         this.monthField.setValue(v, true);
33156         this.inputEl.dom.value = this.getValue();
33157         this.validate();
33158         return;
33159     },
33160     
33161     setYear : function(v)
33162     {
33163         this.yearField.setValue(v);
33164         this.inputEl.dom.value = this.getValue();
33165         this.validate();
33166         return;
33167     },
33168     
33169     getDay : function()
33170     {
33171         return this.dayField.getValue();
33172     },
33173     
33174     getMonth : function()
33175     {
33176         return this.monthField.getValue();
33177     },
33178     
33179     getYear : function()
33180     {
33181         return this.yearField.getValue();
33182     },
33183     
33184     getValue : function()
33185     {
33186         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33187         
33188         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33189         
33190         return date;
33191     },
33192     
33193     reset : function()
33194     {
33195         this.setDay('');
33196         this.setMonth('');
33197         this.setYear('');
33198         this.inputEl.dom.value = '';
33199         this.validate();
33200         return;
33201     },
33202     
33203     validate : function()
33204     {
33205         var d = this.dayField.validate();
33206         var m = this.monthField.validate();
33207         var y = this.yearField.validate();
33208         
33209         var valid = true;
33210         
33211         if(
33212                 (!this.dayAllowBlank && !d) ||
33213                 (!this.monthAllowBlank && !m) ||
33214                 (!this.yearAllowBlank && !y)
33215         ){
33216             valid = false;
33217         }
33218         
33219         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33220             return valid;
33221         }
33222         
33223         if(valid){
33224             this.markValid();
33225             return valid;
33226         }
33227         
33228         this.markInvalid();
33229         
33230         return valid;
33231     },
33232     
33233     markValid : function()
33234     {
33235         
33236         var label = this.el.select('label', true).first();
33237         var icon = this.el.select('i.fa-star', true).first();
33238
33239         if(label && icon){
33240             icon.remove();
33241         }
33242         
33243         this.fireEvent('valid', this);
33244     },
33245     
33246      /**
33247      * Mark this field as invalid
33248      * @param {String} msg The validation message
33249      */
33250     markInvalid : function(msg)
33251     {
33252         
33253         var label = this.el.select('label', true).first();
33254         var icon = this.el.select('i.fa-star', true).first();
33255
33256         if(label && !icon){
33257             this.el.select('.roo-date-split-field-label', true).createChild({
33258                 tag : 'i',
33259                 cls : 'text-danger fa fa-lg fa-star',
33260                 tooltip : 'This field is required',
33261                 style : 'margin-right:5px;'
33262             }, label, true);
33263         }
33264         
33265         this.fireEvent('invalid', this, msg);
33266     },
33267     
33268     clearInvalid : function()
33269     {
33270         var label = this.el.select('label', true).first();
33271         var icon = this.el.select('i.fa-star', true).first();
33272
33273         if(label && icon){
33274             icon.remove();
33275         }
33276         
33277         this.fireEvent('valid', this);
33278     },
33279     
33280     getName: function()
33281     {
33282         return this.name;
33283     }
33284     
33285 });
33286
33287  /**
33288  *
33289  * This is based on 
33290  * http://masonry.desandro.com
33291  *
33292  * The idea is to render all the bricks based on vertical width...
33293  *
33294  * The original code extends 'outlayer' - we might need to use that....
33295  * 
33296  */
33297
33298
33299 /**
33300  * @class Roo.bootstrap.LayoutMasonry
33301  * @extends Roo.bootstrap.Component
33302  * Bootstrap Layout Masonry class
33303  * 
33304  * @constructor
33305  * Create a new Element
33306  * @param {Object} config The config object
33307  */
33308
33309 Roo.bootstrap.LayoutMasonry = function(config){
33310     
33311     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33312     
33313     this.bricks = [];
33314     
33315     Roo.bootstrap.LayoutMasonry.register(this);
33316     
33317     this.addEvents({
33318         // raw events
33319         /**
33320          * @event layout
33321          * Fire after layout the items
33322          * @param {Roo.bootstrap.LayoutMasonry} this
33323          * @param {Roo.EventObject} e
33324          */
33325         "layout" : true
33326     });
33327     
33328 };
33329
33330 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33331     
33332     /**
33333      * @cfg {Boolean} isLayoutInstant = no animation?
33334      */   
33335     isLayoutInstant : false, // needed?
33336    
33337     /**
33338      * @cfg {Number} boxWidth  width of the columns
33339      */   
33340     boxWidth : 450,
33341     
33342       /**
33343      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33344      */   
33345     boxHeight : 0,
33346     
33347     /**
33348      * @cfg {Number} padWidth padding below box..
33349      */   
33350     padWidth : 10, 
33351     
33352     /**
33353      * @cfg {Number} gutter gutter width..
33354      */   
33355     gutter : 10,
33356     
33357      /**
33358      * @cfg {Number} maxCols maximum number of columns
33359      */   
33360     
33361     maxCols: 0,
33362     
33363     /**
33364      * @cfg {Boolean} isAutoInitial defalut true
33365      */   
33366     isAutoInitial : true, 
33367     
33368     containerWidth: 0,
33369     
33370     /**
33371      * @cfg {Boolean} isHorizontal defalut false
33372      */   
33373     isHorizontal : false, 
33374
33375     currentSize : null,
33376     
33377     tag: 'div',
33378     
33379     cls: '',
33380     
33381     bricks: null, //CompositeElement
33382     
33383     cols : 1,
33384     
33385     _isLayoutInited : false,
33386     
33387 //    isAlternative : false, // only use for vertical layout...
33388     
33389     /**
33390      * @cfg {Number} alternativePadWidth padding below box..
33391      */   
33392     alternativePadWidth : 50,
33393     
33394     selectedBrick : [],
33395     
33396     getAutoCreate : function(){
33397         
33398         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33399         
33400         var cfg = {
33401             tag: this.tag,
33402             cls: 'blog-masonary-wrapper ' + this.cls,
33403             cn : {
33404                 cls : 'mas-boxes masonary'
33405             }
33406         };
33407         
33408         return cfg;
33409     },
33410     
33411     getChildContainer: function( )
33412     {
33413         if (this.boxesEl) {
33414             return this.boxesEl;
33415         }
33416         
33417         this.boxesEl = this.el.select('.mas-boxes').first();
33418         
33419         return this.boxesEl;
33420     },
33421     
33422     
33423     initEvents : function()
33424     {
33425         var _this = this;
33426         
33427         if(this.isAutoInitial){
33428             Roo.log('hook children rendered');
33429             this.on('childrenrendered', function() {
33430                 Roo.log('children rendered');
33431                 _this.initial();
33432             } ,this);
33433         }
33434     },
33435     
33436     initial : function()
33437     {
33438         this.selectedBrick = [];
33439         
33440         this.currentSize = this.el.getBox(true);
33441         
33442         Roo.EventManager.onWindowResize(this.resize, this); 
33443
33444         if(!this.isAutoInitial){
33445             this.layout();
33446             return;
33447         }
33448         
33449         this.layout();
33450         
33451         return;
33452         //this.layout.defer(500,this);
33453         
33454     },
33455     
33456     resize : function()
33457     {
33458         var cs = this.el.getBox(true);
33459         
33460         if (
33461                 this.currentSize.width == cs.width && 
33462                 this.currentSize.x == cs.x && 
33463                 this.currentSize.height == cs.height && 
33464                 this.currentSize.y == cs.y 
33465         ) {
33466             Roo.log("no change in with or X or Y");
33467             return;
33468         }
33469         
33470         this.currentSize = cs;
33471         
33472         this.layout();
33473         
33474     },
33475     
33476     layout : function()
33477     {   
33478         this._resetLayout();
33479         
33480         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33481         
33482         this.layoutItems( isInstant );
33483       
33484         this._isLayoutInited = true;
33485         
33486         this.fireEvent('layout', this);
33487         
33488     },
33489     
33490     _resetLayout : function()
33491     {
33492         if(this.isHorizontal){
33493             this.horizontalMeasureColumns();
33494             return;
33495         }
33496         
33497         this.verticalMeasureColumns();
33498         
33499     },
33500     
33501     verticalMeasureColumns : function()
33502     {
33503         this.getContainerWidth();
33504         
33505 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33506 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33507 //            return;
33508 //        }
33509         
33510         var boxWidth = this.boxWidth + this.padWidth;
33511         
33512         if(this.containerWidth < this.boxWidth){
33513             boxWidth = this.containerWidth
33514         }
33515         
33516         var containerWidth = this.containerWidth;
33517         
33518         var cols = Math.floor(containerWidth / boxWidth);
33519         
33520         this.cols = Math.max( cols, 1 );
33521         
33522         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33523         
33524         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33525         
33526         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33527         
33528         this.colWidth = boxWidth + avail - this.padWidth;
33529         
33530         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33531         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33532     },
33533     
33534     horizontalMeasureColumns : function()
33535     {
33536         this.getContainerWidth();
33537         
33538         var boxWidth = this.boxWidth;
33539         
33540         if(this.containerWidth < boxWidth){
33541             boxWidth = this.containerWidth;
33542         }
33543         
33544         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33545         
33546         this.el.setHeight(boxWidth);
33547         
33548     },
33549     
33550     getContainerWidth : function()
33551     {
33552         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33553     },
33554     
33555     layoutItems : function( isInstant )
33556     {
33557         Roo.log(this.bricks);
33558         
33559         var items = Roo.apply([], this.bricks);
33560         
33561         if(this.isHorizontal){
33562             this._horizontalLayoutItems( items , isInstant );
33563             return;
33564         }
33565         
33566 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33567 //            this._verticalAlternativeLayoutItems( items , isInstant );
33568 //            return;
33569 //        }
33570         
33571         this._verticalLayoutItems( items , isInstant );
33572         
33573     },
33574     
33575     _verticalLayoutItems : function ( items , isInstant)
33576     {
33577         if ( !items || !items.length ) {
33578             return;
33579         }
33580         
33581         var standard = [
33582             ['xs', 'xs', 'xs', 'tall'],
33583             ['xs', 'xs', 'tall'],
33584             ['xs', 'xs', 'sm'],
33585             ['xs', 'xs', 'xs'],
33586             ['xs', 'tall'],
33587             ['xs', 'sm'],
33588             ['xs', 'xs'],
33589             ['xs'],
33590             
33591             ['sm', 'xs', 'xs'],
33592             ['sm', 'xs'],
33593             ['sm'],
33594             
33595             ['tall', 'xs', 'xs', 'xs'],
33596             ['tall', 'xs', 'xs'],
33597             ['tall', 'xs'],
33598             ['tall']
33599             
33600         ];
33601         
33602         var queue = [];
33603         
33604         var boxes = [];
33605         
33606         var box = [];
33607         
33608         Roo.each(items, function(item, k){
33609             
33610             switch (item.size) {
33611                 // these layouts take up a full box,
33612                 case 'md' :
33613                 case 'md-left' :
33614                 case 'md-right' :
33615                 case 'wide' :
33616                     
33617                     if(box.length){
33618                         boxes.push(box);
33619                         box = [];
33620                     }
33621                     
33622                     boxes.push([item]);
33623                     
33624                     break;
33625                     
33626                 case 'xs' :
33627                 case 'sm' :
33628                 case 'tall' :
33629                     
33630                     box.push(item);
33631                     
33632                     break;
33633                 default :
33634                     break;
33635                     
33636             }
33637             
33638         }, this);
33639         
33640         if(box.length){
33641             boxes.push(box);
33642             box = [];
33643         }
33644         
33645         var filterPattern = function(box, length)
33646         {
33647             if(!box.length){
33648                 return;
33649             }
33650             
33651             var match = false;
33652             
33653             var pattern = box.slice(0, length);
33654             
33655             var format = [];
33656             
33657             Roo.each(pattern, function(i){
33658                 format.push(i.size);
33659             }, this);
33660             
33661             Roo.each(standard, function(s){
33662                 
33663                 if(String(s) != String(format)){
33664                     return;
33665                 }
33666                 
33667                 match = true;
33668                 return false;
33669                 
33670             }, this);
33671             
33672             if(!match && length == 1){
33673                 return;
33674             }
33675             
33676             if(!match){
33677                 filterPattern(box, length - 1);
33678                 return;
33679             }
33680                 
33681             queue.push(pattern);
33682
33683             box = box.slice(length, box.length);
33684
33685             filterPattern(box, 4);
33686
33687             return;
33688             
33689         }
33690         
33691         Roo.each(boxes, function(box, k){
33692             
33693             if(!box.length){
33694                 return;
33695             }
33696             
33697             if(box.length == 1){
33698                 queue.push(box);
33699                 return;
33700             }
33701             
33702             filterPattern(box, 4);
33703             
33704         }, this);
33705         
33706         this._processVerticalLayoutQueue( queue, isInstant );
33707         
33708     },
33709     
33710 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33711 //    {
33712 //        if ( !items || !items.length ) {
33713 //            return;
33714 //        }
33715 //
33716 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33717 //        
33718 //    },
33719     
33720     _horizontalLayoutItems : function ( items , isInstant)
33721     {
33722         if ( !items || !items.length || items.length < 3) {
33723             return;
33724         }
33725         
33726         items.reverse();
33727         
33728         var eItems = items.slice(0, 3);
33729         
33730         items = items.slice(3, items.length);
33731         
33732         var standard = [
33733             ['xs', 'xs', 'xs', 'wide'],
33734             ['xs', 'xs', 'wide'],
33735             ['xs', 'xs', 'sm'],
33736             ['xs', 'xs', 'xs'],
33737             ['xs', 'wide'],
33738             ['xs', 'sm'],
33739             ['xs', 'xs'],
33740             ['xs'],
33741             
33742             ['sm', 'xs', 'xs'],
33743             ['sm', 'xs'],
33744             ['sm'],
33745             
33746             ['wide', 'xs', 'xs', 'xs'],
33747             ['wide', 'xs', 'xs'],
33748             ['wide', 'xs'],
33749             ['wide'],
33750             
33751             ['wide-thin']
33752         ];
33753         
33754         var queue = [];
33755         
33756         var boxes = [];
33757         
33758         var box = [];
33759         
33760         Roo.each(items, function(item, k){
33761             
33762             switch (item.size) {
33763                 case 'md' :
33764                 case 'md-left' :
33765                 case 'md-right' :
33766                 case 'tall' :
33767                     
33768                     if(box.length){
33769                         boxes.push(box);
33770                         box = [];
33771                     }
33772                     
33773                     boxes.push([item]);
33774                     
33775                     break;
33776                     
33777                 case 'xs' :
33778                 case 'sm' :
33779                 case 'wide' :
33780                 case 'wide-thin' :
33781                     
33782                     box.push(item);
33783                     
33784                     break;
33785                 default :
33786                     break;
33787                     
33788             }
33789             
33790         }, this);
33791         
33792         if(box.length){
33793             boxes.push(box);
33794             box = [];
33795         }
33796         
33797         var filterPattern = function(box, length)
33798         {
33799             if(!box.length){
33800                 return;
33801             }
33802             
33803             var match = false;
33804             
33805             var pattern = box.slice(0, length);
33806             
33807             var format = [];
33808             
33809             Roo.each(pattern, function(i){
33810                 format.push(i.size);
33811             }, this);
33812             
33813             Roo.each(standard, function(s){
33814                 
33815                 if(String(s) != String(format)){
33816                     return;
33817                 }
33818                 
33819                 match = true;
33820                 return false;
33821                 
33822             }, this);
33823             
33824             if(!match && length == 1){
33825                 return;
33826             }
33827             
33828             if(!match){
33829                 filterPattern(box, length - 1);
33830                 return;
33831             }
33832                 
33833             queue.push(pattern);
33834
33835             box = box.slice(length, box.length);
33836
33837             filterPattern(box, 4);
33838
33839             return;
33840             
33841         }
33842         
33843         Roo.each(boxes, function(box, k){
33844             
33845             if(!box.length){
33846                 return;
33847             }
33848             
33849             if(box.length == 1){
33850                 queue.push(box);
33851                 return;
33852             }
33853             
33854             filterPattern(box, 4);
33855             
33856         }, this);
33857         
33858         
33859         var prune = [];
33860         
33861         var pos = this.el.getBox(true);
33862         
33863         var minX = pos.x;
33864         
33865         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33866         
33867         var hit_end = false;
33868         
33869         Roo.each(queue, function(box){
33870             
33871             if(hit_end){
33872                 
33873                 Roo.each(box, function(b){
33874                 
33875                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33876                     b.el.hide();
33877
33878                 }, this);
33879
33880                 return;
33881             }
33882             
33883             var mx = 0;
33884             
33885             Roo.each(box, function(b){
33886                 
33887                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33888                 b.el.show();
33889
33890                 mx = Math.max(mx, b.x);
33891                 
33892             }, this);
33893             
33894             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33895             
33896             if(maxX < minX){
33897                 
33898                 Roo.each(box, function(b){
33899                 
33900                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33901                     b.el.hide();
33902                     
33903                 }, this);
33904                 
33905                 hit_end = true;
33906                 
33907                 return;
33908             }
33909             
33910             prune.push(box);
33911             
33912         }, this);
33913         
33914         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33915     },
33916     
33917     /** Sets position of item in DOM
33918     * @param {Element} item
33919     * @param {Number} x - horizontal position
33920     * @param {Number} y - vertical position
33921     * @param {Boolean} isInstant - disables transitions
33922     */
33923     _processVerticalLayoutQueue : function( queue, isInstant )
33924     {
33925         var pos = this.el.getBox(true);
33926         var x = pos.x;
33927         var y = pos.y;
33928         var maxY = [];
33929         
33930         for (var i = 0; i < this.cols; i++){
33931             maxY[i] = pos.y;
33932         }
33933         
33934         Roo.each(queue, function(box, k){
33935             
33936             var col = k % this.cols;
33937             
33938             Roo.each(box, function(b,kk){
33939                 
33940                 b.el.position('absolute');
33941                 
33942                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33943                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33944                 
33945                 if(b.size == 'md-left' || b.size == 'md-right'){
33946                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33947                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33948                 }
33949                 
33950                 b.el.setWidth(width);
33951                 b.el.setHeight(height);
33952                 // iframe?
33953                 b.el.select('iframe',true).setSize(width,height);
33954                 
33955             }, this);
33956             
33957             for (var i = 0; i < this.cols; i++){
33958                 
33959                 if(maxY[i] < maxY[col]){
33960                     col = i;
33961                     continue;
33962                 }
33963                 
33964                 col = Math.min(col, i);
33965                 
33966             }
33967             
33968             x = pos.x + col * (this.colWidth + this.padWidth);
33969             
33970             y = maxY[col];
33971             
33972             var positions = [];
33973             
33974             switch (box.length){
33975                 case 1 :
33976                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33977                     break;
33978                 case 2 :
33979                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33980                     break;
33981                 case 3 :
33982                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33983                     break;
33984                 case 4 :
33985                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33986                     break;
33987                 default :
33988                     break;
33989             }
33990             
33991             Roo.each(box, function(b,kk){
33992                 
33993                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33994                 
33995                 var sz = b.el.getSize();
33996                 
33997                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33998                 
33999             }, this);
34000             
34001         }, this);
34002         
34003         var mY = 0;
34004         
34005         for (var i = 0; i < this.cols; i++){
34006             mY = Math.max(mY, maxY[i]);
34007         }
34008         
34009         this.el.setHeight(mY - pos.y);
34010         
34011     },
34012     
34013 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34014 //    {
34015 //        var pos = this.el.getBox(true);
34016 //        var x = pos.x;
34017 //        var y = pos.y;
34018 //        var maxX = pos.right;
34019 //        
34020 //        var maxHeight = 0;
34021 //        
34022 //        Roo.each(items, function(item, k){
34023 //            
34024 //            var c = k % 2;
34025 //            
34026 //            item.el.position('absolute');
34027 //                
34028 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34029 //
34030 //            item.el.setWidth(width);
34031 //
34032 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34033 //
34034 //            item.el.setHeight(height);
34035 //            
34036 //            if(c == 0){
34037 //                item.el.setXY([x, y], isInstant ? false : true);
34038 //            } else {
34039 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34040 //            }
34041 //            
34042 //            y = y + height + this.alternativePadWidth;
34043 //            
34044 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34045 //            
34046 //        }, this);
34047 //        
34048 //        this.el.setHeight(maxHeight);
34049 //        
34050 //    },
34051     
34052     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34053     {
34054         var pos = this.el.getBox(true);
34055         
34056         var minX = pos.x;
34057         var minY = pos.y;
34058         
34059         var maxX = pos.right;
34060         
34061         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34062         
34063         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34064         
34065         Roo.each(queue, function(box, k){
34066             
34067             Roo.each(box, function(b, kk){
34068                 
34069                 b.el.position('absolute');
34070                 
34071                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34072                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34073                 
34074                 if(b.size == 'md-left' || b.size == 'md-right'){
34075                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34076                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34077                 }
34078                 
34079                 b.el.setWidth(width);
34080                 b.el.setHeight(height);
34081                 
34082             }, this);
34083             
34084             if(!box.length){
34085                 return;
34086             }
34087             
34088             var positions = [];
34089             
34090             switch (box.length){
34091                 case 1 :
34092                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34093                     break;
34094                 case 2 :
34095                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34096                     break;
34097                 case 3 :
34098                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34099                     break;
34100                 case 4 :
34101                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34102                     break;
34103                 default :
34104                     break;
34105             }
34106             
34107             Roo.each(box, function(b,kk){
34108                 
34109                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34110                 
34111                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34112                 
34113             }, this);
34114             
34115         }, this);
34116         
34117     },
34118     
34119     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34120     {
34121         Roo.each(eItems, function(b,k){
34122             
34123             b.size = (k == 0) ? 'sm' : 'xs';
34124             b.x = (k == 0) ? 2 : 1;
34125             b.y = (k == 0) ? 2 : 1;
34126             
34127             b.el.position('absolute');
34128             
34129             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34130                 
34131             b.el.setWidth(width);
34132             
34133             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34134             
34135             b.el.setHeight(height);
34136             
34137         }, this);
34138
34139         var positions = [];
34140         
34141         positions.push({
34142             x : maxX - this.unitWidth * 2 - this.gutter,
34143             y : minY
34144         });
34145         
34146         positions.push({
34147             x : maxX - this.unitWidth,
34148             y : minY + (this.unitWidth + this.gutter) * 2
34149         });
34150         
34151         positions.push({
34152             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34153             y : minY
34154         });
34155         
34156         Roo.each(eItems, function(b,k){
34157             
34158             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34159
34160         }, this);
34161         
34162     },
34163     
34164     getVerticalOneBoxColPositions : function(x, y, box)
34165     {
34166         var pos = [];
34167         
34168         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34169         
34170         if(box[0].size == 'md-left'){
34171             rand = 0;
34172         }
34173         
34174         if(box[0].size == 'md-right'){
34175             rand = 1;
34176         }
34177         
34178         pos.push({
34179             x : x + (this.unitWidth + this.gutter) * rand,
34180             y : y
34181         });
34182         
34183         return pos;
34184     },
34185     
34186     getVerticalTwoBoxColPositions : function(x, y, box)
34187     {
34188         var pos = [];
34189         
34190         if(box[0].size == 'xs'){
34191             
34192             pos.push({
34193                 x : x,
34194                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34195             });
34196
34197             pos.push({
34198                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34199                 y : y
34200             });
34201             
34202             return pos;
34203             
34204         }
34205         
34206         pos.push({
34207             x : x,
34208             y : y
34209         });
34210
34211         pos.push({
34212             x : x + (this.unitWidth + this.gutter) * 2,
34213             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34214         });
34215         
34216         return pos;
34217         
34218     },
34219     
34220     getVerticalThreeBoxColPositions : function(x, y, box)
34221     {
34222         var pos = [];
34223         
34224         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34225             
34226             pos.push({
34227                 x : x,
34228                 y : y
34229             });
34230
34231             pos.push({
34232                 x : x + (this.unitWidth + this.gutter) * 1,
34233                 y : y
34234             });
34235             
34236             pos.push({
34237                 x : x + (this.unitWidth + this.gutter) * 2,
34238                 y : y
34239             });
34240             
34241             return pos;
34242             
34243         }
34244         
34245         if(box[0].size == 'xs' && box[1].size == 'xs'){
34246             
34247             pos.push({
34248                 x : x,
34249                 y : y
34250             });
34251
34252             pos.push({
34253                 x : x,
34254                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34255             });
34256             
34257             pos.push({
34258                 x : x + (this.unitWidth + this.gutter) * 1,
34259                 y : y
34260             });
34261             
34262             return pos;
34263             
34264         }
34265         
34266         pos.push({
34267             x : x,
34268             y : y
34269         });
34270
34271         pos.push({
34272             x : x + (this.unitWidth + this.gutter) * 2,
34273             y : y
34274         });
34275
34276         pos.push({
34277             x : x + (this.unitWidth + this.gutter) * 2,
34278             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34279         });
34280             
34281         return pos;
34282         
34283     },
34284     
34285     getVerticalFourBoxColPositions : function(x, y, box)
34286     {
34287         var pos = [];
34288         
34289         if(box[0].size == 'xs'){
34290             
34291             pos.push({
34292                 x : x,
34293                 y : y
34294             });
34295
34296             pos.push({
34297                 x : x,
34298                 y : y + (this.unitHeight + this.gutter) * 1
34299             });
34300             
34301             pos.push({
34302                 x : x,
34303                 y : y + (this.unitHeight + this.gutter) * 2
34304             });
34305             
34306             pos.push({
34307                 x : x + (this.unitWidth + this.gutter) * 1,
34308                 y : y
34309             });
34310             
34311             return pos;
34312             
34313         }
34314         
34315         pos.push({
34316             x : x,
34317             y : y
34318         });
34319
34320         pos.push({
34321             x : x + (this.unitWidth + this.gutter) * 2,
34322             y : y
34323         });
34324
34325         pos.push({
34326             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34327             y : y + (this.unitHeight + this.gutter) * 1
34328         });
34329
34330         pos.push({
34331             x : x + (this.unitWidth + this.gutter) * 2,
34332             y : y + (this.unitWidth + this.gutter) * 2
34333         });
34334
34335         return pos;
34336         
34337     },
34338     
34339     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34340     {
34341         var pos = [];
34342         
34343         if(box[0].size == 'md-left'){
34344             pos.push({
34345                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34346                 y : minY
34347             });
34348             
34349             return pos;
34350         }
34351         
34352         if(box[0].size == 'md-right'){
34353             pos.push({
34354                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34355                 y : minY + (this.unitWidth + this.gutter) * 1
34356             });
34357             
34358             return pos;
34359         }
34360         
34361         var rand = Math.floor(Math.random() * (4 - box[0].y));
34362         
34363         pos.push({
34364             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34365             y : minY + (this.unitWidth + this.gutter) * rand
34366         });
34367         
34368         return pos;
34369         
34370     },
34371     
34372     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34373     {
34374         var pos = [];
34375         
34376         if(box[0].size == 'xs'){
34377             
34378             pos.push({
34379                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34380                 y : minY
34381             });
34382
34383             pos.push({
34384                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34385                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34386             });
34387             
34388             return pos;
34389             
34390         }
34391         
34392         pos.push({
34393             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34394             y : minY
34395         });
34396
34397         pos.push({
34398             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34399             y : minY + (this.unitWidth + this.gutter) * 2
34400         });
34401         
34402         return pos;
34403         
34404     },
34405     
34406     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34407     {
34408         var pos = [];
34409         
34410         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34411             
34412             pos.push({
34413                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34414                 y : minY
34415             });
34416
34417             pos.push({
34418                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34419                 y : minY + (this.unitWidth + this.gutter) * 1
34420             });
34421             
34422             pos.push({
34423                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34424                 y : minY + (this.unitWidth + this.gutter) * 2
34425             });
34426             
34427             return pos;
34428             
34429         }
34430         
34431         if(box[0].size == 'xs' && box[1].size == 'xs'){
34432             
34433             pos.push({
34434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34435                 y : minY
34436             });
34437
34438             pos.push({
34439                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34440                 y : minY
34441             });
34442             
34443             pos.push({
34444                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34445                 y : minY + (this.unitWidth + this.gutter) * 1
34446             });
34447             
34448             return pos;
34449             
34450         }
34451         
34452         pos.push({
34453             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34454             y : minY
34455         });
34456
34457         pos.push({
34458             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34459             y : minY + (this.unitWidth + this.gutter) * 2
34460         });
34461
34462         pos.push({
34463             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34464             y : minY + (this.unitWidth + this.gutter) * 2
34465         });
34466             
34467         return pos;
34468         
34469     },
34470     
34471     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34472     {
34473         var pos = [];
34474         
34475         if(box[0].size == 'xs'){
34476             
34477             pos.push({
34478                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34479                 y : minY
34480             });
34481
34482             pos.push({
34483                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34484                 y : minY
34485             });
34486             
34487             pos.push({
34488                 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),
34489                 y : minY
34490             });
34491             
34492             pos.push({
34493                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34494                 y : minY + (this.unitWidth + this.gutter) * 1
34495             });
34496             
34497             return pos;
34498             
34499         }
34500         
34501         pos.push({
34502             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34503             y : minY
34504         });
34505         
34506         pos.push({
34507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34508             y : minY + (this.unitWidth + this.gutter) * 2
34509         });
34510         
34511         pos.push({
34512             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34513             y : minY + (this.unitWidth + this.gutter) * 2
34514         });
34515         
34516         pos.push({
34517             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),
34518             y : minY + (this.unitWidth + this.gutter) * 2
34519         });
34520
34521         return pos;
34522         
34523     },
34524     
34525     /**
34526     * remove a Masonry Brick
34527     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34528     */
34529     removeBrick : function(brick_id)
34530     {
34531         if (!brick_id) {
34532             return;
34533         }
34534         
34535         for (var i = 0; i<this.bricks.length; i++) {
34536             if (this.bricks[i].id == brick_id) {
34537                 this.bricks.splice(i,1);
34538                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34539                 this.initial();
34540             }
34541         }
34542     },
34543     
34544     /**
34545     * adds a Masonry Brick
34546     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34547     */
34548     addBrick : function(cfg)
34549     {
34550         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34551         //this.register(cn);
34552         cn.parentId = this.id;
34553         cn.render(this.el);
34554         return cn;
34555     },
34556     
34557     /**
34558     * register a Masonry Brick
34559     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34560     */
34561     
34562     register : function(brick)
34563     {
34564         this.bricks.push(brick);
34565         brick.masonryId = this.id;
34566     },
34567     
34568     /**
34569     * clear all the Masonry Brick
34570     */
34571     clearAll : function()
34572     {
34573         this.bricks = [];
34574         //this.getChildContainer().dom.innerHTML = "";
34575         this.el.dom.innerHTML = '';
34576     },
34577     
34578     getSelected : function()
34579     {
34580         if (!this.selectedBrick) {
34581             return false;
34582         }
34583         
34584         return this.selectedBrick;
34585     }
34586 });
34587
34588 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34589     
34590     groups: {},
34591      /**
34592     * register a Masonry Layout
34593     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34594     */
34595     
34596     register : function(layout)
34597     {
34598         this.groups[layout.id] = layout;
34599     },
34600     /**
34601     * fetch a  Masonry Layout based on the masonry layout ID
34602     * @param {string} the masonry layout to add
34603     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34604     */
34605     
34606     get: function(layout_id) {
34607         if (typeof(this.groups[layout_id]) == 'undefined') {
34608             return false;
34609         }
34610         return this.groups[layout_id] ;
34611     }
34612     
34613     
34614     
34615 });
34616
34617  
34618
34619  /**
34620  *
34621  * This is based on 
34622  * http://masonry.desandro.com
34623  *
34624  * The idea is to render all the bricks based on vertical width...
34625  *
34626  * The original code extends 'outlayer' - we might need to use that....
34627  * 
34628  */
34629
34630
34631 /**
34632  * @class Roo.bootstrap.LayoutMasonryAuto
34633  * @extends Roo.bootstrap.Component
34634  * Bootstrap Layout Masonry class
34635  * 
34636  * @constructor
34637  * Create a new Element
34638  * @param {Object} config The config object
34639  */
34640
34641 Roo.bootstrap.LayoutMasonryAuto = function(config){
34642     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34643 };
34644
34645 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34646     
34647       /**
34648      * @cfg {Boolean} isFitWidth  - resize the width..
34649      */   
34650     isFitWidth : false,  // options..
34651     /**
34652      * @cfg {Boolean} isOriginLeft = left align?
34653      */   
34654     isOriginLeft : true,
34655     /**
34656      * @cfg {Boolean} isOriginTop = top align?
34657      */   
34658     isOriginTop : false,
34659     /**
34660      * @cfg {Boolean} isLayoutInstant = no animation?
34661      */   
34662     isLayoutInstant : false, // needed?
34663     /**
34664      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34665      */   
34666     isResizingContainer : true,
34667     /**
34668      * @cfg {Number} columnWidth  width of the columns 
34669      */   
34670     
34671     columnWidth : 0,
34672     
34673     /**
34674      * @cfg {Number} maxCols maximum number of columns
34675      */   
34676     
34677     maxCols: 0,
34678     /**
34679      * @cfg {Number} padHeight padding below box..
34680      */   
34681     
34682     padHeight : 10, 
34683     
34684     /**
34685      * @cfg {Boolean} isAutoInitial defalut true
34686      */   
34687     
34688     isAutoInitial : true, 
34689     
34690     // private?
34691     gutter : 0,
34692     
34693     containerWidth: 0,
34694     initialColumnWidth : 0,
34695     currentSize : null,
34696     
34697     colYs : null, // array.
34698     maxY : 0,
34699     padWidth: 10,
34700     
34701     
34702     tag: 'div',
34703     cls: '',
34704     bricks: null, //CompositeElement
34705     cols : 0, // array?
34706     // element : null, // wrapped now this.el
34707     _isLayoutInited : null, 
34708     
34709     
34710     getAutoCreate : function(){
34711         
34712         var cfg = {
34713             tag: this.tag,
34714             cls: 'blog-masonary-wrapper ' + this.cls,
34715             cn : {
34716                 cls : 'mas-boxes masonary'
34717             }
34718         };
34719         
34720         return cfg;
34721     },
34722     
34723     getChildContainer: function( )
34724     {
34725         if (this.boxesEl) {
34726             return this.boxesEl;
34727         }
34728         
34729         this.boxesEl = this.el.select('.mas-boxes').first();
34730         
34731         return this.boxesEl;
34732     },
34733     
34734     
34735     initEvents : function()
34736     {
34737         var _this = this;
34738         
34739         if(this.isAutoInitial){
34740             Roo.log('hook children rendered');
34741             this.on('childrenrendered', function() {
34742                 Roo.log('children rendered');
34743                 _this.initial();
34744             } ,this);
34745         }
34746         
34747     },
34748     
34749     initial : function()
34750     {
34751         this.reloadItems();
34752
34753         this.currentSize = this.el.getBox(true);
34754
34755         /// was window resize... - let's see if this works..
34756         Roo.EventManager.onWindowResize(this.resize, this); 
34757
34758         if(!this.isAutoInitial){
34759             this.layout();
34760             return;
34761         }
34762         
34763         this.layout.defer(500,this);
34764     },
34765     
34766     reloadItems: function()
34767     {
34768         this.bricks = this.el.select('.masonry-brick', true);
34769         
34770         this.bricks.each(function(b) {
34771             //Roo.log(b.getSize());
34772             if (!b.attr('originalwidth')) {
34773                 b.attr('originalwidth',  b.getSize().width);
34774             }
34775             
34776         });
34777         
34778         Roo.log(this.bricks.elements.length);
34779     },
34780     
34781     resize : function()
34782     {
34783         Roo.log('resize');
34784         var cs = this.el.getBox(true);
34785         
34786         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34787             Roo.log("no change in with or X");
34788             return;
34789         }
34790         this.currentSize = cs;
34791         this.layout();
34792     },
34793     
34794     layout : function()
34795     {
34796          Roo.log('layout');
34797         this._resetLayout();
34798         //this._manageStamps();
34799       
34800         // don't animate first layout
34801         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34802         this.layoutItems( isInstant );
34803       
34804         // flag for initalized
34805         this._isLayoutInited = true;
34806     },
34807     
34808     layoutItems : function( isInstant )
34809     {
34810         //var items = this._getItemsForLayout( this.items );
34811         // original code supports filtering layout items.. we just ignore it..
34812         
34813         this._layoutItems( this.bricks , isInstant );
34814       
34815         this._postLayout();
34816     },
34817     _layoutItems : function ( items , isInstant)
34818     {
34819        //this.fireEvent( 'layout', this, items );
34820     
34821
34822         if ( !items || !items.elements.length ) {
34823           // no items, emit event with empty array
34824             return;
34825         }
34826
34827         var queue = [];
34828         items.each(function(item) {
34829             Roo.log("layout item");
34830             Roo.log(item);
34831             // get x/y object from method
34832             var position = this._getItemLayoutPosition( item );
34833             // enqueue
34834             position.item = item;
34835             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34836             queue.push( position );
34837         }, this);
34838       
34839         this._processLayoutQueue( queue );
34840     },
34841     /** Sets position of item in DOM
34842     * @param {Element} item
34843     * @param {Number} x - horizontal position
34844     * @param {Number} y - vertical position
34845     * @param {Boolean} isInstant - disables transitions
34846     */
34847     _processLayoutQueue : function( queue )
34848     {
34849         for ( var i=0, len = queue.length; i < len; i++ ) {
34850             var obj = queue[i];
34851             obj.item.position('absolute');
34852             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34853         }
34854     },
34855       
34856     
34857     /**
34858     * Any logic you want to do after each layout,
34859     * i.e. size the container
34860     */
34861     _postLayout : function()
34862     {
34863         this.resizeContainer();
34864     },
34865     
34866     resizeContainer : function()
34867     {
34868         if ( !this.isResizingContainer ) {
34869             return;
34870         }
34871         var size = this._getContainerSize();
34872         if ( size ) {
34873             this.el.setSize(size.width,size.height);
34874             this.boxesEl.setSize(size.width,size.height);
34875         }
34876     },
34877     
34878     
34879     
34880     _resetLayout : function()
34881     {
34882         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34883         this.colWidth = this.el.getWidth();
34884         //this.gutter = this.el.getWidth(); 
34885         
34886         this.measureColumns();
34887
34888         // reset column Y
34889         var i = this.cols;
34890         this.colYs = [];
34891         while (i--) {
34892             this.colYs.push( 0 );
34893         }
34894     
34895         this.maxY = 0;
34896     },
34897
34898     measureColumns : function()
34899     {
34900         this.getContainerWidth();
34901       // if columnWidth is 0, default to outerWidth of first item
34902         if ( !this.columnWidth ) {
34903             var firstItem = this.bricks.first();
34904             Roo.log(firstItem);
34905             this.columnWidth  = this.containerWidth;
34906             if (firstItem && firstItem.attr('originalwidth') ) {
34907                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34908             }
34909             // columnWidth fall back to item of first element
34910             Roo.log("set column width?");
34911                         this.initialColumnWidth = this.columnWidth  ;
34912
34913             // if first elem has no width, default to size of container
34914             
34915         }
34916         
34917         
34918         if (this.initialColumnWidth) {
34919             this.columnWidth = this.initialColumnWidth;
34920         }
34921         
34922         
34923             
34924         // column width is fixed at the top - however if container width get's smaller we should
34925         // reduce it...
34926         
34927         // this bit calcs how man columns..
34928             
34929         var columnWidth = this.columnWidth += this.gutter;
34930       
34931         // calculate columns
34932         var containerWidth = this.containerWidth + this.gutter;
34933         
34934         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34935         // fix rounding errors, typically with gutters
34936         var excess = columnWidth - containerWidth % columnWidth;
34937         
34938         
34939         // if overshoot is less than a pixel, round up, otherwise floor it
34940         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34941         cols = Math[ mathMethod ]( cols );
34942         this.cols = Math.max( cols, 1 );
34943         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34944         
34945          // padding positioning..
34946         var totalColWidth = this.cols * this.columnWidth;
34947         var padavail = this.containerWidth - totalColWidth;
34948         // so for 2 columns - we need 3 'pads'
34949         
34950         var padNeeded = (1+this.cols) * this.padWidth;
34951         
34952         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34953         
34954         this.columnWidth += padExtra
34955         //this.padWidth = Math.floor(padavail /  ( this.cols));
34956         
34957         // adjust colum width so that padding is fixed??
34958         
34959         // we have 3 columns ... total = width * 3
34960         // we have X left over... that should be used by 
34961         
34962         //if (this.expandC) {
34963             
34964         //}
34965         
34966         
34967         
34968     },
34969     
34970     getContainerWidth : function()
34971     {
34972        /* // container is parent if fit width
34973         var container = this.isFitWidth ? this.element.parentNode : this.element;
34974         // check that this.size and size are there
34975         // IE8 triggers resize on body size change, so they might not be
34976         
34977         var size = getSize( container );  //FIXME
34978         this.containerWidth = size && size.innerWidth; //FIXME
34979         */
34980          
34981         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34982         
34983     },
34984     
34985     _getItemLayoutPosition : function( item )  // what is item?
34986     {
34987         // we resize the item to our columnWidth..
34988       
34989         item.setWidth(this.columnWidth);
34990         item.autoBoxAdjust  = false;
34991         
34992         var sz = item.getSize();
34993  
34994         // how many columns does this brick span
34995         var remainder = this.containerWidth % this.columnWidth;
34996         
34997         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34998         // round if off by 1 pixel, otherwise use ceil
34999         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35000         colSpan = Math.min( colSpan, this.cols );
35001         
35002         // normally this should be '1' as we dont' currently allow multi width columns..
35003         
35004         var colGroup = this._getColGroup( colSpan );
35005         // get the minimum Y value from the columns
35006         var minimumY = Math.min.apply( Math, colGroup );
35007         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35008         
35009         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35010          
35011         // position the brick
35012         var position = {
35013             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35014             y: this.currentSize.y + minimumY + this.padHeight
35015         };
35016         
35017         Roo.log(position);
35018         // apply setHeight to necessary columns
35019         var setHeight = minimumY + sz.height + this.padHeight;
35020         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35021         
35022         var setSpan = this.cols + 1 - colGroup.length;
35023         for ( var i = 0; i < setSpan; i++ ) {
35024           this.colYs[ shortColIndex + i ] = setHeight ;
35025         }
35026       
35027         return position;
35028     },
35029     
35030     /**
35031      * @param {Number} colSpan - number of columns the element spans
35032      * @returns {Array} colGroup
35033      */
35034     _getColGroup : function( colSpan )
35035     {
35036         if ( colSpan < 2 ) {
35037           // if brick spans only one column, use all the column Ys
35038           return this.colYs;
35039         }
35040       
35041         var colGroup = [];
35042         // how many different places could this brick fit horizontally
35043         var groupCount = this.cols + 1 - colSpan;
35044         // for each group potential horizontal position
35045         for ( var i = 0; i < groupCount; i++ ) {
35046           // make an array of colY values for that one group
35047           var groupColYs = this.colYs.slice( i, i + colSpan );
35048           // and get the max value of the array
35049           colGroup[i] = Math.max.apply( Math, groupColYs );
35050         }
35051         return colGroup;
35052     },
35053     /*
35054     _manageStamp : function( stamp )
35055     {
35056         var stampSize =  stamp.getSize();
35057         var offset = stamp.getBox();
35058         // get the columns that this stamp affects
35059         var firstX = this.isOriginLeft ? offset.x : offset.right;
35060         var lastX = firstX + stampSize.width;
35061         var firstCol = Math.floor( firstX / this.columnWidth );
35062         firstCol = Math.max( 0, firstCol );
35063         
35064         var lastCol = Math.floor( lastX / this.columnWidth );
35065         // lastCol should not go over if multiple of columnWidth #425
35066         lastCol -= lastX % this.columnWidth ? 0 : 1;
35067         lastCol = Math.min( this.cols - 1, lastCol );
35068         
35069         // set colYs to bottom of the stamp
35070         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35071             stampSize.height;
35072             
35073         for ( var i = firstCol; i <= lastCol; i++ ) {
35074           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35075         }
35076     },
35077     */
35078     
35079     _getContainerSize : function()
35080     {
35081         this.maxY = Math.max.apply( Math, this.colYs );
35082         var size = {
35083             height: this.maxY
35084         };
35085       
35086         if ( this.isFitWidth ) {
35087             size.width = this._getContainerFitWidth();
35088         }
35089       
35090         return size;
35091     },
35092     
35093     _getContainerFitWidth : function()
35094     {
35095         var unusedCols = 0;
35096         // count unused columns
35097         var i = this.cols;
35098         while ( --i ) {
35099           if ( this.colYs[i] !== 0 ) {
35100             break;
35101           }
35102           unusedCols++;
35103         }
35104         // fit container to columns that have been used
35105         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35106     },
35107     
35108     needsResizeLayout : function()
35109     {
35110         var previousWidth = this.containerWidth;
35111         this.getContainerWidth();
35112         return previousWidth !== this.containerWidth;
35113     }
35114  
35115 });
35116
35117  
35118
35119  /*
35120  * - LGPL
35121  *
35122  * element
35123  * 
35124  */
35125
35126 /**
35127  * @class Roo.bootstrap.MasonryBrick
35128  * @extends Roo.bootstrap.Component
35129  * Bootstrap MasonryBrick class
35130  * 
35131  * @constructor
35132  * Create a new MasonryBrick
35133  * @param {Object} config The config object
35134  */
35135
35136 Roo.bootstrap.MasonryBrick = function(config){
35137     
35138     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35139     
35140     Roo.bootstrap.MasonryBrick.register(this);
35141     
35142     this.addEvents({
35143         // raw events
35144         /**
35145          * @event click
35146          * When a MasonryBrick is clcik
35147          * @param {Roo.bootstrap.MasonryBrick} this
35148          * @param {Roo.EventObject} e
35149          */
35150         "click" : true
35151     });
35152 };
35153
35154 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35155     
35156     /**
35157      * @cfg {String} title
35158      */   
35159     title : '',
35160     /**
35161      * @cfg {String} html
35162      */   
35163     html : '',
35164     /**
35165      * @cfg {String} bgimage
35166      */   
35167     bgimage : '',
35168     /**
35169      * @cfg {String} videourl
35170      */   
35171     videourl : '',
35172     /**
35173      * @cfg {String} cls
35174      */   
35175     cls : '',
35176     /**
35177      * @cfg {String} href
35178      */   
35179     href : '',
35180     /**
35181      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35182      */   
35183     size : 'xs',
35184     
35185     /**
35186      * @cfg {String} placetitle (center|bottom)
35187      */   
35188     placetitle : '',
35189     
35190     /**
35191      * @cfg {Boolean} isFitContainer defalut true
35192      */   
35193     isFitContainer : true, 
35194     
35195     /**
35196      * @cfg {Boolean} preventDefault defalut false
35197      */   
35198     preventDefault : false, 
35199     
35200     /**
35201      * @cfg {Boolean} inverse defalut false
35202      */   
35203     maskInverse : false, 
35204     
35205     getAutoCreate : function()
35206     {
35207         if(!this.isFitContainer){
35208             return this.getSplitAutoCreate();
35209         }
35210         
35211         var cls = 'masonry-brick masonry-brick-full';
35212         
35213         if(this.href.length){
35214             cls += ' masonry-brick-link';
35215         }
35216         
35217         if(this.bgimage.length){
35218             cls += ' masonry-brick-image';
35219         }
35220         
35221         if(this.maskInverse){
35222             cls += ' mask-inverse';
35223         }
35224         
35225         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35226             cls += ' enable-mask';
35227         }
35228         
35229         if(this.size){
35230             cls += ' masonry-' + this.size + '-brick';
35231         }
35232         
35233         if(this.placetitle.length){
35234             
35235             switch (this.placetitle) {
35236                 case 'center' :
35237                     cls += ' masonry-center-title';
35238                     break;
35239                 case 'bottom' :
35240                     cls += ' masonry-bottom-title';
35241                     break;
35242                 default:
35243                     break;
35244             }
35245             
35246         } else {
35247             if(!this.html.length && !this.bgimage.length){
35248                 cls += ' masonry-center-title';
35249             }
35250
35251             if(!this.html.length && this.bgimage.length){
35252                 cls += ' masonry-bottom-title';
35253             }
35254         }
35255         
35256         if(this.cls){
35257             cls += ' ' + this.cls;
35258         }
35259         
35260         var cfg = {
35261             tag: (this.href.length) ? 'a' : 'div',
35262             cls: cls,
35263             cn: [
35264                 {
35265                     tag: 'div',
35266                     cls: 'masonry-brick-mask'
35267                 },
35268                 {
35269                     tag: 'div',
35270                     cls: 'masonry-brick-paragraph',
35271                     cn: []
35272                 }
35273             ]
35274         };
35275         
35276         if(this.href.length){
35277             cfg.href = this.href;
35278         }
35279         
35280         var cn = cfg.cn[1].cn;
35281         
35282         if(this.title.length){
35283             cn.push({
35284                 tag: 'h4',
35285                 cls: 'masonry-brick-title',
35286                 html: this.title
35287             });
35288         }
35289         
35290         if(this.html.length){
35291             cn.push({
35292                 tag: 'p',
35293                 cls: 'masonry-brick-text',
35294                 html: this.html
35295             });
35296         }
35297         
35298         if (!this.title.length && !this.html.length) {
35299             cfg.cn[1].cls += ' hide';
35300         }
35301         
35302         if(this.bgimage.length){
35303             cfg.cn.push({
35304                 tag: 'img',
35305                 cls: 'masonry-brick-image-view',
35306                 src: this.bgimage
35307             });
35308         }
35309         
35310         if(this.videourl.length){
35311             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35312             // youtube support only?
35313             cfg.cn.push({
35314                 tag: 'iframe',
35315                 cls: 'masonry-brick-image-view',
35316                 src: vurl,
35317                 frameborder : 0,
35318                 allowfullscreen : true
35319             });
35320         }
35321         
35322         return cfg;
35323         
35324     },
35325     
35326     getSplitAutoCreate : function()
35327     {
35328         var cls = 'masonry-brick masonry-brick-split';
35329         
35330         if(this.href.length){
35331             cls += ' masonry-brick-link';
35332         }
35333         
35334         if(this.bgimage.length){
35335             cls += ' masonry-brick-image';
35336         }
35337         
35338         if(this.size){
35339             cls += ' masonry-' + this.size + '-brick';
35340         }
35341         
35342         switch (this.placetitle) {
35343             case 'center' :
35344                 cls += ' masonry-center-title';
35345                 break;
35346             case 'bottom' :
35347                 cls += ' masonry-bottom-title';
35348                 break;
35349             default:
35350                 if(!this.bgimage.length){
35351                     cls += ' masonry-center-title';
35352                 }
35353
35354                 if(this.bgimage.length){
35355                     cls += ' masonry-bottom-title';
35356                 }
35357                 break;
35358         }
35359         
35360         if(this.cls){
35361             cls += ' ' + this.cls;
35362         }
35363         
35364         var cfg = {
35365             tag: (this.href.length) ? 'a' : 'div',
35366             cls: cls,
35367             cn: [
35368                 {
35369                     tag: 'div',
35370                     cls: 'masonry-brick-split-head',
35371                     cn: [
35372                         {
35373                             tag: 'div',
35374                             cls: 'masonry-brick-paragraph',
35375                             cn: []
35376                         }
35377                     ]
35378                 },
35379                 {
35380                     tag: 'div',
35381                     cls: 'masonry-brick-split-body',
35382                     cn: []
35383                 }
35384             ]
35385         };
35386         
35387         if(this.href.length){
35388             cfg.href = this.href;
35389         }
35390         
35391         if(this.title.length){
35392             cfg.cn[0].cn[0].cn.push({
35393                 tag: 'h4',
35394                 cls: 'masonry-brick-title',
35395                 html: this.title
35396             });
35397         }
35398         
35399         if(this.html.length){
35400             cfg.cn[1].cn.push({
35401                 tag: 'p',
35402                 cls: 'masonry-brick-text',
35403                 html: this.html
35404             });
35405         }
35406
35407         if(this.bgimage.length){
35408             cfg.cn[0].cn.push({
35409                 tag: 'img',
35410                 cls: 'masonry-brick-image-view',
35411                 src: this.bgimage
35412             });
35413         }
35414         
35415         if(this.videourl.length){
35416             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35417             // youtube support only?
35418             cfg.cn[0].cn.cn.push({
35419                 tag: 'iframe',
35420                 cls: 'masonry-brick-image-view',
35421                 src: vurl,
35422                 frameborder : 0,
35423                 allowfullscreen : true
35424             });
35425         }
35426         
35427         return cfg;
35428     },
35429     
35430     initEvents: function() 
35431     {
35432         switch (this.size) {
35433             case 'xs' :
35434                 this.x = 1;
35435                 this.y = 1;
35436                 break;
35437             case 'sm' :
35438                 this.x = 2;
35439                 this.y = 2;
35440                 break;
35441             case 'md' :
35442             case 'md-left' :
35443             case 'md-right' :
35444                 this.x = 3;
35445                 this.y = 3;
35446                 break;
35447             case 'tall' :
35448                 this.x = 2;
35449                 this.y = 3;
35450                 break;
35451             case 'wide' :
35452                 this.x = 3;
35453                 this.y = 2;
35454                 break;
35455             case 'wide-thin' :
35456                 this.x = 3;
35457                 this.y = 1;
35458                 break;
35459                         
35460             default :
35461                 break;
35462         }
35463         
35464         if(Roo.isTouch){
35465             this.el.on('touchstart', this.onTouchStart, this);
35466             this.el.on('touchmove', this.onTouchMove, this);
35467             this.el.on('touchend', this.onTouchEnd, this);
35468             this.el.on('contextmenu', this.onContextMenu, this);
35469         } else {
35470             this.el.on('mouseenter'  ,this.enter, this);
35471             this.el.on('mouseleave', this.leave, this);
35472             this.el.on('click', this.onClick, this);
35473         }
35474         
35475         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35476             this.parent().bricks.push(this);   
35477         }
35478         
35479     },
35480     
35481     onClick: function(e, el)
35482     {
35483         var time = this.endTimer - this.startTimer;
35484         // Roo.log(e.preventDefault());
35485         if(Roo.isTouch){
35486             if(time > 1000){
35487                 e.preventDefault();
35488                 return;
35489             }
35490         }
35491         
35492         if(!this.preventDefault){
35493             return;
35494         }
35495         
35496         e.preventDefault();
35497         
35498         if (this.activeClass != '') {
35499             this.selectBrick();
35500         }
35501         
35502         this.fireEvent('click', this, e);
35503     },
35504     
35505     enter: function(e, el)
35506     {
35507         e.preventDefault();
35508         
35509         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35510             return;
35511         }
35512         
35513         if(this.bgimage.length && this.html.length){
35514             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35515         }
35516     },
35517     
35518     leave: function(e, el)
35519     {
35520         e.preventDefault();
35521         
35522         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35523             return;
35524         }
35525         
35526         if(this.bgimage.length && this.html.length){
35527             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35528         }
35529     },
35530     
35531     onTouchStart: function(e, el)
35532     {
35533 //        e.preventDefault();
35534         
35535         this.touchmoved = false;
35536         
35537         if(!this.isFitContainer){
35538             return;
35539         }
35540         
35541         if(!this.bgimage.length || !this.html.length){
35542             return;
35543         }
35544         
35545         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35546         
35547         this.timer = new Date().getTime();
35548         
35549     },
35550     
35551     onTouchMove: function(e, el)
35552     {
35553         this.touchmoved = true;
35554     },
35555     
35556     onContextMenu : function(e,el)
35557     {
35558         e.preventDefault();
35559         e.stopPropagation();
35560         return false;
35561     },
35562     
35563     onTouchEnd: function(e, el)
35564     {
35565 //        e.preventDefault();
35566         
35567         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35568         
35569             this.leave(e,el);
35570             
35571             return;
35572         }
35573         
35574         if(!this.bgimage.length || !this.html.length){
35575             
35576             if(this.href.length){
35577                 window.location.href = this.href;
35578             }
35579             
35580             return;
35581         }
35582         
35583         if(!this.isFitContainer){
35584             return;
35585         }
35586         
35587         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35588         
35589         window.location.href = this.href;
35590     },
35591     
35592     //selection on single brick only
35593     selectBrick : function() {
35594         
35595         if (!this.parentId) {
35596             return;
35597         }
35598         
35599         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35600         var index = m.selectedBrick.indexOf(this.id);
35601         
35602         if ( index > -1) {
35603             m.selectedBrick.splice(index,1);
35604             this.el.removeClass(this.activeClass);
35605             return;
35606         }
35607         
35608         for(var i = 0; i < m.selectedBrick.length; i++) {
35609             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35610             b.el.removeClass(b.activeClass);
35611         }
35612         
35613         m.selectedBrick = [];
35614         
35615         m.selectedBrick.push(this.id);
35616         this.el.addClass(this.activeClass);
35617         return;
35618     },
35619     
35620     isSelected : function(){
35621         return this.el.hasClass(this.activeClass);
35622         
35623     }
35624 });
35625
35626 Roo.apply(Roo.bootstrap.MasonryBrick, {
35627     
35628     //groups: {},
35629     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35630      /**
35631     * register a Masonry Brick
35632     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35633     */
35634     
35635     register : function(brick)
35636     {
35637         //this.groups[brick.id] = brick;
35638         this.groups.add(brick.id, brick);
35639     },
35640     /**
35641     * fetch a  masonry brick based on the masonry brick ID
35642     * @param {string} the masonry brick to add
35643     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35644     */
35645     
35646     get: function(brick_id) 
35647     {
35648         // if (typeof(this.groups[brick_id]) == 'undefined') {
35649         //     return false;
35650         // }
35651         // return this.groups[brick_id] ;
35652         
35653         if(this.groups.key(brick_id)) {
35654             return this.groups.key(brick_id);
35655         }
35656         
35657         return false;
35658     }
35659     
35660     
35661     
35662 });
35663
35664  /*
35665  * - LGPL
35666  *
35667  * element
35668  * 
35669  */
35670
35671 /**
35672  * @class Roo.bootstrap.Brick
35673  * @extends Roo.bootstrap.Component
35674  * Bootstrap Brick class
35675  * 
35676  * @constructor
35677  * Create a new Brick
35678  * @param {Object} config The config object
35679  */
35680
35681 Roo.bootstrap.Brick = function(config){
35682     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35683     
35684     this.addEvents({
35685         // raw events
35686         /**
35687          * @event click
35688          * When a Brick is click
35689          * @param {Roo.bootstrap.Brick} this
35690          * @param {Roo.EventObject} e
35691          */
35692         "click" : true
35693     });
35694 };
35695
35696 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35697     
35698     /**
35699      * @cfg {String} title
35700      */   
35701     title : '',
35702     /**
35703      * @cfg {String} html
35704      */   
35705     html : '',
35706     /**
35707      * @cfg {String} bgimage
35708      */   
35709     bgimage : '',
35710     /**
35711      * @cfg {String} cls
35712      */   
35713     cls : '',
35714     /**
35715      * @cfg {String} href
35716      */   
35717     href : '',
35718     /**
35719      * @cfg {String} video
35720      */   
35721     video : '',
35722     /**
35723      * @cfg {Boolean} square
35724      */   
35725     square : true,
35726     
35727     getAutoCreate : function()
35728     {
35729         var cls = 'roo-brick';
35730         
35731         if(this.href.length){
35732             cls += ' roo-brick-link';
35733         }
35734         
35735         if(this.bgimage.length){
35736             cls += ' roo-brick-image';
35737         }
35738         
35739         if(!this.html.length && !this.bgimage.length){
35740             cls += ' roo-brick-center-title';
35741         }
35742         
35743         if(!this.html.length && this.bgimage.length){
35744             cls += ' roo-brick-bottom-title';
35745         }
35746         
35747         if(this.cls){
35748             cls += ' ' + this.cls;
35749         }
35750         
35751         var cfg = {
35752             tag: (this.href.length) ? 'a' : 'div',
35753             cls: cls,
35754             cn: [
35755                 {
35756                     tag: 'div',
35757                     cls: 'roo-brick-paragraph',
35758                     cn: []
35759                 }
35760             ]
35761         };
35762         
35763         if(this.href.length){
35764             cfg.href = this.href;
35765         }
35766         
35767         var cn = cfg.cn[0].cn;
35768         
35769         if(this.title.length){
35770             cn.push({
35771                 tag: 'h4',
35772                 cls: 'roo-brick-title',
35773                 html: this.title
35774             });
35775         }
35776         
35777         if(this.html.length){
35778             cn.push({
35779                 tag: 'p',
35780                 cls: 'roo-brick-text',
35781                 html: this.html
35782             });
35783         } else {
35784             cn.cls += ' hide';
35785         }
35786         
35787         if(this.bgimage.length){
35788             cfg.cn.push({
35789                 tag: 'img',
35790                 cls: 'roo-brick-image-view',
35791                 src: this.bgimage
35792             });
35793         }
35794         
35795         return cfg;
35796     },
35797     
35798     initEvents: function() 
35799     {
35800         if(this.title.length || this.html.length){
35801             this.el.on('mouseenter'  ,this.enter, this);
35802             this.el.on('mouseleave', this.leave, this);
35803         }
35804         
35805         Roo.EventManager.onWindowResize(this.resize, this); 
35806         
35807         if(this.bgimage.length){
35808             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35809             this.imageEl.on('load', this.onImageLoad, this);
35810             return;
35811         }
35812         
35813         this.resize();
35814     },
35815     
35816     onImageLoad : function()
35817     {
35818         this.resize();
35819     },
35820     
35821     resize : function()
35822     {
35823         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35824         
35825         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35826         
35827         if(this.bgimage.length){
35828             var image = this.el.select('.roo-brick-image-view', true).first();
35829             
35830             image.setWidth(paragraph.getWidth());
35831             
35832             if(this.square){
35833                 image.setHeight(paragraph.getWidth());
35834             }
35835             
35836             this.el.setHeight(image.getHeight());
35837             paragraph.setHeight(image.getHeight());
35838             
35839         }
35840         
35841     },
35842     
35843     enter: function(e, el)
35844     {
35845         e.preventDefault();
35846         
35847         if(this.bgimage.length){
35848             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35849             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35850         }
35851     },
35852     
35853     leave: function(e, el)
35854     {
35855         e.preventDefault();
35856         
35857         if(this.bgimage.length){
35858             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35859             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35860         }
35861     }
35862     
35863 });
35864
35865  
35866
35867  /*
35868  * - LGPL
35869  *
35870  * Number field 
35871  */
35872
35873 /**
35874  * @class Roo.bootstrap.NumberField
35875  * @extends Roo.bootstrap.Input
35876  * Bootstrap NumberField class
35877  * 
35878  * 
35879  * 
35880  * 
35881  * @constructor
35882  * Create a new NumberField
35883  * @param {Object} config The config object
35884  */
35885
35886 Roo.bootstrap.NumberField = function(config){
35887     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35888 };
35889
35890 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35891     
35892     /**
35893      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35894      */
35895     allowDecimals : true,
35896     /**
35897      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35898      */
35899     decimalSeparator : ".",
35900     /**
35901      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35902      */
35903     decimalPrecision : 2,
35904     /**
35905      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35906      */
35907     allowNegative : true,
35908     
35909     /**
35910      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35911      */
35912     allowZero: true,
35913     /**
35914      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35915      */
35916     minValue : Number.NEGATIVE_INFINITY,
35917     /**
35918      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35919      */
35920     maxValue : Number.MAX_VALUE,
35921     /**
35922      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35923      */
35924     minText : "The minimum value for this field is {0}",
35925     /**
35926      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35927      */
35928     maxText : "The maximum value for this field is {0}",
35929     /**
35930      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35931      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35932      */
35933     nanText : "{0} is not a valid number",
35934     /**
35935      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35936      */
35937     thousandsDelimiter : false,
35938     /**
35939      * @cfg {String} valueAlign alignment of value
35940      */
35941     valueAlign : "left",
35942
35943     getAutoCreate : function()
35944     {
35945         var hiddenInput = {
35946             tag: 'input',
35947             type: 'hidden',
35948             id: Roo.id(),
35949             cls: 'hidden-number-input'
35950         };
35951         
35952         if (this.name) {
35953             hiddenInput.name = this.name;
35954         }
35955         
35956         this.name = '';
35957         
35958         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35959         
35960         this.name = hiddenInput.name;
35961         
35962         if(cfg.cn.length > 0) {
35963             cfg.cn.push(hiddenInput);
35964         }
35965         
35966         return cfg;
35967     },
35968
35969     // private
35970     initEvents : function()
35971     {   
35972         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35973         
35974         var allowed = "0123456789";
35975         
35976         if(this.allowDecimals){
35977             allowed += this.decimalSeparator;
35978         }
35979         
35980         if(this.allowNegative){
35981             allowed += "-";
35982         }
35983         
35984         if(this.thousandsDelimiter) {
35985             allowed += ",";
35986         }
35987         
35988         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35989         
35990         var keyPress = function(e){
35991             
35992             var k = e.getKey();
35993             
35994             var c = e.getCharCode();
35995             
35996             if(
35997                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35998                     allowed.indexOf(String.fromCharCode(c)) === -1
35999             ){
36000                 e.stopEvent();
36001                 return;
36002             }
36003             
36004             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36005                 return;
36006             }
36007             
36008             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36009                 e.stopEvent();
36010             }
36011         };
36012         
36013         this.el.on("keypress", keyPress, this);
36014     },
36015     
36016     validateValue : function(value)
36017     {
36018         
36019         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36020             return false;
36021         }
36022         
36023         var num = this.parseValue(value);
36024         
36025         if(isNaN(num)){
36026             this.markInvalid(String.format(this.nanText, value));
36027             return false;
36028         }
36029         
36030         if(num < this.minValue){
36031             this.markInvalid(String.format(this.minText, this.minValue));
36032             return false;
36033         }
36034         
36035         if(num > this.maxValue){
36036             this.markInvalid(String.format(this.maxText, this.maxValue));
36037             return false;
36038         }
36039         
36040         return true;
36041     },
36042
36043     getValue : function()
36044     {
36045         var v = this.hiddenEl().getValue();
36046         
36047         return this.fixPrecision(this.parseValue(v));
36048     },
36049
36050     parseValue : function(value)
36051     {
36052         if(this.thousandsDelimiter) {
36053             value += "";
36054             r = new RegExp(",", "g");
36055             value = value.replace(r, "");
36056         }
36057         
36058         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36059         return isNaN(value) ? '' : value;
36060     },
36061
36062     fixPrecision : function(value)
36063     {
36064         if(this.thousandsDelimiter) {
36065             value += "";
36066             r = new RegExp(",", "g");
36067             value = value.replace(r, "");
36068         }
36069         
36070         var nan = isNaN(value);
36071         
36072         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36073             return nan ? '' : value;
36074         }
36075         return parseFloat(value).toFixed(this.decimalPrecision);
36076     },
36077
36078     setValue : function(v)
36079     {
36080         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36081         
36082         this.value = v;
36083         
36084         if(this.rendered){
36085             
36086             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36087             
36088             this.inputEl().dom.value = (v == '') ? '' :
36089                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36090             
36091             if(!this.allowZero && v === '0') {
36092                 this.hiddenEl().dom.value = '';
36093                 this.inputEl().dom.value = '';
36094             }
36095             
36096             this.validate();
36097         }
36098     },
36099
36100     decimalPrecisionFcn : function(v)
36101     {
36102         return Math.floor(v);
36103     },
36104
36105     beforeBlur : function()
36106     {
36107         var v = this.parseValue(this.getRawValue());
36108         
36109         if(v || v === 0 || v === ''){
36110             this.setValue(v);
36111         }
36112     },
36113     
36114     hiddenEl : function()
36115     {
36116         return this.el.select('input.hidden-number-input',true).first();
36117     }
36118     
36119 });
36120
36121  
36122
36123 /*
36124 * Licence: LGPL
36125 */
36126
36127 /**
36128  * @class Roo.bootstrap.DocumentSlider
36129  * @extends Roo.bootstrap.Component
36130  * Bootstrap DocumentSlider class
36131  * 
36132  * @constructor
36133  * Create a new DocumentViewer
36134  * @param {Object} config The config object
36135  */
36136
36137 Roo.bootstrap.DocumentSlider = function(config){
36138     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36139     
36140     this.files = [];
36141     
36142     this.addEvents({
36143         /**
36144          * @event initial
36145          * Fire after initEvent
36146          * @param {Roo.bootstrap.DocumentSlider} this
36147          */
36148         "initial" : true,
36149         /**
36150          * @event update
36151          * Fire after update
36152          * @param {Roo.bootstrap.DocumentSlider} this
36153          */
36154         "update" : true,
36155         /**
36156          * @event click
36157          * Fire after click
36158          * @param {Roo.bootstrap.DocumentSlider} this
36159          */
36160         "click" : true
36161     });
36162 };
36163
36164 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36165     
36166     files : false,
36167     
36168     indicator : 0,
36169     
36170     getAutoCreate : function()
36171     {
36172         var cfg = {
36173             tag : 'div',
36174             cls : 'roo-document-slider',
36175             cn : [
36176                 {
36177                     tag : 'div',
36178                     cls : 'roo-document-slider-header',
36179                     cn : [
36180                         {
36181                             tag : 'div',
36182                             cls : 'roo-document-slider-header-title'
36183                         }
36184                     ]
36185                 },
36186                 {
36187                     tag : 'div',
36188                     cls : 'roo-document-slider-body',
36189                     cn : [
36190                         {
36191                             tag : 'div',
36192                             cls : 'roo-document-slider-prev',
36193                             cn : [
36194                                 {
36195                                     tag : 'i',
36196                                     cls : 'fa fa-chevron-left'
36197                                 }
36198                             ]
36199                         },
36200                         {
36201                             tag : 'div',
36202                             cls : 'roo-document-slider-thumb',
36203                             cn : [
36204                                 {
36205                                     tag : 'img',
36206                                     cls : 'roo-document-slider-image'
36207                                 }
36208                             ]
36209                         },
36210                         {
36211                             tag : 'div',
36212                             cls : 'roo-document-slider-next',
36213                             cn : [
36214                                 {
36215                                     tag : 'i',
36216                                     cls : 'fa fa-chevron-right'
36217                                 }
36218                             ]
36219                         }
36220                     ]
36221                 }
36222             ]
36223         };
36224         
36225         return cfg;
36226     },
36227     
36228     initEvents : function()
36229     {
36230         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36231         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36232         
36233         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36234         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36235         
36236         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36237         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36238         
36239         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36240         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36241         
36242         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36243         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36244         
36245         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36246         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36247         
36248         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36249         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36250         
36251         this.thumbEl.on('click', this.onClick, this);
36252         
36253         this.prevIndicator.on('click', this.prev, this);
36254         
36255         this.nextIndicator.on('click', this.next, this);
36256         
36257     },
36258     
36259     initial : function()
36260     {
36261         if(this.files.length){
36262             this.indicator = 1;
36263             this.update()
36264         }
36265         
36266         this.fireEvent('initial', this);
36267     },
36268     
36269     update : function()
36270     {
36271         this.imageEl.attr('src', this.files[this.indicator - 1]);
36272         
36273         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36274         
36275         this.prevIndicator.show();
36276         
36277         if(this.indicator == 1){
36278             this.prevIndicator.hide();
36279         }
36280         
36281         this.nextIndicator.show();
36282         
36283         if(this.indicator == this.files.length){
36284             this.nextIndicator.hide();
36285         }
36286         
36287         this.thumbEl.scrollTo('top');
36288         
36289         this.fireEvent('update', this);
36290     },
36291     
36292     onClick : function(e)
36293     {
36294         e.preventDefault();
36295         
36296         this.fireEvent('click', this);
36297     },
36298     
36299     prev : function(e)
36300     {
36301         e.preventDefault();
36302         
36303         this.indicator = Math.max(1, this.indicator - 1);
36304         
36305         this.update();
36306     },
36307     
36308     next : function(e)
36309     {
36310         e.preventDefault();
36311         
36312         this.indicator = Math.min(this.files.length, this.indicator + 1);
36313         
36314         this.update();
36315     }
36316 });
36317 /*
36318  * - LGPL
36319  *
36320  * RadioSet
36321  *
36322  *
36323  */
36324
36325 /**
36326  * @class Roo.bootstrap.RadioSet
36327  * @extends Roo.bootstrap.Input
36328  * Bootstrap RadioSet class
36329  * @cfg {String} indicatorpos (left|right) default left
36330  * @cfg {Boolean} inline (true|false) inline the element (default true)
36331  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36332  * @constructor
36333  * Create a new RadioSet
36334  * @param {Object} config The config object
36335  */
36336
36337 Roo.bootstrap.RadioSet = function(config){
36338     
36339     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36340     
36341     this.radioes = [];
36342     
36343     Roo.bootstrap.RadioSet.register(this);
36344     
36345     this.addEvents({
36346         /**
36347         * @event check
36348         * Fires when the element is checked or unchecked.
36349         * @param {Roo.bootstrap.RadioSet} this This radio
36350         * @param {Roo.bootstrap.Radio} item The checked item
36351         */
36352        check : true,
36353        /**
36354         * @event click
36355         * Fires when the element is click.
36356         * @param {Roo.bootstrap.RadioSet} this This radio set
36357         * @param {Roo.bootstrap.Radio} item The checked item
36358         * @param {Roo.EventObject} e The event object
36359         */
36360        click : true
36361     });
36362     
36363 };
36364
36365 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36366
36367     radioes : false,
36368     
36369     inline : true,
36370     
36371     weight : '',
36372     
36373     indicatorpos : 'left',
36374     
36375     getAutoCreate : function()
36376     {
36377         var label = {
36378             tag : 'label',
36379             cls : 'roo-radio-set-label',
36380             cn : [
36381                 {
36382                     tag : 'span',
36383                     html : this.fieldLabel
36384                 }
36385             ]
36386         };
36387         if (Roo.bootstrap.version == 3) {
36388             
36389             
36390             if(this.indicatorpos == 'left'){
36391                 label.cn.unshift({
36392                     tag : 'i',
36393                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36394                     tooltip : 'This field is required'
36395                 });
36396             } else {
36397                 label.cn.push({
36398                     tag : 'i',
36399                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36400                     tooltip : 'This field is required'
36401                 });
36402             }
36403         }
36404         var items = {
36405             tag : 'div',
36406             cls : 'roo-radio-set-items'
36407         };
36408         
36409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36410         
36411         if (align === 'left' && this.fieldLabel.length) {
36412             
36413             items = {
36414                 cls : "roo-radio-set-right", 
36415                 cn: [
36416                     items
36417                 ]
36418             };
36419             
36420             if(this.labelWidth > 12){
36421                 label.style = "width: " + this.labelWidth + 'px';
36422             }
36423             
36424             if(this.labelWidth < 13 && this.labelmd == 0){
36425                 this.labelmd = this.labelWidth;
36426             }
36427             
36428             if(this.labellg > 0){
36429                 label.cls += ' col-lg-' + this.labellg;
36430                 items.cls += ' col-lg-' + (12 - this.labellg);
36431             }
36432             
36433             if(this.labelmd > 0){
36434                 label.cls += ' col-md-' + this.labelmd;
36435                 items.cls += ' col-md-' + (12 - this.labelmd);
36436             }
36437             
36438             if(this.labelsm > 0){
36439                 label.cls += ' col-sm-' + this.labelsm;
36440                 items.cls += ' col-sm-' + (12 - this.labelsm);
36441             }
36442             
36443             if(this.labelxs > 0){
36444                 label.cls += ' col-xs-' + this.labelxs;
36445                 items.cls += ' col-xs-' + (12 - this.labelxs);
36446             }
36447         }
36448         
36449         var cfg = {
36450             tag : 'div',
36451             cls : 'roo-radio-set',
36452             cn : [
36453                 {
36454                     tag : 'input',
36455                     cls : 'roo-radio-set-input',
36456                     type : 'hidden',
36457                     name : this.name,
36458                     value : this.value ? this.value :  ''
36459                 },
36460                 label,
36461                 items
36462             ]
36463         };
36464         
36465         if(this.weight.length){
36466             cfg.cls += ' roo-radio-' + this.weight;
36467         }
36468         
36469         if(this.inline) {
36470             cfg.cls += ' roo-radio-set-inline';
36471         }
36472         
36473         var settings=this;
36474         ['xs','sm','md','lg'].map(function(size){
36475             if (settings[size]) {
36476                 cfg.cls += ' col-' + size + '-' + settings[size];
36477             }
36478         });
36479         
36480         return cfg;
36481         
36482     },
36483
36484     initEvents : function()
36485     {
36486         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36487         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36488         
36489         if(!this.fieldLabel.length){
36490             this.labelEl.hide();
36491         }
36492         
36493         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36494         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36495         
36496         this.indicator = this.indicatorEl();
36497         
36498         if(this.indicator){
36499             this.indicator.addClass('invisible');
36500         }
36501         
36502         this.originalValue = this.getValue();
36503         
36504     },
36505     
36506     inputEl: function ()
36507     {
36508         return this.el.select('.roo-radio-set-input', true).first();
36509     },
36510     
36511     getChildContainer : function()
36512     {
36513         return this.itemsEl;
36514     },
36515     
36516     register : function(item)
36517     {
36518         this.radioes.push(item);
36519         
36520     },
36521     
36522     validate : function()
36523     {   
36524         if(this.getVisibilityEl().hasClass('hidden')){
36525             return true;
36526         }
36527         
36528         var valid = false;
36529         
36530         Roo.each(this.radioes, function(i){
36531             if(!i.checked){
36532                 return;
36533             }
36534             
36535             valid = true;
36536             return false;
36537         });
36538         
36539         if(this.allowBlank) {
36540             return true;
36541         }
36542         
36543         if(this.disabled || valid){
36544             this.markValid();
36545             return true;
36546         }
36547         
36548         this.markInvalid();
36549         return false;
36550         
36551     },
36552     
36553     markValid : function()
36554     {
36555         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36556             this.indicatorEl().removeClass('visible');
36557             this.indicatorEl().addClass('invisible');
36558         }
36559         
36560         
36561         if (Roo.bootstrap.version == 3) {
36562             this.el.removeClass([this.invalidClass, this.validClass]);
36563             this.el.addClass(this.validClass);
36564         } else {
36565             this.el.removeClass(['is-invalid','is-valid']);
36566             this.el.addClass(['is-valid']);
36567         }
36568         this.fireEvent('valid', this);
36569     },
36570     
36571     markInvalid : function(msg)
36572     {
36573         if(this.allowBlank || this.disabled){
36574             return;
36575         }
36576         
36577         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36578             this.indicatorEl().removeClass('invisible');
36579             this.indicatorEl().addClass('visible');
36580         }
36581         if (Roo.bootstrap.version == 3) {
36582             this.el.removeClass([this.invalidClass, this.validClass]);
36583             this.el.addClass(this.invalidClass);
36584         } else {
36585             this.el.removeClass(['is-invalid','is-valid']);
36586             this.el.addClass(['is-invalid']);
36587         }
36588         
36589         this.fireEvent('invalid', this, msg);
36590         
36591     },
36592     
36593     setValue : function(v, suppressEvent)
36594     {   
36595         if(this.value === v){
36596             return;
36597         }
36598         
36599         this.value = v;
36600         
36601         if(this.rendered){
36602             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36603         }
36604         
36605         Roo.each(this.radioes, function(i){
36606             i.checked = false;
36607             i.el.removeClass('checked');
36608         });
36609         
36610         Roo.each(this.radioes, function(i){
36611             
36612             if(i.value === v || i.value.toString() === v.toString()){
36613                 i.checked = true;
36614                 i.el.addClass('checked');
36615                 
36616                 if(suppressEvent !== true){
36617                     this.fireEvent('check', this, i);
36618                 }
36619                 
36620                 return false;
36621             }
36622             
36623         }, this);
36624         
36625         this.validate();
36626     },
36627     
36628     clearInvalid : function(){
36629         
36630         if(!this.el || this.preventMark){
36631             return;
36632         }
36633         
36634         this.el.removeClass([this.invalidClass]);
36635         
36636         this.fireEvent('valid', this);
36637     }
36638     
36639 });
36640
36641 Roo.apply(Roo.bootstrap.RadioSet, {
36642     
36643     groups: {},
36644     
36645     register : function(set)
36646     {
36647         this.groups[set.name] = set;
36648     },
36649     
36650     get: function(name) 
36651     {
36652         if (typeof(this.groups[name]) == 'undefined') {
36653             return false;
36654         }
36655         
36656         return this.groups[name] ;
36657     }
36658     
36659 });
36660 /*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670
36671
36672 /**
36673  * @class Roo.bootstrap.SplitBar
36674  * @extends Roo.util.Observable
36675  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36676  * <br><br>
36677  * Usage:
36678  * <pre><code>
36679 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36680                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36681 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36682 split.minSize = 100;
36683 split.maxSize = 600;
36684 split.animate = true;
36685 split.on('moved', splitterMoved);
36686 </code></pre>
36687  * @constructor
36688  * Create a new SplitBar
36689  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36690  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36691  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36692  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36693                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36694                         position of the SplitBar).
36695  */
36696 Roo.bootstrap.SplitBar = function(cfg){
36697     
36698     /** @private */
36699     
36700     //{
36701     //  dragElement : elm
36702     //  resizingElement: el,
36703         // optional..
36704     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36705     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36706         // existingProxy ???
36707     //}
36708     
36709     this.el = Roo.get(cfg.dragElement, true);
36710     this.el.dom.unselectable = "on";
36711     /** @private */
36712     this.resizingEl = Roo.get(cfg.resizingElement, true);
36713
36714     /**
36715      * @private
36716      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36717      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36718      * @type Number
36719      */
36720     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36721     
36722     /**
36723      * The minimum size of the resizing element. (Defaults to 0)
36724      * @type Number
36725      */
36726     this.minSize = 0;
36727     
36728     /**
36729      * The maximum size of the resizing element. (Defaults to 2000)
36730      * @type Number
36731      */
36732     this.maxSize = 2000;
36733     
36734     /**
36735      * Whether to animate the transition to the new size
36736      * @type Boolean
36737      */
36738     this.animate = false;
36739     
36740     /**
36741      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36742      * @type Boolean
36743      */
36744     this.useShim = false;
36745     
36746     /** @private */
36747     this.shim = null;
36748     
36749     if(!cfg.existingProxy){
36750         /** @private */
36751         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36752     }else{
36753         this.proxy = Roo.get(cfg.existingProxy).dom;
36754     }
36755     /** @private */
36756     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36757     
36758     /** @private */
36759     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36760     
36761     /** @private */
36762     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36763     
36764     /** @private */
36765     this.dragSpecs = {};
36766     
36767     /**
36768      * @private The adapter to use to positon and resize elements
36769      */
36770     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36771     this.adapter.init(this);
36772     
36773     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36774         /** @private */
36775         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36776         this.el.addClass("roo-splitbar-h");
36777     }else{
36778         /** @private */
36779         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36780         this.el.addClass("roo-splitbar-v");
36781     }
36782     
36783     this.addEvents({
36784         /**
36785          * @event resize
36786          * Fires when the splitter is moved (alias for {@link #event-moved})
36787          * @param {Roo.bootstrap.SplitBar} this
36788          * @param {Number} newSize the new width or height
36789          */
36790         "resize" : true,
36791         /**
36792          * @event moved
36793          * Fires when the splitter is moved
36794          * @param {Roo.bootstrap.SplitBar} this
36795          * @param {Number} newSize the new width or height
36796          */
36797         "moved" : true,
36798         /**
36799          * @event beforeresize
36800          * Fires before the splitter is dragged
36801          * @param {Roo.bootstrap.SplitBar} this
36802          */
36803         "beforeresize" : true,
36804
36805         "beforeapply" : true
36806     });
36807
36808     Roo.util.Observable.call(this);
36809 };
36810
36811 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36812     onStartProxyDrag : function(x, y){
36813         this.fireEvent("beforeresize", this);
36814         if(!this.overlay){
36815             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36816             o.unselectable();
36817             o.enableDisplayMode("block");
36818             // all splitbars share the same overlay
36819             Roo.bootstrap.SplitBar.prototype.overlay = o;
36820         }
36821         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36822         this.overlay.show();
36823         Roo.get(this.proxy).setDisplayed("block");
36824         var size = this.adapter.getElementSize(this);
36825         this.activeMinSize = this.getMinimumSize();;
36826         this.activeMaxSize = this.getMaximumSize();;
36827         var c1 = size - this.activeMinSize;
36828         var c2 = Math.max(this.activeMaxSize - size, 0);
36829         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36830             this.dd.resetConstraints();
36831             this.dd.setXConstraint(
36832                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36833                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36834             );
36835             this.dd.setYConstraint(0, 0);
36836         }else{
36837             this.dd.resetConstraints();
36838             this.dd.setXConstraint(0, 0);
36839             this.dd.setYConstraint(
36840                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36841                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36842             );
36843          }
36844         this.dragSpecs.startSize = size;
36845         this.dragSpecs.startPoint = [x, y];
36846         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36847     },
36848     
36849     /** 
36850      * @private Called after the drag operation by the DDProxy
36851      */
36852     onEndProxyDrag : function(e){
36853         Roo.get(this.proxy).setDisplayed(false);
36854         var endPoint = Roo.lib.Event.getXY(e);
36855         if(this.overlay){
36856             this.overlay.hide();
36857         }
36858         var newSize;
36859         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36860             newSize = this.dragSpecs.startSize + 
36861                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36862                     endPoint[0] - this.dragSpecs.startPoint[0] :
36863                     this.dragSpecs.startPoint[0] - endPoint[0]
36864                 );
36865         }else{
36866             newSize = this.dragSpecs.startSize + 
36867                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36868                     endPoint[1] - this.dragSpecs.startPoint[1] :
36869                     this.dragSpecs.startPoint[1] - endPoint[1]
36870                 );
36871         }
36872         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36873         if(newSize != this.dragSpecs.startSize){
36874             if(this.fireEvent('beforeapply', this, newSize) !== false){
36875                 this.adapter.setElementSize(this, newSize);
36876                 this.fireEvent("moved", this, newSize);
36877                 this.fireEvent("resize", this, newSize);
36878             }
36879         }
36880     },
36881     
36882     /**
36883      * Get the adapter this SplitBar uses
36884      * @return The adapter object
36885      */
36886     getAdapter : function(){
36887         return this.adapter;
36888     },
36889     
36890     /**
36891      * Set the adapter this SplitBar uses
36892      * @param {Object} adapter A SplitBar adapter object
36893      */
36894     setAdapter : function(adapter){
36895         this.adapter = adapter;
36896         this.adapter.init(this);
36897     },
36898     
36899     /**
36900      * Gets the minimum size for the resizing element
36901      * @return {Number} The minimum size
36902      */
36903     getMinimumSize : function(){
36904         return this.minSize;
36905     },
36906     
36907     /**
36908      * Sets the minimum size for the resizing element
36909      * @param {Number} minSize The minimum size
36910      */
36911     setMinimumSize : function(minSize){
36912         this.minSize = minSize;
36913     },
36914     
36915     /**
36916      * Gets the maximum size for the resizing element
36917      * @return {Number} The maximum size
36918      */
36919     getMaximumSize : function(){
36920         return this.maxSize;
36921     },
36922     
36923     /**
36924      * Sets the maximum size for the resizing element
36925      * @param {Number} maxSize The maximum size
36926      */
36927     setMaximumSize : function(maxSize){
36928         this.maxSize = maxSize;
36929     },
36930     
36931     /**
36932      * Sets the initialize size for the resizing element
36933      * @param {Number} size The initial size
36934      */
36935     setCurrentSize : function(size){
36936         var oldAnimate = this.animate;
36937         this.animate = false;
36938         this.adapter.setElementSize(this, size);
36939         this.animate = oldAnimate;
36940     },
36941     
36942     /**
36943      * Destroy this splitbar. 
36944      * @param {Boolean} removeEl True to remove the element
36945      */
36946     destroy : function(removeEl){
36947         if(this.shim){
36948             this.shim.remove();
36949         }
36950         this.dd.unreg();
36951         this.proxy.parentNode.removeChild(this.proxy);
36952         if(removeEl){
36953             this.el.remove();
36954         }
36955     }
36956 });
36957
36958 /**
36959  * @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.
36960  */
36961 Roo.bootstrap.SplitBar.createProxy = function(dir){
36962     var proxy = new Roo.Element(document.createElement("div"));
36963     proxy.unselectable();
36964     var cls = 'roo-splitbar-proxy';
36965     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36966     document.body.appendChild(proxy.dom);
36967     return proxy.dom;
36968 };
36969
36970 /** 
36971  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36972  * Default Adapter. It assumes the splitter and resizing element are not positioned
36973  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36974  */
36975 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36976 };
36977
36978 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36979     // do nothing for now
36980     init : function(s){
36981     
36982     },
36983     /**
36984      * Called before drag operations to get the current size of the resizing element. 
36985      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36986      */
36987      getElementSize : function(s){
36988         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36989             return s.resizingEl.getWidth();
36990         }else{
36991             return s.resizingEl.getHeight();
36992         }
36993     },
36994     
36995     /**
36996      * Called after drag operations to set the size of the resizing element.
36997      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36998      * @param {Number} newSize The new size to set
36999      * @param {Function} onComplete A function to be invoked when resizing is complete
37000      */
37001     setElementSize : function(s, newSize, onComplete){
37002         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37003             if(!s.animate){
37004                 s.resizingEl.setWidth(newSize);
37005                 if(onComplete){
37006                     onComplete(s, newSize);
37007                 }
37008             }else{
37009                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37010             }
37011         }else{
37012             
37013             if(!s.animate){
37014                 s.resizingEl.setHeight(newSize);
37015                 if(onComplete){
37016                     onComplete(s, newSize);
37017                 }
37018             }else{
37019                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37020             }
37021         }
37022     }
37023 };
37024
37025 /** 
37026  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37027  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37028  * Adapter that  moves the splitter element to align with the resized sizing element. 
37029  * Used with an absolute positioned SplitBar.
37030  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37031  * document.body, make sure you assign an id to the body element.
37032  */
37033 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37034     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37035     this.container = Roo.get(container);
37036 };
37037
37038 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37039     init : function(s){
37040         this.basic.init(s);
37041     },
37042     
37043     getElementSize : function(s){
37044         return this.basic.getElementSize(s);
37045     },
37046     
37047     setElementSize : function(s, newSize, onComplete){
37048         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37049     },
37050     
37051     moveSplitter : function(s){
37052         var yes = Roo.bootstrap.SplitBar;
37053         switch(s.placement){
37054             case yes.LEFT:
37055                 s.el.setX(s.resizingEl.getRight());
37056                 break;
37057             case yes.RIGHT:
37058                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37059                 break;
37060             case yes.TOP:
37061                 s.el.setY(s.resizingEl.getBottom());
37062                 break;
37063             case yes.BOTTOM:
37064                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37065                 break;
37066         }
37067     }
37068 };
37069
37070 /**
37071  * Orientation constant - Create a vertical SplitBar
37072  * @static
37073  * @type Number
37074  */
37075 Roo.bootstrap.SplitBar.VERTICAL = 1;
37076
37077 /**
37078  * Orientation constant - Create a horizontal SplitBar
37079  * @static
37080  * @type Number
37081  */
37082 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37083
37084 /**
37085  * Placement constant - The resizing element is to the left of the splitter element
37086  * @static
37087  * @type Number
37088  */
37089 Roo.bootstrap.SplitBar.LEFT = 1;
37090
37091 /**
37092  * Placement constant - The resizing element is to the right of the splitter element
37093  * @static
37094  * @type Number
37095  */
37096 Roo.bootstrap.SplitBar.RIGHT = 2;
37097
37098 /**
37099  * Placement constant - The resizing element is positioned above the splitter element
37100  * @static
37101  * @type Number
37102  */
37103 Roo.bootstrap.SplitBar.TOP = 3;
37104
37105 /**
37106  * Placement constant - The resizing element is positioned under splitter element
37107  * @static
37108  * @type Number
37109  */
37110 Roo.bootstrap.SplitBar.BOTTOM = 4;
37111 Roo.namespace("Roo.bootstrap.layout");/*
37112  * Based on:
37113  * Ext JS Library 1.1.1
37114  * Copyright(c) 2006-2007, Ext JS, LLC.
37115  *
37116  * Originally Released Under LGPL - original licence link has changed is not relivant.
37117  *
37118  * Fork - LGPL
37119  * <script type="text/javascript">
37120  */
37121
37122 /**
37123  * @class Roo.bootstrap.layout.Manager
37124  * @extends Roo.bootstrap.Component
37125  * Base class for layout managers.
37126  */
37127 Roo.bootstrap.layout.Manager = function(config)
37128 {
37129     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37130
37131
37132
37133
37134
37135     /** false to disable window resize monitoring @type Boolean */
37136     this.monitorWindowResize = true;
37137     this.regions = {};
37138     this.addEvents({
37139         /**
37140          * @event layout
37141          * Fires when a layout is performed.
37142          * @param {Roo.LayoutManager} this
37143          */
37144         "layout" : true,
37145         /**
37146          * @event regionresized
37147          * Fires when the user resizes a region.
37148          * @param {Roo.LayoutRegion} region The resized region
37149          * @param {Number} newSize The new size (width for east/west, height for north/south)
37150          */
37151         "regionresized" : true,
37152         /**
37153          * @event regioncollapsed
37154          * Fires when a region is collapsed.
37155          * @param {Roo.LayoutRegion} region The collapsed region
37156          */
37157         "regioncollapsed" : true,
37158         /**
37159          * @event regionexpanded
37160          * Fires when a region is expanded.
37161          * @param {Roo.LayoutRegion} region The expanded region
37162          */
37163         "regionexpanded" : true
37164     });
37165     this.updating = false;
37166
37167     if (config.el) {
37168         this.el = Roo.get(config.el);
37169         this.initEvents();
37170     }
37171
37172 };
37173
37174 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37175
37176
37177     regions : null,
37178
37179     monitorWindowResize : true,
37180
37181
37182     updating : false,
37183
37184
37185     onRender : function(ct, position)
37186     {
37187         if(!this.el){
37188             this.el = Roo.get(ct);
37189             this.initEvents();
37190         }
37191         //this.fireEvent('render',this);
37192     },
37193
37194
37195     initEvents: function()
37196     {
37197
37198
37199         // ie scrollbar fix
37200         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37201             document.body.scroll = "no";
37202         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37203             this.el.position('relative');
37204         }
37205         this.id = this.el.id;
37206         this.el.addClass("roo-layout-container");
37207         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37208         if(this.el.dom != document.body ) {
37209             this.el.on('resize', this.layout,this);
37210             this.el.on('show', this.layout,this);
37211         }
37212
37213     },
37214
37215     /**
37216      * Returns true if this layout is currently being updated
37217      * @return {Boolean}
37218      */
37219     isUpdating : function(){
37220         return this.updating;
37221     },
37222
37223     /**
37224      * Suspend the LayoutManager from doing auto-layouts while
37225      * making multiple add or remove calls
37226      */
37227     beginUpdate : function(){
37228         this.updating = true;
37229     },
37230
37231     /**
37232      * Restore auto-layouts and optionally disable the manager from performing a layout
37233      * @param {Boolean} noLayout true to disable a layout update
37234      */
37235     endUpdate : function(noLayout){
37236         this.updating = false;
37237         if(!noLayout){
37238             this.layout();
37239         }
37240     },
37241
37242     layout: function(){
37243         // abstract...
37244     },
37245
37246     onRegionResized : function(region, newSize){
37247         this.fireEvent("regionresized", region, newSize);
37248         this.layout();
37249     },
37250
37251     onRegionCollapsed : function(region){
37252         this.fireEvent("regioncollapsed", region);
37253     },
37254
37255     onRegionExpanded : function(region){
37256         this.fireEvent("regionexpanded", region);
37257     },
37258
37259     /**
37260      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37261      * performs box-model adjustments.
37262      * @return {Object} The size as an object {width: (the width), height: (the height)}
37263      */
37264     getViewSize : function()
37265     {
37266         var size;
37267         if(this.el.dom != document.body){
37268             size = this.el.getSize();
37269         }else{
37270             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37271         }
37272         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37273         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37274         return size;
37275     },
37276
37277     /**
37278      * Returns the Element this layout is bound to.
37279      * @return {Roo.Element}
37280      */
37281     getEl : function(){
37282         return this.el;
37283     },
37284
37285     /**
37286      * Returns the specified region.
37287      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37288      * @return {Roo.LayoutRegion}
37289      */
37290     getRegion : function(target){
37291         return this.regions[target.toLowerCase()];
37292     },
37293
37294     onWindowResize : function(){
37295         if(this.monitorWindowResize){
37296             this.layout();
37297         }
37298     }
37299 });
37300 /*
37301  * Based on:
37302  * Ext JS Library 1.1.1
37303  * Copyright(c) 2006-2007, Ext JS, LLC.
37304  *
37305  * Originally Released Under LGPL - original licence link has changed is not relivant.
37306  *
37307  * Fork - LGPL
37308  * <script type="text/javascript">
37309  */
37310 /**
37311  * @class Roo.bootstrap.layout.Border
37312  * @extends Roo.bootstrap.layout.Manager
37313  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37314  * please see: examples/bootstrap/nested.html<br><br>
37315  
37316 <b>The container the layout is rendered into can be either the body element or any other element.
37317 If it is not the body element, the container needs to either be an absolute positioned element,
37318 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37319 the container size if it is not the body element.</b>
37320
37321 * @constructor
37322 * Create a new Border
37323 * @param {Object} config Configuration options
37324  */
37325 Roo.bootstrap.layout.Border = function(config){
37326     config = config || {};
37327     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37328     
37329     
37330     
37331     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37332         if(config[region]){
37333             config[region].region = region;
37334             this.addRegion(config[region]);
37335         }
37336     },this);
37337     
37338 };
37339
37340 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37341
37342 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37343     
37344     parent : false, // this might point to a 'nest' or a ???
37345     
37346     /**
37347      * Creates and adds a new region if it doesn't already exist.
37348      * @param {String} target The target region key (north, south, east, west or center).
37349      * @param {Object} config The regions config object
37350      * @return {BorderLayoutRegion} The new region
37351      */
37352     addRegion : function(config)
37353     {
37354         if(!this.regions[config.region]){
37355             var r = this.factory(config);
37356             this.bindRegion(r);
37357         }
37358         return this.regions[config.region];
37359     },
37360
37361     // private (kinda)
37362     bindRegion : function(r){
37363         this.regions[r.config.region] = r;
37364         
37365         r.on("visibilitychange",    this.layout, this);
37366         r.on("paneladded",          this.layout, this);
37367         r.on("panelremoved",        this.layout, this);
37368         r.on("invalidated",         this.layout, this);
37369         r.on("resized",             this.onRegionResized, this);
37370         r.on("collapsed",           this.onRegionCollapsed, this);
37371         r.on("expanded",            this.onRegionExpanded, this);
37372     },
37373
37374     /**
37375      * Performs a layout update.
37376      */
37377     layout : function()
37378     {
37379         if(this.updating) {
37380             return;
37381         }
37382         
37383         // render all the rebions if they have not been done alreayd?
37384         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37385             if(this.regions[region] && !this.regions[region].bodyEl){
37386                 this.regions[region].onRender(this.el)
37387             }
37388         },this);
37389         
37390         var size = this.getViewSize();
37391         var w = size.width;
37392         var h = size.height;
37393         var centerW = w;
37394         var centerH = h;
37395         var centerY = 0;
37396         var centerX = 0;
37397         //var x = 0, y = 0;
37398
37399         var rs = this.regions;
37400         var north = rs["north"];
37401         var south = rs["south"]; 
37402         var west = rs["west"];
37403         var east = rs["east"];
37404         var center = rs["center"];
37405         //if(this.hideOnLayout){ // not supported anymore
37406             //c.el.setStyle("display", "none");
37407         //}
37408         if(north && north.isVisible()){
37409             var b = north.getBox();
37410             var m = north.getMargins();
37411             b.width = w - (m.left+m.right);
37412             b.x = m.left;
37413             b.y = m.top;
37414             centerY = b.height + b.y + m.bottom;
37415             centerH -= centerY;
37416             north.updateBox(this.safeBox(b));
37417         }
37418         if(south && south.isVisible()){
37419             var b = south.getBox();
37420             var m = south.getMargins();
37421             b.width = w - (m.left+m.right);
37422             b.x = m.left;
37423             var totalHeight = (b.height + m.top + m.bottom);
37424             b.y = h - totalHeight + m.top;
37425             centerH -= totalHeight;
37426             south.updateBox(this.safeBox(b));
37427         }
37428         if(west && west.isVisible()){
37429             var b = west.getBox();
37430             var m = west.getMargins();
37431             b.height = centerH - (m.top+m.bottom);
37432             b.x = m.left;
37433             b.y = centerY + m.top;
37434             var totalWidth = (b.width + m.left + m.right);
37435             centerX += totalWidth;
37436             centerW -= totalWidth;
37437             west.updateBox(this.safeBox(b));
37438         }
37439         if(east && east.isVisible()){
37440             var b = east.getBox();
37441             var m = east.getMargins();
37442             b.height = centerH - (m.top+m.bottom);
37443             var totalWidth = (b.width + m.left + m.right);
37444             b.x = w - totalWidth + m.left;
37445             b.y = centerY + m.top;
37446             centerW -= totalWidth;
37447             east.updateBox(this.safeBox(b));
37448         }
37449         if(center){
37450             var m = center.getMargins();
37451             var centerBox = {
37452                 x: centerX + m.left,
37453                 y: centerY + m.top,
37454                 width: centerW - (m.left+m.right),
37455                 height: centerH - (m.top+m.bottom)
37456             };
37457             //if(this.hideOnLayout){
37458                 //center.el.setStyle("display", "block");
37459             //}
37460             center.updateBox(this.safeBox(centerBox));
37461         }
37462         this.el.repaint();
37463         this.fireEvent("layout", this);
37464     },
37465
37466     // private
37467     safeBox : function(box){
37468         box.width = Math.max(0, box.width);
37469         box.height = Math.max(0, box.height);
37470         return box;
37471     },
37472
37473     /**
37474      * Adds a ContentPanel (or subclass) to this layout.
37475      * @param {String} target The target region key (north, south, east, west or center).
37476      * @param {Roo.ContentPanel} panel The panel to add
37477      * @return {Roo.ContentPanel} The added panel
37478      */
37479     add : function(target, panel){
37480          
37481         target = target.toLowerCase();
37482         return this.regions[target].add(panel);
37483     },
37484
37485     /**
37486      * Remove a ContentPanel (or subclass) to this layout.
37487      * @param {String} target The target region key (north, south, east, west or center).
37488      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37489      * @return {Roo.ContentPanel} The removed panel
37490      */
37491     remove : function(target, panel){
37492         target = target.toLowerCase();
37493         return this.regions[target].remove(panel);
37494     },
37495
37496     /**
37497      * Searches all regions for a panel with the specified id
37498      * @param {String} panelId
37499      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37500      */
37501     findPanel : function(panelId){
37502         var rs = this.regions;
37503         for(var target in rs){
37504             if(typeof rs[target] != "function"){
37505                 var p = rs[target].getPanel(panelId);
37506                 if(p){
37507                     return p;
37508                 }
37509             }
37510         }
37511         return null;
37512     },
37513
37514     /**
37515      * Searches all regions for a panel with the specified id and activates (shows) it.
37516      * @param {String/ContentPanel} panelId The panels id or the panel itself
37517      * @return {Roo.ContentPanel} The shown panel or null
37518      */
37519     showPanel : function(panelId) {
37520       var rs = this.regions;
37521       for(var target in rs){
37522          var r = rs[target];
37523          if(typeof r != "function"){
37524             if(r.hasPanel(panelId)){
37525                return r.showPanel(panelId);
37526             }
37527          }
37528       }
37529       return null;
37530    },
37531
37532    /**
37533      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37534      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37535      */
37536    /*
37537     restoreState : function(provider){
37538         if(!provider){
37539             provider = Roo.state.Manager;
37540         }
37541         var sm = new Roo.LayoutStateManager();
37542         sm.init(this, provider);
37543     },
37544 */
37545  
37546  
37547     /**
37548      * Adds a xtype elements to the layout.
37549      * <pre><code>
37550
37551 layout.addxtype({
37552        xtype : 'ContentPanel',
37553        region: 'west',
37554        items: [ .... ]
37555    }
37556 );
37557
37558 layout.addxtype({
37559         xtype : 'NestedLayoutPanel',
37560         region: 'west',
37561         layout: {
37562            center: { },
37563            west: { }   
37564         },
37565         items : [ ... list of content panels or nested layout panels.. ]
37566    }
37567 );
37568 </code></pre>
37569      * @param {Object} cfg Xtype definition of item to add.
37570      */
37571     addxtype : function(cfg)
37572     {
37573         // basically accepts a pannel...
37574         // can accept a layout region..!?!?
37575         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37576         
37577         
37578         // theory?  children can only be panels??
37579         
37580         //if (!cfg.xtype.match(/Panel$/)) {
37581         //    return false;
37582         //}
37583         var ret = false;
37584         
37585         if (typeof(cfg.region) == 'undefined') {
37586             Roo.log("Failed to add Panel, region was not set");
37587             Roo.log(cfg);
37588             return false;
37589         }
37590         var region = cfg.region;
37591         delete cfg.region;
37592         
37593           
37594         var xitems = [];
37595         if (cfg.items) {
37596             xitems = cfg.items;
37597             delete cfg.items;
37598         }
37599         var nb = false;
37600         
37601         if ( region == 'center') {
37602             Roo.log("Center: " + cfg.title);
37603         }
37604         
37605         
37606         switch(cfg.xtype) 
37607         {
37608             case 'Content':  // ContentPanel (el, cfg)
37609             case 'Scroll':  // ContentPanel (el, cfg)
37610             case 'View': 
37611                 cfg.autoCreate = cfg.autoCreate || true;
37612                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37613                 //} else {
37614                 //    var el = this.el.createChild();
37615                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37616                 //}
37617                 
37618                 this.add(region, ret);
37619                 break;
37620             
37621             /*
37622             case 'TreePanel': // our new panel!
37623                 cfg.el = this.el.createChild();
37624                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37625                 this.add(region, ret);
37626                 break;
37627             */
37628             
37629             case 'Nest': 
37630                 // create a new Layout (which is  a Border Layout...
37631                 
37632                 var clayout = cfg.layout;
37633                 clayout.el  = this.el.createChild();
37634                 clayout.items   = clayout.items  || [];
37635                 
37636                 delete cfg.layout;
37637                 
37638                 // replace this exitems with the clayout ones..
37639                 xitems = clayout.items;
37640                  
37641                 // force background off if it's in center...
37642                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37643                     cfg.background = false;
37644                 }
37645                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37646                 
37647                 
37648                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37649                 //console.log('adding nested layout panel '  + cfg.toSource());
37650                 this.add(region, ret);
37651                 nb = {}; /// find first...
37652                 break;
37653             
37654             case 'Grid':
37655                 
37656                 // needs grid and region
37657                 
37658                 //var el = this.getRegion(region).el.createChild();
37659                 /*
37660                  *var el = this.el.createChild();
37661                 // create the grid first...
37662                 cfg.grid.container = el;
37663                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37664                 */
37665                 
37666                 if (region == 'center' && this.active ) {
37667                     cfg.background = false;
37668                 }
37669                 
37670                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37671                 
37672                 this.add(region, ret);
37673                 /*
37674                 if (cfg.background) {
37675                     // render grid on panel activation (if panel background)
37676                     ret.on('activate', function(gp) {
37677                         if (!gp.grid.rendered) {
37678                     //        gp.grid.render(el);
37679                         }
37680                     });
37681                 } else {
37682                   //  cfg.grid.render(el);
37683                 }
37684                 */
37685                 break;
37686            
37687            
37688             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37689                 // it was the old xcomponent building that caused this before.
37690                 // espeically if border is the top element in the tree.
37691                 ret = this;
37692                 break; 
37693                 
37694                     
37695                 
37696                 
37697                 
37698             default:
37699                 /*
37700                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37701                     
37702                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37703                     this.add(region, ret);
37704                 } else {
37705                 */
37706                     Roo.log(cfg);
37707                     throw "Can not add '" + cfg.xtype + "' to Border";
37708                     return null;
37709              
37710                                 
37711              
37712         }
37713         this.beginUpdate();
37714         // add children..
37715         var region = '';
37716         var abn = {};
37717         Roo.each(xitems, function(i)  {
37718             region = nb && i.region ? i.region : false;
37719             
37720             var add = ret.addxtype(i);
37721            
37722             if (region) {
37723                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37724                 if (!i.background) {
37725                     abn[region] = nb[region] ;
37726                 }
37727             }
37728             
37729         });
37730         this.endUpdate();
37731
37732         // make the last non-background panel active..
37733         //if (nb) { Roo.log(abn); }
37734         if (nb) {
37735             
37736             for(var r in abn) {
37737                 region = this.getRegion(r);
37738                 if (region) {
37739                     // tried using nb[r], but it does not work..
37740                      
37741                     region.showPanel(abn[r]);
37742                    
37743                 }
37744             }
37745         }
37746         return ret;
37747         
37748     },
37749     
37750     
37751 // private
37752     factory : function(cfg)
37753     {
37754         
37755         var validRegions = Roo.bootstrap.layout.Border.regions;
37756
37757         var target = cfg.region;
37758         cfg.mgr = this;
37759         
37760         var r = Roo.bootstrap.layout;
37761         Roo.log(target);
37762         switch(target){
37763             case "north":
37764                 return new r.North(cfg);
37765             case "south":
37766                 return new r.South(cfg);
37767             case "east":
37768                 return new r.East(cfg);
37769             case "west":
37770                 return new r.West(cfg);
37771             case "center":
37772                 return new r.Center(cfg);
37773         }
37774         throw 'Layout region "'+target+'" not supported.';
37775     }
37776     
37777     
37778 });
37779  /*
37780  * Based on:
37781  * Ext JS Library 1.1.1
37782  * Copyright(c) 2006-2007, Ext JS, LLC.
37783  *
37784  * Originally Released Under LGPL - original licence link has changed is not relivant.
37785  *
37786  * Fork - LGPL
37787  * <script type="text/javascript">
37788  */
37789  
37790 /**
37791  * @class Roo.bootstrap.layout.Basic
37792  * @extends Roo.util.Observable
37793  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37794  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37795  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37796  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37797  * @cfg {string}   region  the region that it inhabits..
37798  * @cfg {bool}   skipConfig skip config?
37799  * 
37800
37801  */
37802 Roo.bootstrap.layout.Basic = function(config){
37803     
37804     this.mgr = config.mgr;
37805     
37806     this.position = config.region;
37807     
37808     var skipConfig = config.skipConfig;
37809     
37810     this.events = {
37811         /**
37812          * @scope Roo.BasicLayoutRegion
37813          */
37814         
37815         /**
37816          * @event beforeremove
37817          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37818          * @param {Roo.LayoutRegion} this
37819          * @param {Roo.ContentPanel} panel The panel
37820          * @param {Object} e The cancel event object
37821          */
37822         "beforeremove" : true,
37823         /**
37824          * @event invalidated
37825          * Fires when the layout for this region is changed.
37826          * @param {Roo.LayoutRegion} this
37827          */
37828         "invalidated" : true,
37829         /**
37830          * @event visibilitychange
37831          * Fires when this region is shown or hidden 
37832          * @param {Roo.LayoutRegion} this
37833          * @param {Boolean} visibility true or false
37834          */
37835         "visibilitychange" : true,
37836         /**
37837          * @event paneladded
37838          * Fires when a panel is added. 
37839          * @param {Roo.LayoutRegion} this
37840          * @param {Roo.ContentPanel} panel The panel
37841          */
37842         "paneladded" : true,
37843         /**
37844          * @event panelremoved
37845          * Fires when a panel is removed. 
37846          * @param {Roo.LayoutRegion} this
37847          * @param {Roo.ContentPanel} panel The panel
37848          */
37849         "panelremoved" : true,
37850         /**
37851          * @event beforecollapse
37852          * Fires when this region before collapse.
37853          * @param {Roo.LayoutRegion} this
37854          */
37855         "beforecollapse" : true,
37856         /**
37857          * @event collapsed
37858          * Fires when this region is collapsed.
37859          * @param {Roo.LayoutRegion} this
37860          */
37861         "collapsed" : true,
37862         /**
37863          * @event expanded
37864          * Fires when this region is expanded.
37865          * @param {Roo.LayoutRegion} this
37866          */
37867         "expanded" : true,
37868         /**
37869          * @event slideshow
37870          * Fires when this region is slid into view.
37871          * @param {Roo.LayoutRegion} this
37872          */
37873         "slideshow" : true,
37874         /**
37875          * @event slidehide
37876          * Fires when this region slides out of view. 
37877          * @param {Roo.LayoutRegion} this
37878          */
37879         "slidehide" : true,
37880         /**
37881          * @event panelactivated
37882          * Fires when a panel is activated. 
37883          * @param {Roo.LayoutRegion} this
37884          * @param {Roo.ContentPanel} panel The activated panel
37885          */
37886         "panelactivated" : true,
37887         /**
37888          * @event resized
37889          * Fires when the user resizes this region. 
37890          * @param {Roo.LayoutRegion} this
37891          * @param {Number} newSize The new size (width for east/west, height for north/south)
37892          */
37893         "resized" : true
37894     };
37895     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37896     this.panels = new Roo.util.MixedCollection();
37897     this.panels.getKey = this.getPanelId.createDelegate(this);
37898     this.box = null;
37899     this.activePanel = null;
37900     // ensure listeners are added...
37901     
37902     if (config.listeners || config.events) {
37903         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37904             listeners : config.listeners || {},
37905             events : config.events || {}
37906         });
37907     }
37908     
37909     if(skipConfig !== true){
37910         this.applyConfig(config);
37911     }
37912 };
37913
37914 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37915 {
37916     getPanelId : function(p){
37917         return p.getId();
37918     },
37919     
37920     applyConfig : function(config){
37921         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37922         this.config = config;
37923         
37924     },
37925     
37926     /**
37927      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37928      * the width, for horizontal (north, south) the height.
37929      * @param {Number} newSize The new width or height
37930      */
37931     resizeTo : function(newSize){
37932         var el = this.el ? this.el :
37933                  (this.activePanel ? this.activePanel.getEl() : null);
37934         if(el){
37935             switch(this.position){
37936                 case "east":
37937                 case "west":
37938                     el.setWidth(newSize);
37939                     this.fireEvent("resized", this, newSize);
37940                 break;
37941                 case "north":
37942                 case "south":
37943                     el.setHeight(newSize);
37944                     this.fireEvent("resized", this, newSize);
37945                 break;                
37946             }
37947         }
37948     },
37949     
37950     getBox : function(){
37951         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37952     },
37953     
37954     getMargins : function(){
37955         return this.margins;
37956     },
37957     
37958     updateBox : function(box){
37959         this.box = box;
37960         var el = this.activePanel.getEl();
37961         el.dom.style.left = box.x + "px";
37962         el.dom.style.top = box.y + "px";
37963         this.activePanel.setSize(box.width, box.height);
37964     },
37965     
37966     /**
37967      * Returns the container element for this region.
37968      * @return {Roo.Element}
37969      */
37970     getEl : function(){
37971         return this.activePanel;
37972     },
37973     
37974     /**
37975      * Returns true if this region is currently visible.
37976      * @return {Boolean}
37977      */
37978     isVisible : function(){
37979         return this.activePanel ? true : false;
37980     },
37981     
37982     setActivePanel : function(panel){
37983         panel = this.getPanel(panel);
37984         if(this.activePanel && this.activePanel != panel){
37985             this.activePanel.setActiveState(false);
37986             this.activePanel.getEl().setLeftTop(-10000,-10000);
37987         }
37988         this.activePanel = panel;
37989         panel.setActiveState(true);
37990         if(this.box){
37991             panel.setSize(this.box.width, this.box.height);
37992         }
37993         this.fireEvent("panelactivated", this, panel);
37994         this.fireEvent("invalidated");
37995     },
37996     
37997     /**
37998      * Show the specified panel.
37999      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38000      * @return {Roo.ContentPanel} The shown panel or null
38001      */
38002     showPanel : function(panel){
38003         panel = this.getPanel(panel);
38004         if(panel){
38005             this.setActivePanel(panel);
38006         }
38007         return panel;
38008     },
38009     
38010     /**
38011      * Get the active panel for this region.
38012      * @return {Roo.ContentPanel} The active panel or null
38013      */
38014     getActivePanel : function(){
38015         return this.activePanel;
38016     },
38017     
38018     /**
38019      * Add the passed ContentPanel(s)
38020      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38021      * @return {Roo.ContentPanel} The panel added (if only one was added)
38022      */
38023     add : function(panel){
38024         if(arguments.length > 1){
38025             for(var i = 0, len = arguments.length; i < len; i++) {
38026                 this.add(arguments[i]);
38027             }
38028             return null;
38029         }
38030         if(this.hasPanel(panel)){
38031             this.showPanel(panel);
38032             return panel;
38033         }
38034         var el = panel.getEl();
38035         if(el.dom.parentNode != this.mgr.el.dom){
38036             this.mgr.el.dom.appendChild(el.dom);
38037         }
38038         if(panel.setRegion){
38039             panel.setRegion(this);
38040         }
38041         this.panels.add(panel);
38042         el.setStyle("position", "absolute");
38043         if(!panel.background){
38044             this.setActivePanel(panel);
38045             if(this.config.initialSize && this.panels.getCount()==1){
38046                 this.resizeTo(this.config.initialSize);
38047             }
38048         }
38049         this.fireEvent("paneladded", this, panel);
38050         return panel;
38051     },
38052     
38053     /**
38054      * Returns true if the panel is in this region.
38055      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38056      * @return {Boolean}
38057      */
38058     hasPanel : function(panel){
38059         if(typeof panel == "object"){ // must be panel obj
38060             panel = panel.getId();
38061         }
38062         return this.getPanel(panel) ? true : false;
38063     },
38064     
38065     /**
38066      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38067      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38068      * @param {Boolean} preservePanel Overrides the config preservePanel option
38069      * @return {Roo.ContentPanel} The panel that was removed
38070      */
38071     remove : function(panel, preservePanel){
38072         panel = this.getPanel(panel);
38073         if(!panel){
38074             return null;
38075         }
38076         var e = {};
38077         this.fireEvent("beforeremove", this, panel, e);
38078         if(e.cancel === true){
38079             return null;
38080         }
38081         var panelId = panel.getId();
38082         this.panels.removeKey(panelId);
38083         return panel;
38084     },
38085     
38086     /**
38087      * Returns the panel specified or null if it's not in this region.
38088      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38089      * @return {Roo.ContentPanel}
38090      */
38091     getPanel : function(id){
38092         if(typeof id == "object"){ // must be panel obj
38093             return id;
38094         }
38095         return this.panels.get(id);
38096     },
38097     
38098     /**
38099      * Returns this regions position (north/south/east/west/center).
38100      * @return {String} 
38101      */
38102     getPosition: function(){
38103         return this.position;    
38104     }
38105 });/*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116 /**
38117  * @class Roo.bootstrap.layout.Region
38118  * @extends Roo.bootstrap.layout.Basic
38119  * This class represents a region in a layout manager.
38120  
38121  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38122  * @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})
38123  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38124  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38125  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38126  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38127  * @cfg {String}    title           The title for the region (overrides panel titles)
38128  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38129  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38130  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38131  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38132  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38133  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38134  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38135  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38136  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38137  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38138
38139  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38140  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38141  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38142  * @cfg {Number}    width           For East/West panels
38143  * @cfg {Number}    height          For North/South panels
38144  * @cfg {Boolean}   split           To show the splitter
38145  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38146  * 
38147  * @cfg {string}   cls             Extra CSS classes to add to region
38148  * 
38149  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38150  * @cfg {string}   region  the region that it inhabits..
38151  *
38152
38153  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38154  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38155
38156  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38157  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38158  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38159  */
38160 Roo.bootstrap.layout.Region = function(config)
38161 {
38162     this.applyConfig(config);
38163
38164     var mgr = config.mgr;
38165     var pos = config.region;
38166     config.skipConfig = true;
38167     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38168     
38169     if (mgr.el) {
38170         this.onRender(mgr.el);   
38171     }
38172      
38173     this.visible = true;
38174     this.collapsed = false;
38175     this.unrendered_panels = [];
38176 };
38177
38178 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38179
38180     position: '', // set by wrapper (eg. north/south etc..)
38181     unrendered_panels : null,  // unrendered panels.
38182     
38183     tabPosition : false,
38184     
38185     mgr: false, // points to 'Border'
38186     
38187     
38188     createBody : function(){
38189         /** This region's body element 
38190         * @type Roo.Element */
38191         this.bodyEl = this.el.createChild({
38192                 tag: "div",
38193                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38194         });
38195     },
38196
38197     onRender: function(ctr, pos)
38198     {
38199         var dh = Roo.DomHelper;
38200         /** This region's container element 
38201         * @type Roo.Element */
38202         this.el = dh.append(ctr.dom, {
38203                 tag: "div",
38204                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38205             }, true);
38206         /** This region's title element 
38207         * @type Roo.Element */
38208     
38209         this.titleEl = dh.append(this.el.dom,  {
38210                 tag: "div",
38211                 unselectable: "on",
38212                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38213                 children:[
38214                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38215                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38216                 ]
38217             }, true);
38218         
38219         this.titleEl.enableDisplayMode();
38220         /** This region's title text element 
38221         * @type HTMLElement */
38222         this.titleTextEl = this.titleEl.dom.firstChild;
38223         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38224         /*
38225         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38226         this.closeBtn.enableDisplayMode();
38227         this.closeBtn.on("click", this.closeClicked, this);
38228         this.closeBtn.hide();
38229     */
38230         this.createBody(this.config);
38231         if(this.config.hideWhenEmpty){
38232             this.hide();
38233             this.on("paneladded", this.validateVisibility, this);
38234             this.on("panelremoved", this.validateVisibility, this);
38235         }
38236         if(this.autoScroll){
38237             this.bodyEl.setStyle("overflow", "auto");
38238         }else{
38239             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38240         }
38241         //if(c.titlebar !== false){
38242             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38243                 this.titleEl.hide();
38244             }else{
38245                 this.titleEl.show();
38246                 if(this.config.title){
38247                     this.titleTextEl.innerHTML = this.config.title;
38248                 }
38249             }
38250         //}
38251         if(this.config.collapsed){
38252             this.collapse(true);
38253         }
38254         if(this.config.hidden){
38255             this.hide();
38256         }
38257         
38258         if (this.unrendered_panels && this.unrendered_panels.length) {
38259             for (var i =0;i< this.unrendered_panels.length; i++) {
38260                 this.add(this.unrendered_panels[i]);
38261             }
38262             this.unrendered_panels = null;
38263             
38264         }
38265         
38266     },
38267     
38268     applyConfig : function(c)
38269     {
38270         /*
38271          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38272             var dh = Roo.DomHelper;
38273             if(c.titlebar !== false){
38274                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38275                 this.collapseBtn.on("click", this.collapse, this);
38276                 this.collapseBtn.enableDisplayMode();
38277                 /*
38278                 if(c.showPin === true || this.showPin){
38279                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38280                     this.stickBtn.enableDisplayMode();
38281                     this.stickBtn.on("click", this.expand, this);
38282                     this.stickBtn.hide();
38283                 }
38284                 
38285             }
38286             */
38287             /** This region's collapsed element
38288             * @type Roo.Element */
38289             /*
38290              *
38291             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38292                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38293             ]}, true);
38294             
38295             if(c.floatable !== false){
38296                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38297                this.collapsedEl.on("click", this.collapseClick, this);
38298             }
38299
38300             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38301                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38302                    id: "message", unselectable: "on", style:{"float":"left"}});
38303                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38304              }
38305             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38306             this.expandBtn.on("click", this.expand, this);
38307             
38308         }
38309         
38310         if(this.collapseBtn){
38311             this.collapseBtn.setVisible(c.collapsible == true);
38312         }
38313         
38314         this.cmargins = c.cmargins || this.cmargins ||
38315                          (this.position == "west" || this.position == "east" ?
38316                              {top: 0, left: 2, right:2, bottom: 0} :
38317                              {top: 2, left: 0, right:0, bottom: 2});
38318         */
38319         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38320         
38321         
38322         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38323         
38324         this.autoScroll = c.autoScroll || false;
38325         
38326         
38327        
38328         
38329         this.duration = c.duration || .30;
38330         this.slideDuration = c.slideDuration || .45;
38331         this.config = c;
38332        
38333     },
38334     /**
38335      * Returns true if this region is currently visible.
38336      * @return {Boolean}
38337      */
38338     isVisible : function(){
38339         return this.visible;
38340     },
38341
38342     /**
38343      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38344      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38345      */
38346     //setCollapsedTitle : function(title){
38347     //    title = title || "&#160;";
38348      //   if(this.collapsedTitleTextEl){
38349       //      this.collapsedTitleTextEl.innerHTML = title;
38350        // }
38351     //},
38352
38353     getBox : function(){
38354         var b;
38355       //  if(!this.collapsed){
38356             b = this.el.getBox(false, true);
38357        // }else{
38358           //  b = this.collapsedEl.getBox(false, true);
38359         //}
38360         return b;
38361     },
38362
38363     getMargins : function(){
38364         return this.margins;
38365         //return this.collapsed ? this.cmargins : this.margins;
38366     },
38367 /*
38368     highlight : function(){
38369         this.el.addClass("x-layout-panel-dragover");
38370     },
38371
38372     unhighlight : function(){
38373         this.el.removeClass("x-layout-panel-dragover");
38374     },
38375 */
38376     updateBox : function(box)
38377     {
38378         if (!this.bodyEl) {
38379             return; // not rendered yet..
38380         }
38381         
38382         this.box = box;
38383         if(!this.collapsed){
38384             this.el.dom.style.left = box.x + "px";
38385             this.el.dom.style.top = box.y + "px";
38386             this.updateBody(box.width, box.height);
38387         }else{
38388             this.collapsedEl.dom.style.left = box.x + "px";
38389             this.collapsedEl.dom.style.top = box.y + "px";
38390             this.collapsedEl.setSize(box.width, box.height);
38391         }
38392         if(this.tabs){
38393             this.tabs.autoSizeTabs();
38394         }
38395     },
38396
38397     updateBody : function(w, h)
38398     {
38399         if(w !== null){
38400             this.el.setWidth(w);
38401             w -= this.el.getBorderWidth("rl");
38402             if(this.config.adjustments){
38403                 w += this.config.adjustments[0];
38404             }
38405         }
38406         if(h !== null && h > 0){
38407             this.el.setHeight(h);
38408             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38409             h -= this.el.getBorderWidth("tb");
38410             if(this.config.adjustments){
38411                 h += this.config.adjustments[1];
38412             }
38413             this.bodyEl.setHeight(h);
38414             if(this.tabs){
38415                 h = this.tabs.syncHeight(h);
38416             }
38417         }
38418         if(this.panelSize){
38419             w = w !== null ? w : this.panelSize.width;
38420             h = h !== null ? h : this.panelSize.height;
38421         }
38422         if(this.activePanel){
38423             var el = this.activePanel.getEl();
38424             w = w !== null ? w : el.getWidth();
38425             h = h !== null ? h : el.getHeight();
38426             this.panelSize = {width: w, height: h};
38427             this.activePanel.setSize(w, h);
38428         }
38429         if(Roo.isIE && this.tabs){
38430             this.tabs.el.repaint();
38431         }
38432     },
38433
38434     /**
38435      * Returns the container element for this region.
38436      * @return {Roo.Element}
38437      */
38438     getEl : function(){
38439         return this.el;
38440     },
38441
38442     /**
38443      * Hides this region.
38444      */
38445     hide : function(){
38446         //if(!this.collapsed){
38447             this.el.dom.style.left = "-2000px";
38448             this.el.hide();
38449         //}else{
38450          //   this.collapsedEl.dom.style.left = "-2000px";
38451          //   this.collapsedEl.hide();
38452        // }
38453         this.visible = false;
38454         this.fireEvent("visibilitychange", this, false);
38455     },
38456
38457     /**
38458      * Shows this region if it was previously hidden.
38459      */
38460     show : function(){
38461         //if(!this.collapsed){
38462             this.el.show();
38463         //}else{
38464         //    this.collapsedEl.show();
38465        // }
38466         this.visible = true;
38467         this.fireEvent("visibilitychange", this, true);
38468     },
38469 /*
38470     closeClicked : function(){
38471         if(this.activePanel){
38472             this.remove(this.activePanel);
38473         }
38474     },
38475
38476     collapseClick : function(e){
38477         if(this.isSlid){
38478            e.stopPropagation();
38479            this.slideIn();
38480         }else{
38481            e.stopPropagation();
38482            this.slideOut();
38483         }
38484     },
38485 */
38486     /**
38487      * Collapses this region.
38488      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38489      */
38490     /*
38491     collapse : function(skipAnim, skipCheck = false){
38492         if(this.collapsed) {
38493             return;
38494         }
38495         
38496         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38497             
38498             this.collapsed = true;
38499             if(this.split){
38500                 this.split.el.hide();
38501             }
38502             if(this.config.animate && skipAnim !== true){
38503                 this.fireEvent("invalidated", this);
38504                 this.animateCollapse();
38505             }else{
38506                 this.el.setLocation(-20000,-20000);
38507                 this.el.hide();
38508                 this.collapsedEl.show();
38509                 this.fireEvent("collapsed", this);
38510                 this.fireEvent("invalidated", this);
38511             }
38512         }
38513         
38514     },
38515 */
38516     animateCollapse : function(){
38517         // overridden
38518     },
38519
38520     /**
38521      * Expands this region if it was previously collapsed.
38522      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38523      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38524      */
38525     /*
38526     expand : function(e, skipAnim){
38527         if(e) {
38528             e.stopPropagation();
38529         }
38530         if(!this.collapsed || this.el.hasActiveFx()) {
38531             return;
38532         }
38533         if(this.isSlid){
38534             this.afterSlideIn();
38535             skipAnim = true;
38536         }
38537         this.collapsed = false;
38538         if(this.config.animate && skipAnim !== true){
38539             this.animateExpand();
38540         }else{
38541             this.el.show();
38542             if(this.split){
38543                 this.split.el.show();
38544             }
38545             this.collapsedEl.setLocation(-2000,-2000);
38546             this.collapsedEl.hide();
38547             this.fireEvent("invalidated", this);
38548             this.fireEvent("expanded", this);
38549         }
38550     },
38551 */
38552     animateExpand : function(){
38553         // overridden
38554     },
38555
38556     initTabs : function()
38557     {
38558         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38559         
38560         var ts = new Roo.bootstrap.panel.Tabs({
38561             el: this.bodyEl.dom,
38562             region : this,
38563             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38564             disableTooltips: this.config.disableTabTips,
38565             toolbar : this.config.toolbar
38566         });
38567         
38568         if(this.config.hideTabs){
38569             ts.stripWrap.setDisplayed(false);
38570         }
38571         this.tabs = ts;
38572         ts.resizeTabs = this.config.resizeTabs === true;
38573         ts.minTabWidth = this.config.minTabWidth || 40;
38574         ts.maxTabWidth = this.config.maxTabWidth || 250;
38575         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38576         ts.monitorResize = false;
38577         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38578         ts.bodyEl.addClass('roo-layout-tabs-body');
38579         this.panels.each(this.initPanelAsTab, this);
38580     },
38581
38582     initPanelAsTab : function(panel){
38583         var ti = this.tabs.addTab(
38584             panel.getEl().id,
38585             panel.getTitle(),
38586             null,
38587             this.config.closeOnTab && panel.isClosable(),
38588             panel.tpl
38589         );
38590         if(panel.tabTip !== undefined){
38591             ti.setTooltip(panel.tabTip);
38592         }
38593         ti.on("activate", function(){
38594               this.setActivePanel(panel);
38595         }, this);
38596         
38597         if(this.config.closeOnTab){
38598             ti.on("beforeclose", function(t, e){
38599                 e.cancel = true;
38600                 this.remove(panel);
38601             }, this);
38602         }
38603         
38604         panel.tabItem = ti;
38605         
38606         return ti;
38607     },
38608
38609     updatePanelTitle : function(panel, title)
38610     {
38611         if(this.activePanel == panel){
38612             this.updateTitle(title);
38613         }
38614         if(this.tabs){
38615             var ti = this.tabs.getTab(panel.getEl().id);
38616             ti.setText(title);
38617             if(panel.tabTip !== undefined){
38618                 ti.setTooltip(panel.tabTip);
38619             }
38620         }
38621     },
38622
38623     updateTitle : function(title){
38624         if(this.titleTextEl && !this.config.title){
38625             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38626         }
38627     },
38628
38629     setActivePanel : function(panel)
38630     {
38631         panel = this.getPanel(panel);
38632         if(this.activePanel && this.activePanel != panel){
38633             if(this.activePanel.setActiveState(false) === false){
38634                 return;
38635             }
38636         }
38637         this.activePanel = panel;
38638         panel.setActiveState(true);
38639         if(this.panelSize){
38640             panel.setSize(this.panelSize.width, this.panelSize.height);
38641         }
38642         if(this.closeBtn){
38643             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38644         }
38645         this.updateTitle(panel.getTitle());
38646         if(this.tabs){
38647             this.fireEvent("invalidated", this);
38648         }
38649         this.fireEvent("panelactivated", this, panel);
38650     },
38651
38652     /**
38653      * Shows the specified panel.
38654      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38655      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38656      */
38657     showPanel : function(panel)
38658     {
38659         panel = this.getPanel(panel);
38660         if(panel){
38661             if(this.tabs){
38662                 var tab = this.tabs.getTab(panel.getEl().id);
38663                 if(tab.isHidden()){
38664                     this.tabs.unhideTab(tab.id);
38665                 }
38666                 tab.activate();
38667             }else{
38668                 this.setActivePanel(panel);
38669             }
38670         }
38671         return panel;
38672     },
38673
38674     /**
38675      * Get the active panel for this region.
38676      * @return {Roo.ContentPanel} The active panel or null
38677      */
38678     getActivePanel : function(){
38679         return this.activePanel;
38680     },
38681
38682     validateVisibility : function(){
38683         if(this.panels.getCount() < 1){
38684             this.updateTitle("&#160;");
38685             this.closeBtn.hide();
38686             this.hide();
38687         }else{
38688             if(!this.isVisible()){
38689                 this.show();
38690             }
38691         }
38692     },
38693
38694     /**
38695      * Adds the passed ContentPanel(s) to this region.
38696      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38697      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38698      */
38699     add : function(panel)
38700     {
38701         if(arguments.length > 1){
38702             for(var i = 0, len = arguments.length; i < len; i++) {
38703                 this.add(arguments[i]);
38704             }
38705             return null;
38706         }
38707         
38708         // if we have not been rendered yet, then we can not really do much of this..
38709         if (!this.bodyEl) {
38710             this.unrendered_panels.push(panel);
38711             return panel;
38712         }
38713         
38714         
38715         
38716         
38717         if(this.hasPanel(panel)){
38718             this.showPanel(panel);
38719             return panel;
38720         }
38721         panel.setRegion(this);
38722         this.panels.add(panel);
38723        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38724             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38725             // and hide them... ???
38726             this.bodyEl.dom.appendChild(panel.getEl().dom);
38727             if(panel.background !== true){
38728                 this.setActivePanel(panel);
38729             }
38730             this.fireEvent("paneladded", this, panel);
38731             return panel;
38732         }
38733         */
38734         if(!this.tabs){
38735             this.initTabs();
38736         }else{
38737             this.initPanelAsTab(panel);
38738         }
38739         
38740         
38741         if(panel.background !== true){
38742             this.tabs.activate(panel.getEl().id);
38743         }
38744         this.fireEvent("paneladded", this, panel);
38745         return panel;
38746     },
38747
38748     /**
38749      * Hides the tab for the specified panel.
38750      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38751      */
38752     hidePanel : function(panel){
38753         if(this.tabs && (panel = this.getPanel(panel))){
38754             this.tabs.hideTab(panel.getEl().id);
38755         }
38756     },
38757
38758     /**
38759      * Unhides the tab for a previously hidden panel.
38760      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38761      */
38762     unhidePanel : function(panel){
38763         if(this.tabs && (panel = this.getPanel(panel))){
38764             this.tabs.unhideTab(panel.getEl().id);
38765         }
38766     },
38767
38768     clearPanels : function(){
38769         while(this.panels.getCount() > 0){
38770              this.remove(this.panels.first());
38771         }
38772     },
38773
38774     /**
38775      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38776      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38777      * @param {Boolean} preservePanel Overrides the config preservePanel option
38778      * @return {Roo.ContentPanel} The panel that was removed
38779      */
38780     remove : function(panel, preservePanel)
38781     {
38782         panel = this.getPanel(panel);
38783         if(!panel){
38784             return null;
38785         }
38786         var e = {};
38787         this.fireEvent("beforeremove", this, panel, e);
38788         if(e.cancel === true){
38789             return null;
38790         }
38791         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38792         var panelId = panel.getId();
38793         this.panels.removeKey(panelId);
38794         if(preservePanel){
38795             document.body.appendChild(panel.getEl().dom);
38796         }
38797         if(this.tabs){
38798             this.tabs.removeTab(panel.getEl().id);
38799         }else if (!preservePanel){
38800             this.bodyEl.dom.removeChild(panel.getEl().dom);
38801         }
38802         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38803             var p = this.panels.first();
38804             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38805             tempEl.appendChild(p.getEl().dom);
38806             this.bodyEl.update("");
38807             this.bodyEl.dom.appendChild(p.getEl().dom);
38808             tempEl = null;
38809             this.updateTitle(p.getTitle());
38810             this.tabs = null;
38811             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38812             this.setActivePanel(p);
38813         }
38814         panel.setRegion(null);
38815         if(this.activePanel == panel){
38816             this.activePanel = null;
38817         }
38818         if(this.config.autoDestroy !== false && preservePanel !== true){
38819             try{panel.destroy();}catch(e){}
38820         }
38821         this.fireEvent("panelremoved", this, panel);
38822         return panel;
38823     },
38824
38825     /**
38826      * Returns the TabPanel component used by this region
38827      * @return {Roo.TabPanel}
38828      */
38829     getTabs : function(){
38830         return this.tabs;
38831     },
38832
38833     createTool : function(parentEl, className){
38834         var btn = Roo.DomHelper.append(parentEl, {
38835             tag: "div",
38836             cls: "x-layout-tools-button",
38837             children: [ {
38838                 tag: "div",
38839                 cls: "roo-layout-tools-button-inner " + className,
38840                 html: "&#160;"
38841             }]
38842         }, true);
38843         btn.addClassOnOver("roo-layout-tools-button-over");
38844         return btn;
38845     }
38846 });/*
38847  * Based on:
38848  * Ext JS Library 1.1.1
38849  * Copyright(c) 2006-2007, Ext JS, LLC.
38850  *
38851  * Originally Released Under LGPL - original licence link has changed is not relivant.
38852  *
38853  * Fork - LGPL
38854  * <script type="text/javascript">
38855  */
38856  
38857
38858
38859 /**
38860  * @class Roo.SplitLayoutRegion
38861  * @extends Roo.LayoutRegion
38862  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38863  */
38864 Roo.bootstrap.layout.Split = function(config){
38865     this.cursor = config.cursor;
38866     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38867 };
38868
38869 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38870 {
38871     splitTip : "Drag to resize.",
38872     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38873     useSplitTips : false,
38874
38875     applyConfig : function(config){
38876         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38877     },
38878     
38879     onRender : function(ctr,pos) {
38880         
38881         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38882         if(!this.config.split){
38883             return;
38884         }
38885         if(!this.split){
38886             
38887             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38888                             tag: "div",
38889                             id: this.el.id + "-split",
38890                             cls: "roo-layout-split roo-layout-split-"+this.position,
38891                             html: "&#160;"
38892             });
38893             /** The SplitBar for this region 
38894             * @type Roo.SplitBar */
38895             // does not exist yet...
38896             Roo.log([this.position, this.orientation]);
38897             
38898             this.split = new Roo.bootstrap.SplitBar({
38899                 dragElement : splitEl,
38900                 resizingElement: this.el,
38901                 orientation : this.orientation
38902             });
38903             
38904             this.split.on("moved", this.onSplitMove, this);
38905             this.split.useShim = this.config.useShim === true;
38906             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38907             if(this.useSplitTips){
38908                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38909             }
38910             //if(config.collapsible){
38911             //    this.split.el.on("dblclick", this.collapse,  this);
38912             //}
38913         }
38914         if(typeof this.config.minSize != "undefined"){
38915             this.split.minSize = this.config.minSize;
38916         }
38917         if(typeof this.config.maxSize != "undefined"){
38918             this.split.maxSize = this.config.maxSize;
38919         }
38920         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38921             this.hideSplitter();
38922         }
38923         
38924     },
38925
38926     getHMaxSize : function(){
38927          var cmax = this.config.maxSize || 10000;
38928          var center = this.mgr.getRegion("center");
38929          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38930     },
38931
38932     getVMaxSize : function(){
38933          var cmax = this.config.maxSize || 10000;
38934          var center = this.mgr.getRegion("center");
38935          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38936     },
38937
38938     onSplitMove : function(split, newSize){
38939         this.fireEvent("resized", this, newSize);
38940     },
38941     
38942     /** 
38943      * Returns the {@link Roo.SplitBar} for this region.
38944      * @return {Roo.SplitBar}
38945      */
38946     getSplitBar : function(){
38947         return this.split;
38948     },
38949     
38950     hide : function(){
38951         this.hideSplitter();
38952         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38953     },
38954
38955     hideSplitter : function(){
38956         if(this.split){
38957             this.split.el.setLocation(-2000,-2000);
38958             this.split.el.hide();
38959         }
38960     },
38961
38962     show : function(){
38963         if(this.split){
38964             this.split.el.show();
38965         }
38966         Roo.bootstrap.layout.Split.superclass.show.call(this);
38967     },
38968     
38969     beforeSlide: function(){
38970         if(Roo.isGecko){// firefox overflow auto bug workaround
38971             this.bodyEl.clip();
38972             if(this.tabs) {
38973                 this.tabs.bodyEl.clip();
38974             }
38975             if(this.activePanel){
38976                 this.activePanel.getEl().clip();
38977                 
38978                 if(this.activePanel.beforeSlide){
38979                     this.activePanel.beforeSlide();
38980                 }
38981             }
38982         }
38983     },
38984     
38985     afterSlide : function(){
38986         if(Roo.isGecko){// firefox overflow auto bug workaround
38987             this.bodyEl.unclip();
38988             if(this.tabs) {
38989                 this.tabs.bodyEl.unclip();
38990             }
38991             if(this.activePanel){
38992                 this.activePanel.getEl().unclip();
38993                 if(this.activePanel.afterSlide){
38994                     this.activePanel.afterSlide();
38995                 }
38996             }
38997         }
38998     },
38999
39000     initAutoHide : function(){
39001         if(this.autoHide !== false){
39002             if(!this.autoHideHd){
39003                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39004                 this.autoHideHd = {
39005                     "mouseout": function(e){
39006                         if(!e.within(this.el, true)){
39007                             st.delay(500);
39008                         }
39009                     },
39010                     "mouseover" : function(e){
39011                         st.cancel();
39012                     },
39013                     scope : this
39014                 };
39015             }
39016             this.el.on(this.autoHideHd);
39017         }
39018     },
39019
39020     clearAutoHide : function(){
39021         if(this.autoHide !== false){
39022             this.el.un("mouseout", this.autoHideHd.mouseout);
39023             this.el.un("mouseover", this.autoHideHd.mouseover);
39024         }
39025     },
39026
39027     clearMonitor : function(){
39028         Roo.get(document).un("click", this.slideInIf, this);
39029     },
39030
39031     // these names are backwards but not changed for compat
39032     slideOut : function(){
39033         if(this.isSlid || this.el.hasActiveFx()){
39034             return;
39035         }
39036         this.isSlid = true;
39037         if(this.collapseBtn){
39038             this.collapseBtn.hide();
39039         }
39040         this.closeBtnState = this.closeBtn.getStyle('display');
39041         this.closeBtn.hide();
39042         if(this.stickBtn){
39043             this.stickBtn.show();
39044         }
39045         this.el.show();
39046         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39047         this.beforeSlide();
39048         this.el.setStyle("z-index", 10001);
39049         this.el.slideIn(this.getSlideAnchor(), {
39050             callback: function(){
39051                 this.afterSlide();
39052                 this.initAutoHide();
39053                 Roo.get(document).on("click", this.slideInIf, this);
39054                 this.fireEvent("slideshow", this);
39055             },
39056             scope: this,
39057             block: true
39058         });
39059     },
39060
39061     afterSlideIn : function(){
39062         this.clearAutoHide();
39063         this.isSlid = false;
39064         this.clearMonitor();
39065         this.el.setStyle("z-index", "");
39066         if(this.collapseBtn){
39067             this.collapseBtn.show();
39068         }
39069         this.closeBtn.setStyle('display', this.closeBtnState);
39070         if(this.stickBtn){
39071             this.stickBtn.hide();
39072         }
39073         this.fireEvent("slidehide", this);
39074     },
39075
39076     slideIn : function(cb){
39077         if(!this.isSlid || this.el.hasActiveFx()){
39078             Roo.callback(cb);
39079             return;
39080         }
39081         this.isSlid = false;
39082         this.beforeSlide();
39083         this.el.slideOut(this.getSlideAnchor(), {
39084             callback: function(){
39085                 this.el.setLeftTop(-10000, -10000);
39086                 this.afterSlide();
39087                 this.afterSlideIn();
39088                 Roo.callback(cb);
39089             },
39090             scope: this,
39091             block: true
39092         });
39093     },
39094     
39095     slideInIf : function(e){
39096         if(!e.within(this.el)){
39097             this.slideIn();
39098         }
39099     },
39100
39101     animateCollapse : function(){
39102         this.beforeSlide();
39103         this.el.setStyle("z-index", 20000);
39104         var anchor = this.getSlideAnchor();
39105         this.el.slideOut(anchor, {
39106             callback : function(){
39107                 this.el.setStyle("z-index", "");
39108                 this.collapsedEl.slideIn(anchor, {duration:.3});
39109                 this.afterSlide();
39110                 this.el.setLocation(-10000,-10000);
39111                 this.el.hide();
39112                 this.fireEvent("collapsed", this);
39113             },
39114             scope: this,
39115             block: true
39116         });
39117     },
39118
39119     animateExpand : function(){
39120         this.beforeSlide();
39121         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39122         this.el.setStyle("z-index", 20000);
39123         this.collapsedEl.hide({
39124             duration:.1
39125         });
39126         this.el.slideIn(this.getSlideAnchor(), {
39127             callback : function(){
39128                 this.el.setStyle("z-index", "");
39129                 this.afterSlide();
39130                 if(this.split){
39131                     this.split.el.show();
39132                 }
39133                 this.fireEvent("invalidated", this);
39134                 this.fireEvent("expanded", this);
39135             },
39136             scope: this,
39137             block: true
39138         });
39139     },
39140
39141     anchors : {
39142         "west" : "left",
39143         "east" : "right",
39144         "north" : "top",
39145         "south" : "bottom"
39146     },
39147
39148     sanchors : {
39149         "west" : "l",
39150         "east" : "r",
39151         "north" : "t",
39152         "south" : "b"
39153     },
39154
39155     canchors : {
39156         "west" : "tl-tr",
39157         "east" : "tr-tl",
39158         "north" : "tl-bl",
39159         "south" : "bl-tl"
39160     },
39161
39162     getAnchor : function(){
39163         return this.anchors[this.position];
39164     },
39165
39166     getCollapseAnchor : function(){
39167         return this.canchors[this.position];
39168     },
39169
39170     getSlideAnchor : function(){
39171         return this.sanchors[this.position];
39172     },
39173
39174     getAlignAdj : function(){
39175         var cm = this.cmargins;
39176         switch(this.position){
39177             case "west":
39178                 return [0, 0];
39179             break;
39180             case "east":
39181                 return [0, 0];
39182             break;
39183             case "north":
39184                 return [0, 0];
39185             break;
39186             case "south":
39187                 return [0, 0];
39188             break;
39189         }
39190     },
39191
39192     getExpandAdj : function(){
39193         var c = this.collapsedEl, cm = this.cmargins;
39194         switch(this.position){
39195             case "west":
39196                 return [-(cm.right+c.getWidth()+cm.left), 0];
39197             break;
39198             case "east":
39199                 return [cm.right+c.getWidth()+cm.left, 0];
39200             break;
39201             case "north":
39202                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39203             break;
39204             case "south":
39205                 return [0, cm.top+cm.bottom+c.getHeight()];
39206             break;
39207         }
39208     }
39209 });/*
39210  * Based on:
39211  * Ext JS Library 1.1.1
39212  * Copyright(c) 2006-2007, Ext JS, LLC.
39213  *
39214  * Originally Released Under LGPL - original licence link has changed is not relivant.
39215  *
39216  * Fork - LGPL
39217  * <script type="text/javascript">
39218  */
39219 /*
39220  * These classes are private internal classes
39221  */
39222 Roo.bootstrap.layout.Center = function(config){
39223     config.region = "center";
39224     Roo.bootstrap.layout.Region.call(this, config);
39225     this.visible = true;
39226     this.minWidth = config.minWidth || 20;
39227     this.minHeight = config.minHeight || 20;
39228 };
39229
39230 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39231     hide : function(){
39232         // center panel can't be hidden
39233     },
39234     
39235     show : function(){
39236         // center panel can't be hidden
39237     },
39238     
39239     getMinWidth: function(){
39240         return this.minWidth;
39241     },
39242     
39243     getMinHeight: function(){
39244         return this.minHeight;
39245     }
39246 });
39247
39248
39249
39250
39251  
39252
39253
39254
39255
39256
39257
39258 Roo.bootstrap.layout.North = function(config)
39259 {
39260     config.region = 'north';
39261     config.cursor = 'n-resize';
39262     
39263     Roo.bootstrap.layout.Split.call(this, config);
39264     
39265     
39266     if(this.split){
39267         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39268         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39269         this.split.el.addClass("roo-layout-split-v");
39270     }
39271     var size = config.initialSize || config.height;
39272     if(typeof size != "undefined"){
39273         this.el.setHeight(size);
39274     }
39275 };
39276 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39277 {
39278     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39279     
39280     
39281     
39282     getBox : function(){
39283         if(this.collapsed){
39284             return this.collapsedEl.getBox();
39285         }
39286         var box = this.el.getBox();
39287         if(this.split){
39288             box.height += this.split.el.getHeight();
39289         }
39290         return box;
39291     },
39292     
39293     updateBox : function(box){
39294         if(this.split && !this.collapsed){
39295             box.height -= this.split.el.getHeight();
39296             this.split.el.setLeft(box.x);
39297             this.split.el.setTop(box.y+box.height);
39298             this.split.el.setWidth(box.width);
39299         }
39300         if(this.collapsed){
39301             this.updateBody(box.width, null);
39302         }
39303         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39304     }
39305 });
39306
39307
39308
39309
39310
39311 Roo.bootstrap.layout.South = function(config){
39312     config.region = 'south';
39313     config.cursor = 's-resize';
39314     Roo.bootstrap.layout.Split.call(this, config);
39315     if(this.split){
39316         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39317         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39318         this.split.el.addClass("roo-layout-split-v");
39319     }
39320     var size = config.initialSize || config.height;
39321     if(typeof size != "undefined"){
39322         this.el.setHeight(size);
39323     }
39324 };
39325
39326 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39327     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39328     getBox : function(){
39329         if(this.collapsed){
39330             return this.collapsedEl.getBox();
39331         }
39332         var box = this.el.getBox();
39333         if(this.split){
39334             var sh = this.split.el.getHeight();
39335             box.height += sh;
39336             box.y -= sh;
39337         }
39338         return box;
39339     },
39340     
39341     updateBox : function(box){
39342         if(this.split && !this.collapsed){
39343             var sh = this.split.el.getHeight();
39344             box.height -= sh;
39345             box.y += sh;
39346             this.split.el.setLeft(box.x);
39347             this.split.el.setTop(box.y-sh);
39348             this.split.el.setWidth(box.width);
39349         }
39350         if(this.collapsed){
39351             this.updateBody(box.width, null);
39352         }
39353         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39354     }
39355 });
39356
39357 Roo.bootstrap.layout.East = function(config){
39358     config.region = "east";
39359     config.cursor = "e-resize";
39360     Roo.bootstrap.layout.Split.call(this, config);
39361     if(this.split){
39362         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39363         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39364         this.split.el.addClass("roo-layout-split-h");
39365     }
39366     var size = config.initialSize || config.width;
39367     if(typeof size != "undefined"){
39368         this.el.setWidth(size);
39369     }
39370 };
39371 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39372     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39373     getBox : function(){
39374         if(this.collapsed){
39375             return this.collapsedEl.getBox();
39376         }
39377         var box = this.el.getBox();
39378         if(this.split){
39379             var sw = this.split.el.getWidth();
39380             box.width += sw;
39381             box.x -= sw;
39382         }
39383         return box;
39384     },
39385
39386     updateBox : function(box){
39387         if(this.split && !this.collapsed){
39388             var sw = this.split.el.getWidth();
39389             box.width -= sw;
39390             this.split.el.setLeft(box.x);
39391             this.split.el.setTop(box.y);
39392             this.split.el.setHeight(box.height);
39393             box.x += sw;
39394         }
39395         if(this.collapsed){
39396             this.updateBody(null, box.height);
39397         }
39398         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39399     }
39400 });
39401
39402 Roo.bootstrap.layout.West = function(config){
39403     config.region = "west";
39404     config.cursor = "w-resize";
39405     
39406     Roo.bootstrap.layout.Split.call(this, config);
39407     if(this.split){
39408         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39409         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39410         this.split.el.addClass("roo-layout-split-h");
39411     }
39412     
39413 };
39414 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39415     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39416     
39417     onRender: function(ctr, pos)
39418     {
39419         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39420         var size = this.config.initialSize || this.config.width;
39421         if(typeof size != "undefined"){
39422             this.el.setWidth(size);
39423         }
39424     },
39425     
39426     getBox : function(){
39427         if(this.collapsed){
39428             return this.collapsedEl.getBox();
39429         }
39430         var box = this.el.getBox();
39431         if(this.split){
39432             box.width += this.split.el.getWidth();
39433         }
39434         return box;
39435     },
39436     
39437     updateBox : function(box){
39438         if(this.split && !this.collapsed){
39439             var sw = this.split.el.getWidth();
39440             box.width -= sw;
39441             this.split.el.setLeft(box.x+box.width);
39442             this.split.el.setTop(box.y);
39443             this.split.el.setHeight(box.height);
39444         }
39445         if(this.collapsed){
39446             this.updateBody(null, box.height);
39447         }
39448         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39449     }
39450 });Roo.namespace("Roo.bootstrap.panel");/*
39451  * Based on:
39452  * Ext JS Library 1.1.1
39453  * Copyright(c) 2006-2007, Ext JS, LLC.
39454  *
39455  * Originally Released Under LGPL - original licence link has changed is not relivant.
39456  *
39457  * Fork - LGPL
39458  * <script type="text/javascript">
39459  */
39460 /**
39461  * @class Roo.ContentPanel
39462  * @extends Roo.util.Observable
39463  * A basic ContentPanel element.
39464  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39465  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39466  * @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
39467  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39468  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39469  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39470  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39471  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39472  * @cfg {String} title          The title for this panel
39473  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39474  * @cfg {String} url            Calls {@link #setUrl} with this value
39475  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39476  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39477  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39478  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39479  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39480  * @cfg {Boolean} badges render the badges
39481  * @cfg {String} cls  extra classes to use  
39482  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39483
39484  * @constructor
39485  * Create a new ContentPanel.
39486  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39487  * @param {String/Object} config A string to set only the title or a config object
39488  * @param {String} content (optional) Set the HTML content for this panel
39489  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39490  */
39491 Roo.bootstrap.panel.Content = function( config){
39492     
39493     this.tpl = config.tpl || false;
39494     
39495     var el = config.el;
39496     var content = config.content;
39497
39498     if(config.autoCreate){ // xtype is available if this is called from factory
39499         el = Roo.id();
39500     }
39501     this.el = Roo.get(el);
39502     if(!this.el && config && config.autoCreate){
39503         if(typeof config.autoCreate == "object"){
39504             if(!config.autoCreate.id){
39505                 config.autoCreate.id = config.id||el;
39506             }
39507             this.el = Roo.DomHelper.append(document.body,
39508                         config.autoCreate, true);
39509         }else{
39510             var elcfg =  {
39511                 tag: "div",
39512                 cls: (config.cls || '') +
39513                     (config.background ? ' bg-' + config.background : '') +
39514                     " roo-layout-inactive-content",
39515                 id: config.id||el
39516             };
39517             if (config.iframe) {
39518                 elcfg.cn = [
39519                     {
39520                         tag : 'iframe',
39521                         style : 'border: 0px',
39522                         src : 'about:blank'
39523                     }
39524                 ];
39525             }
39526               
39527             if (config.html) {
39528                 elcfg.html = config.html;
39529                 
39530             }
39531                         
39532             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39533             if (config.iframe) {
39534                 this.iframeEl = this.el.select('iframe',true).first();
39535             }
39536             
39537         }
39538     } 
39539     this.closable = false;
39540     this.loaded = false;
39541     this.active = false;
39542    
39543       
39544     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39545         
39546         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39547         
39548         this.wrapEl = this.el; //this.el.wrap();
39549         var ti = [];
39550         if (config.toolbar.items) {
39551             ti = config.toolbar.items ;
39552             delete config.toolbar.items ;
39553         }
39554         
39555         var nitems = [];
39556         this.toolbar.render(this.wrapEl, 'before');
39557         for(var i =0;i < ti.length;i++) {
39558           //  Roo.log(['add child', items[i]]);
39559             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39560         }
39561         this.toolbar.items = nitems;
39562         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39563         delete config.toolbar;
39564         
39565     }
39566     /*
39567     // xtype created footer. - not sure if will work as we normally have to render first..
39568     if (this.footer && !this.footer.el && this.footer.xtype) {
39569         if (!this.wrapEl) {
39570             this.wrapEl = this.el.wrap();
39571         }
39572     
39573         this.footer.container = this.wrapEl.createChild();
39574          
39575         this.footer = Roo.factory(this.footer, Roo);
39576         
39577     }
39578     */
39579     
39580      if(typeof config == "string"){
39581         this.title = config;
39582     }else{
39583         Roo.apply(this, config);
39584     }
39585     
39586     if(this.resizeEl){
39587         this.resizeEl = Roo.get(this.resizeEl, true);
39588     }else{
39589         this.resizeEl = this.el;
39590     }
39591     // handle view.xtype
39592     
39593  
39594     
39595     
39596     this.addEvents({
39597         /**
39598          * @event activate
39599          * Fires when this panel is activated. 
39600          * @param {Roo.ContentPanel} this
39601          */
39602         "activate" : true,
39603         /**
39604          * @event deactivate
39605          * Fires when this panel is activated. 
39606          * @param {Roo.ContentPanel} this
39607          */
39608         "deactivate" : true,
39609
39610         /**
39611          * @event resize
39612          * Fires when this panel is resized if fitToFrame is true.
39613          * @param {Roo.ContentPanel} this
39614          * @param {Number} width The width after any component adjustments
39615          * @param {Number} height The height after any component adjustments
39616          */
39617         "resize" : true,
39618         
39619          /**
39620          * @event render
39621          * Fires when this tab is created
39622          * @param {Roo.ContentPanel} this
39623          */
39624         "render" : true
39625         
39626         
39627         
39628     });
39629     
39630
39631     
39632     
39633     if(this.autoScroll && !this.iframe){
39634         this.resizeEl.setStyle("overflow", "auto");
39635     } else {
39636         // fix randome scrolling
39637         //this.el.on('scroll', function() {
39638         //    Roo.log('fix random scolling');
39639         //    this.scrollTo('top',0); 
39640         //});
39641     }
39642     content = content || this.content;
39643     if(content){
39644         this.setContent(content);
39645     }
39646     if(config && config.url){
39647         this.setUrl(this.url, this.params, this.loadOnce);
39648     }
39649     
39650     
39651     
39652     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39653     
39654     if (this.view && typeof(this.view.xtype) != 'undefined') {
39655         this.view.el = this.el.appendChild(document.createElement("div"));
39656         this.view = Roo.factory(this.view); 
39657         this.view.render  &&  this.view.render(false, '');  
39658     }
39659     
39660     
39661     this.fireEvent('render', this);
39662 };
39663
39664 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39665     
39666     cls : '',
39667     background : '',
39668     
39669     tabTip : '',
39670     
39671     iframe : false,
39672     iframeEl : false,
39673     
39674     setRegion : function(region){
39675         this.region = region;
39676         this.setActiveClass(region && !this.background);
39677     },
39678     
39679     
39680     setActiveClass: function(state)
39681     {
39682         if(state){
39683            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39684            this.el.setStyle('position','relative');
39685         }else{
39686            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39687            this.el.setStyle('position', 'absolute');
39688         } 
39689     },
39690     
39691     /**
39692      * Returns the toolbar for this Panel if one was configured. 
39693      * @return {Roo.Toolbar} 
39694      */
39695     getToolbar : function(){
39696         return this.toolbar;
39697     },
39698     
39699     setActiveState : function(active)
39700     {
39701         this.active = active;
39702         this.setActiveClass(active);
39703         if(!active){
39704             if(this.fireEvent("deactivate", this) === false){
39705                 return false;
39706             }
39707             return true;
39708         }
39709         this.fireEvent("activate", this);
39710         return true;
39711     },
39712     /**
39713      * Updates this panel's element (not for iframe)
39714      * @param {String} content The new content
39715      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39716     */
39717     setContent : function(content, loadScripts){
39718         if (this.iframe) {
39719             return;
39720         }
39721         
39722         this.el.update(content, loadScripts);
39723     },
39724
39725     ignoreResize : function(w, h){
39726         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39727             return true;
39728         }else{
39729             this.lastSize = {width: w, height: h};
39730             return false;
39731         }
39732     },
39733     /**
39734      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39735      * @return {Roo.UpdateManager} The UpdateManager
39736      */
39737     getUpdateManager : function(){
39738         if (this.iframe) {
39739             return false;
39740         }
39741         return this.el.getUpdateManager();
39742     },
39743      /**
39744      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39745      * Does not work with IFRAME contents
39746      * @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:
39747 <pre><code>
39748 panel.load({
39749     url: "your-url.php",
39750     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39751     callback: yourFunction,
39752     scope: yourObject, //(optional scope)
39753     discardUrl: false,
39754     nocache: false,
39755     text: "Loading...",
39756     timeout: 30,
39757     scripts: false
39758 });
39759 </code></pre>
39760      
39761      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39762      * 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.
39763      * @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}
39764      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39765      * @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.
39766      * @return {Roo.ContentPanel} this
39767      */
39768     load : function(){
39769         
39770         if (this.iframe) {
39771             return this;
39772         }
39773         
39774         var um = this.el.getUpdateManager();
39775         um.update.apply(um, arguments);
39776         return this;
39777     },
39778
39779
39780     /**
39781      * 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.
39782      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39783      * @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)
39784      * @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)
39785      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39786      */
39787     setUrl : function(url, params, loadOnce){
39788         if (this.iframe) {
39789             this.iframeEl.dom.src = url;
39790             return false;
39791         }
39792         
39793         if(this.refreshDelegate){
39794             this.removeListener("activate", this.refreshDelegate);
39795         }
39796         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39797         this.on("activate", this.refreshDelegate);
39798         return this.el.getUpdateManager();
39799     },
39800     
39801     _handleRefresh : function(url, params, loadOnce){
39802         if(!loadOnce || !this.loaded){
39803             var updater = this.el.getUpdateManager();
39804             updater.update(url, params, this._setLoaded.createDelegate(this));
39805         }
39806     },
39807     
39808     _setLoaded : function(){
39809         this.loaded = true;
39810     }, 
39811     
39812     /**
39813      * Returns this panel's id
39814      * @return {String} 
39815      */
39816     getId : function(){
39817         return this.el.id;
39818     },
39819     
39820     /** 
39821      * Returns this panel's element - used by regiosn to add.
39822      * @return {Roo.Element} 
39823      */
39824     getEl : function(){
39825         return this.wrapEl || this.el;
39826     },
39827     
39828    
39829     
39830     adjustForComponents : function(width, height)
39831     {
39832         //Roo.log('adjustForComponents ');
39833         if(this.resizeEl != this.el){
39834             width -= this.el.getFrameWidth('lr');
39835             height -= this.el.getFrameWidth('tb');
39836         }
39837         if(this.toolbar){
39838             var te = this.toolbar.getEl();
39839             te.setWidth(width);
39840             height -= te.getHeight();
39841         }
39842         if(this.footer){
39843             var te = this.footer.getEl();
39844             te.setWidth(width);
39845             height -= te.getHeight();
39846         }
39847         
39848         
39849         if(this.adjustments){
39850             width += this.adjustments[0];
39851             height += this.adjustments[1];
39852         }
39853         return {"width": width, "height": height};
39854     },
39855     
39856     setSize : function(width, height){
39857         if(this.fitToFrame && !this.ignoreResize(width, height)){
39858             if(this.fitContainer && this.resizeEl != this.el){
39859                 this.el.setSize(width, height);
39860             }
39861             var size = this.adjustForComponents(width, height);
39862             if (this.iframe) {
39863                 this.iframeEl.setSize(width,height);
39864             }
39865             
39866             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39867             this.fireEvent('resize', this, size.width, size.height);
39868             
39869             
39870         }
39871     },
39872     
39873     /**
39874      * Returns this panel's title
39875      * @return {String} 
39876      */
39877     getTitle : function(){
39878         
39879         if (typeof(this.title) != 'object') {
39880             return this.title;
39881         }
39882         
39883         var t = '';
39884         for (var k in this.title) {
39885             if (!this.title.hasOwnProperty(k)) {
39886                 continue;
39887             }
39888             
39889             if (k.indexOf('-') >= 0) {
39890                 var s = k.split('-');
39891                 for (var i = 0; i<s.length; i++) {
39892                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39893                 }
39894             } else {
39895                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39896             }
39897         }
39898         return t;
39899     },
39900     
39901     /**
39902      * Set this panel's title
39903      * @param {String} title
39904      */
39905     setTitle : function(title){
39906         this.title = title;
39907         if(this.region){
39908             this.region.updatePanelTitle(this, title);
39909         }
39910     },
39911     
39912     /**
39913      * Returns true is this panel was configured to be closable
39914      * @return {Boolean} 
39915      */
39916     isClosable : function(){
39917         return this.closable;
39918     },
39919     
39920     beforeSlide : function(){
39921         this.el.clip();
39922         this.resizeEl.clip();
39923     },
39924     
39925     afterSlide : function(){
39926         this.el.unclip();
39927         this.resizeEl.unclip();
39928     },
39929     
39930     /**
39931      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39932      *   Will fail silently if the {@link #setUrl} method has not been called.
39933      *   This does not activate the panel, just updates its content.
39934      */
39935     refresh : function(){
39936         if(this.refreshDelegate){
39937            this.loaded = false;
39938            this.refreshDelegate();
39939         }
39940     },
39941     
39942     /**
39943      * Destroys this panel
39944      */
39945     destroy : function(){
39946         this.el.removeAllListeners();
39947         var tempEl = document.createElement("span");
39948         tempEl.appendChild(this.el.dom);
39949         tempEl.innerHTML = "";
39950         this.el.remove();
39951         this.el = null;
39952     },
39953     
39954     /**
39955      * form - if the content panel contains a form - this is a reference to it.
39956      * @type {Roo.form.Form}
39957      */
39958     form : false,
39959     /**
39960      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39961      *    This contains a reference to it.
39962      * @type {Roo.View}
39963      */
39964     view : false,
39965     
39966       /**
39967      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39968      * <pre><code>
39969
39970 layout.addxtype({
39971        xtype : 'Form',
39972        items: [ .... ]
39973    }
39974 );
39975
39976 </code></pre>
39977      * @param {Object} cfg Xtype definition of item to add.
39978      */
39979     
39980     
39981     getChildContainer: function () {
39982         return this.getEl();
39983     }
39984     
39985     
39986     /*
39987         var  ret = new Roo.factory(cfg);
39988         return ret;
39989         
39990         
39991         // add form..
39992         if (cfg.xtype.match(/^Form$/)) {
39993             
39994             var el;
39995             //if (this.footer) {
39996             //    el = this.footer.container.insertSibling(false, 'before');
39997             //} else {
39998                 el = this.el.createChild();
39999             //}
40000
40001             this.form = new  Roo.form.Form(cfg);
40002             
40003             
40004             if ( this.form.allItems.length) {
40005                 this.form.render(el.dom);
40006             }
40007             return this.form;
40008         }
40009         // should only have one of theses..
40010         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40011             // views.. should not be just added - used named prop 'view''
40012             
40013             cfg.el = this.el.appendChild(document.createElement("div"));
40014             // factory?
40015             
40016             var ret = new Roo.factory(cfg);
40017              
40018              ret.render && ret.render(false, ''); // render blank..
40019             this.view = ret;
40020             return ret;
40021         }
40022         return false;
40023     }
40024     \*/
40025 });
40026  
40027 /**
40028  * @class Roo.bootstrap.panel.Grid
40029  * @extends Roo.bootstrap.panel.Content
40030  * @constructor
40031  * Create a new GridPanel.
40032  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40033  * @param {Object} config A the config object
40034   
40035  */
40036
40037
40038
40039 Roo.bootstrap.panel.Grid = function(config)
40040 {
40041     
40042       
40043     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40044         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40045
40046     config.el = this.wrapper;
40047     //this.el = this.wrapper;
40048     
40049       if (config.container) {
40050         // ctor'ed from a Border/panel.grid
40051         
40052         
40053         this.wrapper.setStyle("overflow", "hidden");
40054         this.wrapper.addClass('roo-grid-container');
40055
40056     }
40057     
40058     
40059     if(config.toolbar){
40060         var tool_el = this.wrapper.createChild();    
40061         this.toolbar = Roo.factory(config.toolbar);
40062         var ti = [];
40063         if (config.toolbar.items) {
40064             ti = config.toolbar.items ;
40065             delete config.toolbar.items ;
40066         }
40067         
40068         var nitems = [];
40069         this.toolbar.render(tool_el);
40070         for(var i =0;i < ti.length;i++) {
40071           //  Roo.log(['add child', items[i]]);
40072             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40073         }
40074         this.toolbar.items = nitems;
40075         
40076         delete config.toolbar;
40077     }
40078     
40079     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40080     config.grid.scrollBody = true;;
40081     config.grid.monitorWindowResize = false; // turn off autosizing
40082     config.grid.autoHeight = false;
40083     config.grid.autoWidth = false;
40084     
40085     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40086     
40087     if (config.background) {
40088         // render grid on panel activation (if panel background)
40089         this.on('activate', function(gp) {
40090             if (!gp.grid.rendered) {
40091                 gp.grid.render(this.wrapper);
40092                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40093             }
40094         });
40095             
40096     } else {
40097         this.grid.render(this.wrapper);
40098         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40099
40100     }
40101     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40102     // ??? needed ??? config.el = this.wrapper;
40103     
40104     
40105     
40106   
40107     // xtype created footer. - not sure if will work as we normally have to render first..
40108     if (this.footer && !this.footer.el && this.footer.xtype) {
40109         
40110         var ctr = this.grid.getView().getFooterPanel(true);
40111         this.footer.dataSource = this.grid.dataSource;
40112         this.footer = Roo.factory(this.footer, Roo);
40113         this.footer.render(ctr);
40114         
40115     }
40116     
40117     
40118     
40119     
40120      
40121 };
40122
40123 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40124     getId : function(){
40125         return this.grid.id;
40126     },
40127     
40128     /**
40129      * Returns the grid for this panel
40130      * @return {Roo.bootstrap.Table} 
40131      */
40132     getGrid : function(){
40133         return this.grid;    
40134     },
40135     
40136     setSize : function(width, height){
40137         if(!this.ignoreResize(width, height)){
40138             var grid = this.grid;
40139             var size = this.adjustForComponents(width, height);
40140             // tfoot is not a footer?
40141           
40142             
40143             var gridel = grid.getGridEl();
40144             gridel.setSize(size.width, size.height);
40145             
40146             var tbd = grid.getGridEl().select('tbody', true).first();
40147             var thd = grid.getGridEl().select('thead',true).first();
40148             var tbf= grid.getGridEl().select('tfoot', true).first();
40149
40150             if (tbf) {
40151                 size.height -= thd.getHeight();
40152             }
40153             if (thd) {
40154                 size.height -= thd.getHeight();
40155             }
40156             
40157             tbd.setSize(size.width, size.height );
40158             // this is for the account management tab -seems to work there.
40159             var thd = grid.getGridEl().select('thead',true).first();
40160             //if (tbd) {
40161             //    tbd.setSize(size.width, size.height - thd.getHeight());
40162             //}
40163              
40164             grid.autoSize();
40165         }
40166     },
40167      
40168     
40169     
40170     beforeSlide : function(){
40171         this.grid.getView().scroller.clip();
40172     },
40173     
40174     afterSlide : function(){
40175         this.grid.getView().scroller.unclip();
40176     },
40177     
40178     destroy : function(){
40179         this.grid.destroy();
40180         delete this.grid;
40181         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40182     }
40183 });
40184
40185 /**
40186  * @class Roo.bootstrap.panel.Nest
40187  * @extends Roo.bootstrap.panel.Content
40188  * @constructor
40189  * Create a new Panel, that can contain a layout.Border.
40190  * 
40191  * 
40192  * @param {Roo.BorderLayout} layout The layout for this panel
40193  * @param {String/Object} config A string to set only the title or a config object
40194  */
40195 Roo.bootstrap.panel.Nest = function(config)
40196 {
40197     // construct with only one argument..
40198     /* FIXME - implement nicer consturctors
40199     if (layout.layout) {
40200         config = layout;
40201         layout = config.layout;
40202         delete config.layout;
40203     }
40204     if (layout.xtype && !layout.getEl) {
40205         // then layout needs constructing..
40206         layout = Roo.factory(layout, Roo);
40207     }
40208     */
40209     
40210     config.el =  config.layout.getEl();
40211     
40212     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40213     
40214     config.layout.monitorWindowResize = false; // turn off autosizing
40215     this.layout = config.layout;
40216     this.layout.getEl().addClass("roo-layout-nested-layout");
40217     this.layout.parent = this;
40218     
40219     
40220     
40221     
40222 };
40223
40224 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40225
40226     setSize : function(width, height){
40227         if(!this.ignoreResize(width, height)){
40228             var size = this.adjustForComponents(width, height);
40229             var el = this.layout.getEl();
40230             if (size.height < 1) {
40231                 el.setWidth(size.width);   
40232             } else {
40233                 el.setSize(size.width, size.height);
40234             }
40235             var touch = el.dom.offsetWidth;
40236             this.layout.layout();
40237             // ie requires a double layout on the first pass
40238             if(Roo.isIE && !this.initialized){
40239                 this.initialized = true;
40240                 this.layout.layout();
40241             }
40242         }
40243     },
40244     
40245     // activate all subpanels if not currently active..
40246     
40247     setActiveState : function(active){
40248         this.active = active;
40249         this.setActiveClass(active);
40250         
40251         if(!active){
40252             this.fireEvent("deactivate", this);
40253             return;
40254         }
40255         
40256         this.fireEvent("activate", this);
40257         // not sure if this should happen before or after..
40258         if (!this.layout) {
40259             return; // should not happen..
40260         }
40261         var reg = false;
40262         for (var r in this.layout.regions) {
40263             reg = this.layout.getRegion(r);
40264             if (reg.getActivePanel()) {
40265                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40266                 reg.setActivePanel(reg.getActivePanel());
40267                 continue;
40268             }
40269             if (!reg.panels.length) {
40270                 continue;
40271             }
40272             reg.showPanel(reg.getPanel(0));
40273         }
40274         
40275         
40276         
40277         
40278     },
40279     
40280     /**
40281      * Returns the nested BorderLayout for this panel
40282      * @return {Roo.BorderLayout} 
40283      */
40284     getLayout : function(){
40285         return this.layout;
40286     },
40287     
40288      /**
40289      * Adds a xtype elements to the layout of the nested panel
40290      * <pre><code>
40291
40292 panel.addxtype({
40293        xtype : 'ContentPanel',
40294        region: 'west',
40295        items: [ .... ]
40296    }
40297 );
40298
40299 panel.addxtype({
40300         xtype : 'NestedLayoutPanel',
40301         region: 'west',
40302         layout: {
40303            center: { },
40304            west: { }   
40305         },
40306         items : [ ... list of content panels or nested layout panels.. ]
40307    }
40308 );
40309 </code></pre>
40310      * @param {Object} cfg Xtype definition of item to add.
40311      */
40312     addxtype : function(cfg) {
40313         return this.layout.addxtype(cfg);
40314     
40315     }
40316 });/*
40317  * Based on:
40318  * Ext JS Library 1.1.1
40319  * Copyright(c) 2006-2007, Ext JS, LLC.
40320  *
40321  * Originally Released Under LGPL - original licence link has changed is not relivant.
40322  *
40323  * Fork - LGPL
40324  * <script type="text/javascript">
40325  */
40326 /**
40327  * @class Roo.TabPanel
40328  * @extends Roo.util.Observable
40329  * A lightweight tab container.
40330  * <br><br>
40331  * Usage:
40332  * <pre><code>
40333 // basic tabs 1, built from existing content
40334 var tabs = new Roo.TabPanel("tabs1");
40335 tabs.addTab("script", "View Script");
40336 tabs.addTab("markup", "View Markup");
40337 tabs.activate("script");
40338
40339 // more advanced tabs, built from javascript
40340 var jtabs = new Roo.TabPanel("jtabs");
40341 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40342
40343 // set up the UpdateManager
40344 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40345 var updater = tab2.getUpdateManager();
40346 updater.setDefaultUrl("ajax1.htm");
40347 tab2.on('activate', updater.refresh, updater, true);
40348
40349 // Use setUrl for Ajax loading
40350 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40351 tab3.setUrl("ajax2.htm", null, true);
40352
40353 // Disabled tab
40354 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40355 tab4.disable();
40356
40357 jtabs.activate("jtabs-1");
40358  * </code></pre>
40359  * @constructor
40360  * Create a new TabPanel.
40361  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40362  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40363  */
40364 Roo.bootstrap.panel.Tabs = function(config){
40365     /**
40366     * The container element for this TabPanel.
40367     * @type Roo.Element
40368     */
40369     this.el = Roo.get(config.el);
40370     delete config.el;
40371     if(config){
40372         if(typeof config == "boolean"){
40373             this.tabPosition = config ? "bottom" : "top";
40374         }else{
40375             Roo.apply(this, config);
40376         }
40377     }
40378     
40379     if(this.tabPosition == "bottom"){
40380         // if tabs are at the bottom = create the body first.
40381         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40382         this.el.addClass("roo-tabs-bottom");
40383     }
40384     // next create the tabs holders
40385     
40386     if (this.tabPosition == "west"){
40387         
40388         var reg = this.region; // fake it..
40389         while (reg) {
40390             if (!reg.mgr.parent) {
40391                 break;
40392             }
40393             reg = reg.mgr.parent.region;
40394         }
40395         Roo.log("got nest?");
40396         Roo.log(reg);
40397         if (reg.mgr.getRegion('west')) {
40398             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40399             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40400             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40401             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40402             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40403         
40404             
40405         }
40406         
40407         
40408     } else {
40409      
40410         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40411         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40412         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40413         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40414     }
40415     
40416     
40417     if(Roo.isIE){
40418         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40419     }
40420     
40421     // finally - if tabs are at the top, then create the body last..
40422     if(this.tabPosition != "bottom"){
40423         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40424          * @type Roo.Element
40425          */
40426         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40427         this.el.addClass("roo-tabs-top");
40428     }
40429     this.items = [];
40430
40431     this.bodyEl.setStyle("position", "relative");
40432
40433     this.active = null;
40434     this.activateDelegate = this.activate.createDelegate(this);
40435
40436     this.addEvents({
40437         /**
40438          * @event tabchange
40439          * Fires when the active tab changes
40440          * @param {Roo.TabPanel} this
40441          * @param {Roo.TabPanelItem} activePanel The new active tab
40442          */
40443         "tabchange": true,
40444         /**
40445          * @event beforetabchange
40446          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40447          * @param {Roo.TabPanel} this
40448          * @param {Object} e Set cancel to true on this object to cancel the tab change
40449          * @param {Roo.TabPanelItem} tab The tab being changed to
40450          */
40451         "beforetabchange" : true
40452     });
40453
40454     Roo.EventManager.onWindowResize(this.onResize, this);
40455     this.cpad = this.el.getPadding("lr");
40456     this.hiddenCount = 0;
40457
40458
40459     // toolbar on the tabbar support...
40460     if (this.toolbar) {
40461         alert("no toolbar support yet");
40462         this.toolbar  = false;
40463         /*
40464         var tcfg = this.toolbar;
40465         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40466         this.toolbar = new Roo.Toolbar(tcfg);
40467         if (Roo.isSafari) {
40468             var tbl = tcfg.container.child('table', true);
40469             tbl.setAttribute('width', '100%');
40470         }
40471         */
40472         
40473     }
40474    
40475
40476
40477     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40478 };
40479
40480 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40481     /*
40482      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40483      */
40484     tabPosition : "top",
40485     /*
40486      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40487      */
40488     currentTabWidth : 0,
40489     /*
40490      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40491      */
40492     minTabWidth : 40,
40493     /*
40494      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40495      */
40496     maxTabWidth : 250,
40497     /*
40498      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40499      */
40500     preferredTabWidth : 175,
40501     /*
40502      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40503      */
40504     resizeTabs : false,
40505     /*
40506      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40507      */
40508     monitorResize : true,
40509     /*
40510      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40511      */
40512     toolbar : false,  // set by caller..
40513     
40514     region : false, /// set by caller
40515     
40516     disableTooltips : true, // not used yet...
40517
40518     /**
40519      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40520      * @param {String} id The id of the div to use <b>or create</b>
40521      * @param {String} text The text for the tab
40522      * @param {String} content (optional) Content to put in the TabPanelItem body
40523      * @param {Boolean} closable (optional) True to create a close icon on the tab
40524      * @return {Roo.TabPanelItem} The created TabPanelItem
40525      */
40526     addTab : function(id, text, content, closable, tpl)
40527     {
40528         var item = new Roo.bootstrap.panel.TabItem({
40529             panel: this,
40530             id : id,
40531             text : text,
40532             closable : closable,
40533             tpl : tpl
40534         });
40535         this.addTabItem(item);
40536         if(content){
40537             item.setContent(content);
40538         }
40539         return item;
40540     },
40541
40542     /**
40543      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40544      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40545      * @return {Roo.TabPanelItem}
40546      */
40547     getTab : function(id){
40548         return this.items[id];
40549     },
40550
40551     /**
40552      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40553      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40554      */
40555     hideTab : function(id){
40556         var t = this.items[id];
40557         if(!t.isHidden()){
40558            t.setHidden(true);
40559            this.hiddenCount++;
40560            this.autoSizeTabs();
40561         }
40562     },
40563
40564     /**
40565      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40566      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40567      */
40568     unhideTab : function(id){
40569         var t = this.items[id];
40570         if(t.isHidden()){
40571            t.setHidden(false);
40572            this.hiddenCount--;
40573            this.autoSizeTabs();
40574         }
40575     },
40576
40577     /**
40578      * Adds an existing {@link Roo.TabPanelItem}.
40579      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40580      */
40581     addTabItem : function(item)
40582     {
40583         this.items[item.id] = item;
40584         this.items.push(item);
40585         this.autoSizeTabs();
40586       //  if(this.resizeTabs){
40587     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40588   //         this.autoSizeTabs();
40589 //        }else{
40590 //            item.autoSize();
40591        // }
40592     },
40593
40594     /**
40595      * Removes a {@link Roo.TabPanelItem}.
40596      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40597      */
40598     removeTab : function(id){
40599         var items = this.items;
40600         var tab = items[id];
40601         if(!tab) { return; }
40602         var index = items.indexOf(tab);
40603         if(this.active == tab && items.length > 1){
40604             var newTab = this.getNextAvailable(index);
40605             if(newTab) {
40606                 newTab.activate();
40607             }
40608         }
40609         this.stripEl.dom.removeChild(tab.pnode.dom);
40610         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40611             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40612         }
40613         items.splice(index, 1);
40614         delete this.items[tab.id];
40615         tab.fireEvent("close", tab);
40616         tab.purgeListeners();
40617         this.autoSizeTabs();
40618     },
40619
40620     getNextAvailable : function(start){
40621         var items = this.items;
40622         var index = start;
40623         // look for a next tab that will slide over to
40624         // replace the one being removed
40625         while(index < items.length){
40626             var item = items[++index];
40627             if(item && !item.isHidden()){
40628                 return item;
40629             }
40630         }
40631         // if one isn't found select the previous tab (on the left)
40632         index = start;
40633         while(index >= 0){
40634             var item = items[--index];
40635             if(item && !item.isHidden()){
40636                 return item;
40637             }
40638         }
40639         return null;
40640     },
40641
40642     /**
40643      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40644      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40645      */
40646     disableTab : function(id){
40647         var tab = this.items[id];
40648         if(tab && this.active != tab){
40649             tab.disable();
40650         }
40651     },
40652
40653     /**
40654      * Enables a {@link Roo.TabPanelItem} that is disabled.
40655      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40656      */
40657     enableTab : function(id){
40658         var tab = this.items[id];
40659         tab.enable();
40660     },
40661
40662     /**
40663      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40664      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40665      * @return {Roo.TabPanelItem} The TabPanelItem.
40666      */
40667     activate : function(id)
40668     {
40669         //Roo.log('activite:'  + id);
40670         
40671         var tab = this.items[id];
40672         if(!tab){
40673             return null;
40674         }
40675         if(tab == this.active || tab.disabled){
40676             return tab;
40677         }
40678         var e = {};
40679         this.fireEvent("beforetabchange", this, e, tab);
40680         if(e.cancel !== true && !tab.disabled){
40681             if(this.active){
40682                 this.active.hide();
40683             }
40684             this.active = this.items[id];
40685             this.active.show();
40686             this.fireEvent("tabchange", this, this.active);
40687         }
40688         return tab;
40689     },
40690
40691     /**
40692      * Gets the active {@link Roo.TabPanelItem}.
40693      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40694      */
40695     getActiveTab : function(){
40696         return this.active;
40697     },
40698
40699     /**
40700      * Updates the tab body element to fit the height of the container element
40701      * for overflow scrolling
40702      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40703      */
40704     syncHeight : function(targetHeight){
40705         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40706         var bm = this.bodyEl.getMargins();
40707         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40708         this.bodyEl.setHeight(newHeight);
40709         return newHeight;
40710     },
40711
40712     onResize : function(){
40713         if(this.monitorResize){
40714             this.autoSizeTabs();
40715         }
40716     },
40717
40718     /**
40719      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40720      */
40721     beginUpdate : function(){
40722         this.updating = true;
40723     },
40724
40725     /**
40726      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40727      */
40728     endUpdate : function(){
40729         this.updating = false;
40730         this.autoSizeTabs();
40731     },
40732
40733     /**
40734      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40735      */
40736     autoSizeTabs : function()
40737     {
40738         var count = this.items.length;
40739         var vcount = count - this.hiddenCount;
40740         
40741         if (vcount < 2) {
40742             this.stripEl.hide();
40743         } else {
40744             this.stripEl.show();
40745         }
40746         
40747         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40748             return;
40749         }
40750         
40751         
40752         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40753         var availWidth = Math.floor(w / vcount);
40754         var b = this.stripBody;
40755         if(b.getWidth() > w){
40756             var tabs = this.items;
40757             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40758             if(availWidth < this.minTabWidth){
40759                 /*if(!this.sleft){    // incomplete scrolling code
40760                     this.createScrollButtons();
40761                 }
40762                 this.showScroll();
40763                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40764             }
40765         }else{
40766             if(this.currentTabWidth < this.preferredTabWidth){
40767                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40768             }
40769         }
40770     },
40771
40772     /**
40773      * Returns the number of tabs in this TabPanel.
40774      * @return {Number}
40775      */
40776      getCount : function(){
40777          return this.items.length;
40778      },
40779
40780     /**
40781      * Resizes all the tabs to the passed width
40782      * @param {Number} The new width
40783      */
40784     setTabWidth : function(width){
40785         this.currentTabWidth = width;
40786         for(var i = 0, len = this.items.length; i < len; i++) {
40787                 if(!this.items[i].isHidden()) {
40788                 this.items[i].setWidth(width);
40789             }
40790         }
40791     },
40792
40793     /**
40794      * Destroys this TabPanel
40795      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40796      */
40797     destroy : function(removeEl){
40798         Roo.EventManager.removeResizeListener(this.onResize, this);
40799         for(var i = 0, len = this.items.length; i < len; i++){
40800             this.items[i].purgeListeners();
40801         }
40802         if(removeEl === true){
40803             this.el.update("");
40804             this.el.remove();
40805         }
40806     },
40807     
40808     createStrip : function(container)
40809     {
40810         var strip = document.createElement("nav");
40811         strip.className = Roo.bootstrap.version == 4 ?
40812             "navbar-light bg-light" : 
40813             "navbar navbar-default"; //"x-tabs-wrap";
40814         container.appendChild(strip);
40815         return strip;
40816     },
40817     
40818     createStripList : function(strip)
40819     {
40820         // div wrapper for retard IE
40821         // returns the "tr" element.
40822         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40823         //'<div class="x-tabs-strip-wrap">'+
40824           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40825           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40826         return strip.firstChild; //.firstChild.firstChild.firstChild;
40827     },
40828     createBody : function(container)
40829     {
40830         var body = document.createElement("div");
40831         Roo.id(body, "tab-body");
40832         //Roo.fly(body).addClass("x-tabs-body");
40833         Roo.fly(body).addClass("tab-content");
40834         container.appendChild(body);
40835         return body;
40836     },
40837     createItemBody :function(bodyEl, id){
40838         var body = Roo.getDom(id);
40839         if(!body){
40840             body = document.createElement("div");
40841             body.id = id;
40842         }
40843         //Roo.fly(body).addClass("x-tabs-item-body");
40844         Roo.fly(body).addClass("tab-pane");
40845          bodyEl.insertBefore(body, bodyEl.firstChild);
40846         return body;
40847     },
40848     /** @private */
40849     createStripElements :  function(stripEl, text, closable, tpl)
40850     {
40851         var td = document.createElement("li"); // was td..
40852         td.className = 'nav-item';
40853         
40854         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40855         
40856         
40857         stripEl.appendChild(td);
40858         /*if(closable){
40859             td.className = "x-tabs-closable";
40860             if(!this.closeTpl){
40861                 this.closeTpl = new Roo.Template(
40862                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40863                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40864                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40865                 );
40866             }
40867             var el = this.closeTpl.overwrite(td, {"text": text});
40868             var close = el.getElementsByTagName("div")[0];
40869             var inner = el.getElementsByTagName("em")[0];
40870             return {"el": el, "close": close, "inner": inner};
40871         } else {
40872         */
40873         // not sure what this is..
40874 //            if(!this.tabTpl){
40875                 //this.tabTpl = new Roo.Template(
40876                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40877                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40878                 //);
40879 //                this.tabTpl = new Roo.Template(
40880 //                   '<a href="#">' +
40881 //                   '<span unselectable="on"' +
40882 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40883 //                            ' >{text}</span></a>'
40884 //                );
40885 //                
40886 //            }
40887
40888
40889             var template = tpl || this.tabTpl || false;
40890             
40891             if(!template){
40892                 template =  new Roo.Template(
40893                         Roo.bootstrap.version == 4 ? 
40894                             (
40895                                 '<a class="nav-link" href="#" unselectable="on"' +
40896                                      (this.disableTooltips ? '' : ' title="{text}"') +
40897                                      ' >{text}</a>'
40898                             ) : (
40899                                 '<a class="nav-link" href="#">' +
40900                                 '<span unselectable="on"' +
40901                                          (this.disableTooltips ? '' : ' title="{text}"') +
40902                                     ' >{text}</span></a>'
40903                             )
40904                 );
40905             }
40906             
40907             switch (typeof(template)) {
40908                 case 'object' :
40909                     break;
40910                 case 'string' :
40911                     template = new Roo.Template(template);
40912                     break;
40913                 default :
40914                     break;
40915             }
40916             
40917             var el = template.overwrite(td, {"text": text});
40918             
40919             var inner = el.getElementsByTagName("span")[0];
40920             
40921             return {"el": el, "inner": inner};
40922             
40923     }
40924         
40925     
40926 });
40927
40928 /**
40929  * @class Roo.TabPanelItem
40930  * @extends Roo.util.Observable
40931  * Represents an individual item (tab plus body) in a TabPanel.
40932  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40933  * @param {String} id The id of this TabPanelItem
40934  * @param {String} text The text for the tab of this TabPanelItem
40935  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40936  */
40937 Roo.bootstrap.panel.TabItem = function(config){
40938     /**
40939      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40940      * @type Roo.TabPanel
40941      */
40942     this.tabPanel = config.panel;
40943     /**
40944      * The id for this TabPanelItem
40945      * @type String
40946      */
40947     this.id = config.id;
40948     /** @private */
40949     this.disabled = false;
40950     /** @private */
40951     this.text = config.text;
40952     /** @private */
40953     this.loaded = false;
40954     this.closable = config.closable;
40955
40956     /**
40957      * The body element for this TabPanelItem.
40958      * @type Roo.Element
40959      */
40960     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40961     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40962     this.bodyEl.setStyle("display", "block");
40963     this.bodyEl.setStyle("zoom", "1");
40964     //this.hideAction();
40965
40966     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40967     /** @private */
40968     this.el = Roo.get(els.el);
40969     this.inner = Roo.get(els.inner, true);
40970      this.textEl = Roo.bootstrap.version == 4 ?
40971         this.el : Roo.get(this.el.dom.firstChild, true);
40972
40973     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40974     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40975
40976     
40977 //    this.el.on("mousedown", this.onTabMouseDown, this);
40978     this.el.on("click", this.onTabClick, this);
40979     /** @private */
40980     if(config.closable){
40981         var c = Roo.get(els.close, true);
40982         c.dom.title = this.closeText;
40983         c.addClassOnOver("close-over");
40984         c.on("click", this.closeClick, this);
40985      }
40986
40987     this.addEvents({
40988          /**
40989          * @event activate
40990          * Fires when this tab becomes the active tab.
40991          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40992          * @param {Roo.TabPanelItem} this
40993          */
40994         "activate": true,
40995         /**
40996          * @event beforeclose
40997          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40998          * @param {Roo.TabPanelItem} this
40999          * @param {Object} e Set cancel to true on this object to cancel the close.
41000          */
41001         "beforeclose": true,
41002         /**
41003          * @event close
41004          * Fires when this tab is closed.
41005          * @param {Roo.TabPanelItem} this
41006          */
41007          "close": true,
41008         /**
41009          * @event deactivate
41010          * Fires when this tab is no longer the active tab.
41011          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41012          * @param {Roo.TabPanelItem} this
41013          */
41014          "deactivate" : true
41015     });
41016     this.hidden = false;
41017
41018     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41019 };
41020
41021 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41022            {
41023     purgeListeners : function(){
41024        Roo.util.Observable.prototype.purgeListeners.call(this);
41025        this.el.removeAllListeners();
41026     },
41027     /**
41028      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41029      */
41030     show : function(){
41031         this.status_node.addClass("active");
41032         this.showAction();
41033         if(Roo.isOpera){
41034             this.tabPanel.stripWrap.repaint();
41035         }
41036         this.fireEvent("activate", this.tabPanel, this);
41037     },
41038
41039     /**
41040      * Returns true if this tab is the active tab.
41041      * @return {Boolean}
41042      */
41043     isActive : function(){
41044         return this.tabPanel.getActiveTab() == this;
41045     },
41046
41047     /**
41048      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41049      */
41050     hide : function(){
41051         this.status_node.removeClass("active");
41052         this.hideAction();
41053         this.fireEvent("deactivate", this.tabPanel, this);
41054     },
41055
41056     hideAction : function(){
41057         this.bodyEl.hide();
41058         this.bodyEl.setStyle("position", "absolute");
41059         this.bodyEl.setLeft("-20000px");
41060         this.bodyEl.setTop("-20000px");
41061     },
41062
41063     showAction : function(){
41064         this.bodyEl.setStyle("position", "relative");
41065         this.bodyEl.setTop("");
41066         this.bodyEl.setLeft("");
41067         this.bodyEl.show();
41068     },
41069
41070     /**
41071      * Set the tooltip for the tab.
41072      * @param {String} tooltip The tab's tooltip
41073      */
41074     setTooltip : function(text){
41075         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41076             this.textEl.dom.qtip = text;
41077             this.textEl.dom.removeAttribute('title');
41078         }else{
41079             this.textEl.dom.title = text;
41080         }
41081     },
41082
41083     onTabClick : function(e){
41084         e.preventDefault();
41085         this.tabPanel.activate(this.id);
41086     },
41087
41088     onTabMouseDown : function(e){
41089         e.preventDefault();
41090         this.tabPanel.activate(this.id);
41091     },
41092 /*
41093     getWidth : function(){
41094         return this.inner.getWidth();
41095     },
41096
41097     setWidth : function(width){
41098         var iwidth = width - this.linode.getPadding("lr");
41099         this.inner.setWidth(iwidth);
41100         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41101         this.linode.setWidth(width);
41102     },
41103 */
41104     /**
41105      * Show or hide the tab
41106      * @param {Boolean} hidden True to hide or false to show.
41107      */
41108     setHidden : function(hidden){
41109         this.hidden = hidden;
41110         this.linode.setStyle("display", hidden ? "none" : "");
41111     },
41112
41113     /**
41114      * Returns true if this tab is "hidden"
41115      * @return {Boolean}
41116      */
41117     isHidden : function(){
41118         return this.hidden;
41119     },
41120
41121     /**
41122      * Returns the text for this tab
41123      * @return {String}
41124      */
41125     getText : function(){
41126         return this.text;
41127     },
41128     /*
41129     autoSize : function(){
41130         //this.el.beginMeasure();
41131         this.textEl.setWidth(1);
41132         /*
41133          *  #2804 [new] Tabs in Roojs
41134          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41135          */
41136         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41137         //this.el.endMeasure();
41138     //},
41139
41140     /**
41141      * Sets the text for the tab (Note: this also sets the tooltip text)
41142      * @param {String} text The tab's text and tooltip
41143      */
41144     setText : function(text){
41145         this.text = text;
41146         this.textEl.update(text);
41147         this.setTooltip(text);
41148         //if(!this.tabPanel.resizeTabs){
41149         //    this.autoSize();
41150         //}
41151     },
41152     /**
41153      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41154      */
41155     activate : function(){
41156         this.tabPanel.activate(this.id);
41157     },
41158
41159     /**
41160      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41161      */
41162     disable : function(){
41163         if(this.tabPanel.active != this){
41164             this.disabled = true;
41165             this.status_node.addClass("disabled");
41166         }
41167     },
41168
41169     /**
41170      * Enables this TabPanelItem if it was previously disabled.
41171      */
41172     enable : function(){
41173         this.disabled = false;
41174         this.status_node.removeClass("disabled");
41175     },
41176
41177     /**
41178      * Sets the content for this TabPanelItem.
41179      * @param {String} content The content
41180      * @param {Boolean} loadScripts true to look for and load scripts
41181      */
41182     setContent : function(content, loadScripts){
41183         this.bodyEl.update(content, loadScripts);
41184     },
41185
41186     /**
41187      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41188      * @return {Roo.UpdateManager} The UpdateManager
41189      */
41190     getUpdateManager : function(){
41191         return this.bodyEl.getUpdateManager();
41192     },
41193
41194     /**
41195      * Set a URL to be used to load the content for this TabPanelItem.
41196      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41197      * @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)
41198      * @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)
41199      * @return {Roo.UpdateManager} The UpdateManager
41200      */
41201     setUrl : function(url, params, loadOnce){
41202         if(this.refreshDelegate){
41203             this.un('activate', this.refreshDelegate);
41204         }
41205         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41206         this.on("activate", this.refreshDelegate);
41207         return this.bodyEl.getUpdateManager();
41208     },
41209
41210     /** @private */
41211     _handleRefresh : function(url, params, loadOnce){
41212         if(!loadOnce || !this.loaded){
41213             var updater = this.bodyEl.getUpdateManager();
41214             updater.update(url, params, this._setLoaded.createDelegate(this));
41215         }
41216     },
41217
41218     /**
41219      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41220      *   Will fail silently if the setUrl method has not been called.
41221      *   This does not activate the panel, just updates its content.
41222      */
41223     refresh : function(){
41224         if(this.refreshDelegate){
41225            this.loaded = false;
41226            this.refreshDelegate();
41227         }
41228     },
41229
41230     /** @private */
41231     _setLoaded : function(){
41232         this.loaded = true;
41233     },
41234
41235     /** @private */
41236     closeClick : function(e){
41237         var o = {};
41238         e.stopEvent();
41239         this.fireEvent("beforeclose", this, o);
41240         if(o.cancel !== true){
41241             this.tabPanel.removeTab(this.id);
41242         }
41243     },
41244     /**
41245      * The text displayed in the tooltip for the close icon.
41246      * @type String
41247      */
41248     closeText : "Close this tab"
41249 });
41250 /**
41251 *    This script refer to:
41252 *    Title: International Telephone Input
41253 *    Author: Jack O'Connor
41254 *    Code version:  v12.1.12
41255 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41256 **/
41257
41258 Roo.bootstrap.PhoneInputData = function() {
41259     var d = [
41260       [
41261         "Afghanistan (‫افغانستان‬‎)",
41262         "af",
41263         "93"
41264       ],
41265       [
41266         "Albania (Shqipëri)",
41267         "al",
41268         "355"
41269       ],
41270       [
41271         "Algeria (‫الجزائر‬‎)",
41272         "dz",
41273         "213"
41274       ],
41275       [
41276         "American Samoa",
41277         "as",
41278         "1684"
41279       ],
41280       [
41281         "Andorra",
41282         "ad",
41283         "376"
41284       ],
41285       [
41286         "Angola",
41287         "ao",
41288         "244"
41289       ],
41290       [
41291         "Anguilla",
41292         "ai",
41293         "1264"
41294       ],
41295       [
41296         "Antigua and Barbuda",
41297         "ag",
41298         "1268"
41299       ],
41300       [
41301         "Argentina",
41302         "ar",
41303         "54"
41304       ],
41305       [
41306         "Armenia (Հայաստան)",
41307         "am",
41308         "374"
41309       ],
41310       [
41311         "Aruba",
41312         "aw",
41313         "297"
41314       ],
41315       [
41316         "Australia",
41317         "au",
41318         "61",
41319         0
41320       ],
41321       [
41322         "Austria (Österreich)",
41323         "at",
41324         "43"
41325       ],
41326       [
41327         "Azerbaijan (Azərbaycan)",
41328         "az",
41329         "994"
41330       ],
41331       [
41332         "Bahamas",
41333         "bs",
41334         "1242"
41335       ],
41336       [
41337         "Bahrain (‫البحرين‬‎)",
41338         "bh",
41339         "973"
41340       ],
41341       [
41342         "Bangladesh (বাংলাদেশ)",
41343         "bd",
41344         "880"
41345       ],
41346       [
41347         "Barbados",
41348         "bb",
41349         "1246"
41350       ],
41351       [
41352         "Belarus (Беларусь)",
41353         "by",
41354         "375"
41355       ],
41356       [
41357         "Belgium (België)",
41358         "be",
41359         "32"
41360       ],
41361       [
41362         "Belize",
41363         "bz",
41364         "501"
41365       ],
41366       [
41367         "Benin (Bénin)",
41368         "bj",
41369         "229"
41370       ],
41371       [
41372         "Bermuda",
41373         "bm",
41374         "1441"
41375       ],
41376       [
41377         "Bhutan (འབྲུག)",
41378         "bt",
41379         "975"
41380       ],
41381       [
41382         "Bolivia",
41383         "bo",
41384         "591"
41385       ],
41386       [
41387         "Bosnia and Herzegovina (Босна и Херцеговина)",
41388         "ba",
41389         "387"
41390       ],
41391       [
41392         "Botswana",
41393         "bw",
41394         "267"
41395       ],
41396       [
41397         "Brazil (Brasil)",
41398         "br",
41399         "55"
41400       ],
41401       [
41402         "British Indian Ocean Territory",
41403         "io",
41404         "246"
41405       ],
41406       [
41407         "British Virgin Islands",
41408         "vg",
41409         "1284"
41410       ],
41411       [
41412         "Brunei",
41413         "bn",
41414         "673"
41415       ],
41416       [
41417         "Bulgaria (България)",
41418         "bg",
41419         "359"
41420       ],
41421       [
41422         "Burkina Faso",
41423         "bf",
41424         "226"
41425       ],
41426       [
41427         "Burundi (Uburundi)",
41428         "bi",
41429         "257"
41430       ],
41431       [
41432         "Cambodia (កម្ពុជា)",
41433         "kh",
41434         "855"
41435       ],
41436       [
41437         "Cameroon (Cameroun)",
41438         "cm",
41439         "237"
41440       ],
41441       [
41442         "Canada",
41443         "ca",
41444         "1",
41445         1,
41446         ["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"]
41447       ],
41448       [
41449         "Cape Verde (Kabu Verdi)",
41450         "cv",
41451         "238"
41452       ],
41453       [
41454         "Caribbean Netherlands",
41455         "bq",
41456         "599",
41457         1
41458       ],
41459       [
41460         "Cayman Islands",
41461         "ky",
41462         "1345"
41463       ],
41464       [
41465         "Central African Republic (République centrafricaine)",
41466         "cf",
41467         "236"
41468       ],
41469       [
41470         "Chad (Tchad)",
41471         "td",
41472         "235"
41473       ],
41474       [
41475         "Chile",
41476         "cl",
41477         "56"
41478       ],
41479       [
41480         "China (中国)",
41481         "cn",
41482         "86"
41483       ],
41484       [
41485         "Christmas Island",
41486         "cx",
41487         "61",
41488         2
41489       ],
41490       [
41491         "Cocos (Keeling) Islands",
41492         "cc",
41493         "61",
41494         1
41495       ],
41496       [
41497         "Colombia",
41498         "co",
41499         "57"
41500       ],
41501       [
41502         "Comoros (‫جزر القمر‬‎)",
41503         "km",
41504         "269"
41505       ],
41506       [
41507         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41508         "cd",
41509         "243"
41510       ],
41511       [
41512         "Congo (Republic) (Congo-Brazzaville)",
41513         "cg",
41514         "242"
41515       ],
41516       [
41517         "Cook Islands",
41518         "ck",
41519         "682"
41520       ],
41521       [
41522         "Costa Rica",
41523         "cr",
41524         "506"
41525       ],
41526       [
41527         "Côte d’Ivoire",
41528         "ci",
41529         "225"
41530       ],
41531       [
41532         "Croatia (Hrvatska)",
41533         "hr",
41534         "385"
41535       ],
41536       [
41537         "Cuba",
41538         "cu",
41539         "53"
41540       ],
41541       [
41542         "Curaçao",
41543         "cw",
41544         "599",
41545         0
41546       ],
41547       [
41548         "Cyprus (Κύπρος)",
41549         "cy",
41550         "357"
41551       ],
41552       [
41553         "Czech Republic (Česká republika)",
41554         "cz",
41555         "420"
41556       ],
41557       [
41558         "Denmark (Danmark)",
41559         "dk",
41560         "45"
41561       ],
41562       [
41563         "Djibouti",
41564         "dj",
41565         "253"
41566       ],
41567       [
41568         "Dominica",
41569         "dm",
41570         "1767"
41571       ],
41572       [
41573         "Dominican Republic (República Dominicana)",
41574         "do",
41575         "1",
41576         2,
41577         ["809", "829", "849"]
41578       ],
41579       [
41580         "Ecuador",
41581         "ec",
41582         "593"
41583       ],
41584       [
41585         "Egypt (‫مصر‬‎)",
41586         "eg",
41587         "20"
41588       ],
41589       [
41590         "El Salvador",
41591         "sv",
41592         "503"
41593       ],
41594       [
41595         "Equatorial Guinea (Guinea Ecuatorial)",
41596         "gq",
41597         "240"
41598       ],
41599       [
41600         "Eritrea",
41601         "er",
41602         "291"
41603       ],
41604       [
41605         "Estonia (Eesti)",
41606         "ee",
41607         "372"
41608       ],
41609       [
41610         "Ethiopia",
41611         "et",
41612         "251"
41613       ],
41614       [
41615         "Falkland Islands (Islas Malvinas)",
41616         "fk",
41617         "500"
41618       ],
41619       [
41620         "Faroe Islands (Føroyar)",
41621         "fo",
41622         "298"
41623       ],
41624       [
41625         "Fiji",
41626         "fj",
41627         "679"
41628       ],
41629       [
41630         "Finland (Suomi)",
41631         "fi",
41632         "358",
41633         0
41634       ],
41635       [
41636         "France",
41637         "fr",
41638         "33"
41639       ],
41640       [
41641         "French Guiana (Guyane française)",
41642         "gf",
41643         "594"
41644       ],
41645       [
41646         "French Polynesia (Polynésie française)",
41647         "pf",
41648         "689"
41649       ],
41650       [
41651         "Gabon",
41652         "ga",
41653         "241"
41654       ],
41655       [
41656         "Gambia",
41657         "gm",
41658         "220"
41659       ],
41660       [
41661         "Georgia (საქართველო)",
41662         "ge",
41663         "995"
41664       ],
41665       [
41666         "Germany (Deutschland)",
41667         "de",
41668         "49"
41669       ],
41670       [
41671         "Ghana (Gaana)",
41672         "gh",
41673         "233"
41674       ],
41675       [
41676         "Gibraltar",
41677         "gi",
41678         "350"
41679       ],
41680       [
41681         "Greece (Ελλάδα)",
41682         "gr",
41683         "30"
41684       ],
41685       [
41686         "Greenland (Kalaallit Nunaat)",
41687         "gl",
41688         "299"
41689       ],
41690       [
41691         "Grenada",
41692         "gd",
41693         "1473"
41694       ],
41695       [
41696         "Guadeloupe",
41697         "gp",
41698         "590",
41699         0
41700       ],
41701       [
41702         "Guam",
41703         "gu",
41704         "1671"
41705       ],
41706       [
41707         "Guatemala",
41708         "gt",
41709         "502"
41710       ],
41711       [
41712         "Guernsey",
41713         "gg",
41714         "44",
41715         1
41716       ],
41717       [
41718         "Guinea (Guinée)",
41719         "gn",
41720         "224"
41721       ],
41722       [
41723         "Guinea-Bissau (Guiné Bissau)",
41724         "gw",
41725         "245"
41726       ],
41727       [
41728         "Guyana",
41729         "gy",
41730         "592"
41731       ],
41732       [
41733         "Haiti",
41734         "ht",
41735         "509"
41736       ],
41737       [
41738         "Honduras",
41739         "hn",
41740         "504"
41741       ],
41742       [
41743         "Hong Kong (香港)",
41744         "hk",
41745         "852"
41746       ],
41747       [
41748         "Hungary (Magyarország)",
41749         "hu",
41750         "36"
41751       ],
41752       [
41753         "Iceland (Ísland)",
41754         "is",
41755         "354"
41756       ],
41757       [
41758         "India (भारत)",
41759         "in",
41760         "91"
41761       ],
41762       [
41763         "Indonesia",
41764         "id",
41765         "62"
41766       ],
41767       [
41768         "Iran (‫ایران‬‎)",
41769         "ir",
41770         "98"
41771       ],
41772       [
41773         "Iraq (‫العراق‬‎)",
41774         "iq",
41775         "964"
41776       ],
41777       [
41778         "Ireland",
41779         "ie",
41780         "353"
41781       ],
41782       [
41783         "Isle of Man",
41784         "im",
41785         "44",
41786         2
41787       ],
41788       [
41789         "Israel (‫ישראל‬‎)",
41790         "il",
41791         "972"
41792       ],
41793       [
41794         "Italy (Italia)",
41795         "it",
41796         "39",
41797         0
41798       ],
41799       [
41800         "Jamaica",
41801         "jm",
41802         "1876"
41803       ],
41804       [
41805         "Japan (日本)",
41806         "jp",
41807         "81"
41808       ],
41809       [
41810         "Jersey",
41811         "je",
41812         "44",
41813         3
41814       ],
41815       [
41816         "Jordan (‫الأردن‬‎)",
41817         "jo",
41818         "962"
41819       ],
41820       [
41821         "Kazakhstan (Казахстан)",
41822         "kz",
41823         "7",
41824         1
41825       ],
41826       [
41827         "Kenya",
41828         "ke",
41829         "254"
41830       ],
41831       [
41832         "Kiribati",
41833         "ki",
41834         "686"
41835       ],
41836       [
41837         "Kosovo",
41838         "xk",
41839         "383"
41840       ],
41841       [
41842         "Kuwait (‫الكويت‬‎)",
41843         "kw",
41844         "965"
41845       ],
41846       [
41847         "Kyrgyzstan (Кыргызстан)",
41848         "kg",
41849         "996"
41850       ],
41851       [
41852         "Laos (ລາວ)",
41853         "la",
41854         "856"
41855       ],
41856       [
41857         "Latvia (Latvija)",
41858         "lv",
41859         "371"
41860       ],
41861       [
41862         "Lebanon (‫لبنان‬‎)",
41863         "lb",
41864         "961"
41865       ],
41866       [
41867         "Lesotho",
41868         "ls",
41869         "266"
41870       ],
41871       [
41872         "Liberia",
41873         "lr",
41874         "231"
41875       ],
41876       [
41877         "Libya (‫ليبيا‬‎)",
41878         "ly",
41879         "218"
41880       ],
41881       [
41882         "Liechtenstein",
41883         "li",
41884         "423"
41885       ],
41886       [
41887         "Lithuania (Lietuva)",
41888         "lt",
41889         "370"
41890       ],
41891       [
41892         "Luxembourg",
41893         "lu",
41894         "352"
41895       ],
41896       [
41897         "Macau (澳門)",
41898         "mo",
41899         "853"
41900       ],
41901       [
41902         "Macedonia (FYROM) (Македонија)",
41903         "mk",
41904         "389"
41905       ],
41906       [
41907         "Madagascar (Madagasikara)",
41908         "mg",
41909         "261"
41910       ],
41911       [
41912         "Malawi",
41913         "mw",
41914         "265"
41915       ],
41916       [
41917         "Malaysia",
41918         "my",
41919         "60"
41920       ],
41921       [
41922         "Maldives",
41923         "mv",
41924         "960"
41925       ],
41926       [
41927         "Mali",
41928         "ml",
41929         "223"
41930       ],
41931       [
41932         "Malta",
41933         "mt",
41934         "356"
41935       ],
41936       [
41937         "Marshall Islands",
41938         "mh",
41939         "692"
41940       ],
41941       [
41942         "Martinique",
41943         "mq",
41944         "596"
41945       ],
41946       [
41947         "Mauritania (‫موريتانيا‬‎)",
41948         "mr",
41949         "222"
41950       ],
41951       [
41952         "Mauritius (Moris)",
41953         "mu",
41954         "230"
41955       ],
41956       [
41957         "Mayotte",
41958         "yt",
41959         "262",
41960         1
41961       ],
41962       [
41963         "Mexico (México)",
41964         "mx",
41965         "52"
41966       ],
41967       [
41968         "Micronesia",
41969         "fm",
41970         "691"
41971       ],
41972       [
41973         "Moldova (Republica Moldova)",
41974         "md",
41975         "373"
41976       ],
41977       [
41978         "Monaco",
41979         "mc",
41980         "377"
41981       ],
41982       [
41983         "Mongolia (Монгол)",
41984         "mn",
41985         "976"
41986       ],
41987       [
41988         "Montenegro (Crna Gora)",
41989         "me",
41990         "382"
41991       ],
41992       [
41993         "Montserrat",
41994         "ms",
41995         "1664"
41996       ],
41997       [
41998         "Morocco (‫المغرب‬‎)",
41999         "ma",
42000         "212",
42001         0
42002       ],
42003       [
42004         "Mozambique (Moçambique)",
42005         "mz",
42006         "258"
42007       ],
42008       [
42009         "Myanmar (Burma) (မြန်မာ)",
42010         "mm",
42011         "95"
42012       ],
42013       [
42014         "Namibia (Namibië)",
42015         "na",
42016         "264"
42017       ],
42018       [
42019         "Nauru",
42020         "nr",
42021         "674"
42022       ],
42023       [
42024         "Nepal (नेपाल)",
42025         "np",
42026         "977"
42027       ],
42028       [
42029         "Netherlands (Nederland)",
42030         "nl",
42031         "31"
42032       ],
42033       [
42034         "New Caledonia (Nouvelle-Calédonie)",
42035         "nc",
42036         "687"
42037       ],
42038       [
42039         "New Zealand",
42040         "nz",
42041         "64"
42042       ],
42043       [
42044         "Nicaragua",
42045         "ni",
42046         "505"
42047       ],
42048       [
42049         "Niger (Nijar)",
42050         "ne",
42051         "227"
42052       ],
42053       [
42054         "Nigeria",
42055         "ng",
42056         "234"
42057       ],
42058       [
42059         "Niue",
42060         "nu",
42061         "683"
42062       ],
42063       [
42064         "Norfolk Island",
42065         "nf",
42066         "672"
42067       ],
42068       [
42069         "North Korea (조선 민주주의 인민 공화국)",
42070         "kp",
42071         "850"
42072       ],
42073       [
42074         "Northern Mariana Islands",
42075         "mp",
42076         "1670"
42077       ],
42078       [
42079         "Norway (Norge)",
42080         "no",
42081         "47",
42082         0
42083       ],
42084       [
42085         "Oman (‫عُمان‬‎)",
42086         "om",
42087         "968"
42088       ],
42089       [
42090         "Pakistan (‫پاکستان‬‎)",
42091         "pk",
42092         "92"
42093       ],
42094       [
42095         "Palau",
42096         "pw",
42097         "680"
42098       ],
42099       [
42100         "Palestine (‫فلسطين‬‎)",
42101         "ps",
42102         "970"
42103       ],
42104       [
42105         "Panama (Panamá)",
42106         "pa",
42107         "507"
42108       ],
42109       [
42110         "Papua New Guinea",
42111         "pg",
42112         "675"
42113       ],
42114       [
42115         "Paraguay",
42116         "py",
42117         "595"
42118       ],
42119       [
42120         "Peru (Perú)",
42121         "pe",
42122         "51"
42123       ],
42124       [
42125         "Philippines",
42126         "ph",
42127         "63"
42128       ],
42129       [
42130         "Poland (Polska)",
42131         "pl",
42132         "48"
42133       ],
42134       [
42135         "Portugal",
42136         "pt",
42137         "351"
42138       ],
42139       [
42140         "Puerto Rico",
42141         "pr",
42142         "1",
42143         3,
42144         ["787", "939"]
42145       ],
42146       [
42147         "Qatar (‫قطر‬‎)",
42148         "qa",
42149         "974"
42150       ],
42151       [
42152         "Réunion (La Réunion)",
42153         "re",
42154         "262",
42155         0
42156       ],
42157       [
42158         "Romania (România)",
42159         "ro",
42160         "40"
42161       ],
42162       [
42163         "Russia (Россия)",
42164         "ru",
42165         "7",
42166         0
42167       ],
42168       [
42169         "Rwanda",
42170         "rw",
42171         "250"
42172       ],
42173       [
42174         "Saint Barthélemy",
42175         "bl",
42176         "590",
42177         1
42178       ],
42179       [
42180         "Saint Helena",
42181         "sh",
42182         "290"
42183       ],
42184       [
42185         "Saint Kitts and Nevis",
42186         "kn",
42187         "1869"
42188       ],
42189       [
42190         "Saint Lucia",
42191         "lc",
42192         "1758"
42193       ],
42194       [
42195         "Saint Martin (Saint-Martin (partie française))",
42196         "mf",
42197         "590",
42198         2
42199       ],
42200       [
42201         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42202         "pm",
42203         "508"
42204       ],
42205       [
42206         "Saint Vincent and the Grenadines",
42207         "vc",
42208         "1784"
42209       ],
42210       [
42211         "Samoa",
42212         "ws",
42213         "685"
42214       ],
42215       [
42216         "San Marino",
42217         "sm",
42218         "378"
42219       ],
42220       [
42221         "São Tomé and Príncipe (São Tomé e Príncipe)",
42222         "st",
42223         "239"
42224       ],
42225       [
42226         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42227         "sa",
42228         "966"
42229       ],
42230       [
42231         "Senegal (Sénégal)",
42232         "sn",
42233         "221"
42234       ],
42235       [
42236         "Serbia (Србија)",
42237         "rs",
42238         "381"
42239       ],
42240       [
42241         "Seychelles",
42242         "sc",
42243         "248"
42244       ],
42245       [
42246         "Sierra Leone",
42247         "sl",
42248         "232"
42249       ],
42250       [
42251         "Singapore",
42252         "sg",
42253         "65"
42254       ],
42255       [
42256         "Sint Maarten",
42257         "sx",
42258         "1721"
42259       ],
42260       [
42261         "Slovakia (Slovensko)",
42262         "sk",
42263         "421"
42264       ],
42265       [
42266         "Slovenia (Slovenija)",
42267         "si",
42268         "386"
42269       ],
42270       [
42271         "Solomon Islands",
42272         "sb",
42273         "677"
42274       ],
42275       [
42276         "Somalia (Soomaaliya)",
42277         "so",
42278         "252"
42279       ],
42280       [
42281         "South Africa",
42282         "za",
42283         "27"
42284       ],
42285       [
42286         "South Korea (대한민국)",
42287         "kr",
42288         "82"
42289       ],
42290       [
42291         "South Sudan (‫جنوب السودان‬‎)",
42292         "ss",
42293         "211"
42294       ],
42295       [
42296         "Spain (España)",
42297         "es",
42298         "34"
42299       ],
42300       [
42301         "Sri Lanka (ශ්‍රී ලංකාව)",
42302         "lk",
42303         "94"
42304       ],
42305       [
42306         "Sudan (‫السودان‬‎)",
42307         "sd",
42308         "249"
42309       ],
42310       [
42311         "Suriname",
42312         "sr",
42313         "597"
42314       ],
42315       [
42316         "Svalbard and Jan Mayen",
42317         "sj",
42318         "47",
42319         1
42320       ],
42321       [
42322         "Swaziland",
42323         "sz",
42324         "268"
42325       ],
42326       [
42327         "Sweden (Sverige)",
42328         "se",
42329         "46"
42330       ],
42331       [
42332         "Switzerland (Schweiz)",
42333         "ch",
42334         "41"
42335       ],
42336       [
42337         "Syria (‫سوريا‬‎)",
42338         "sy",
42339         "963"
42340       ],
42341       [
42342         "Taiwan (台灣)",
42343         "tw",
42344         "886"
42345       ],
42346       [
42347         "Tajikistan",
42348         "tj",
42349         "992"
42350       ],
42351       [
42352         "Tanzania",
42353         "tz",
42354         "255"
42355       ],
42356       [
42357         "Thailand (ไทย)",
42358         "th",
42359         "66"
42360       ],
42361       [
42362         "Timor-Leste",
42363         "tl",
42364         "670"
42365       ],
42366       [
42367         "Togo",
42368         "tg",
42369         "228"
42370       ],
42371       [
42372         "Tokelau",
42373         "tk",
42374         "690"
42375       ],
42376       [
42377         "Tonga",
42378         "to",
42379         "676"
42380       ],
42381       [
42382         "Trinidad and Tobago",
42383         "tt",
42384         "1868"
42385       ],
42386       [
42387         "Tunisia (‫تونس‬‎)",
42388         "tn",
42389         "216"
42390       ],
42391       [
42392         "Turkey (Türkiye)",
42393         "tr",
42394         "90"
42395       ],
42396       [
42397         "Turkmenistan",
42398         "tm",
42399         "993"
42400       ],
42401       [
42402         "Turks and Caicos Islands",
42403         "tc",
42404         "1649"
42405       ],
42406       [
42407         "Tuvalu",
42408         "tv",
42409         "688"
42410       ],
42411       [
42412         "U.S. Virgin Islands",
42413         "vi",
42414         "1340"
42415       ],
42416       [
42417         "Uganda",
42418         "ug",
42419         "256"
42420       ],
42421       [
42422         "Ukraine (Україна)",
42423         "ua",
42424         "380"
42425       ],
42426       [
42427         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42428         "ae",
42429         "971"
42430       ],
42431       [
42432         "United Kingdom",
42433         "gb",
42434         "44",
42435         0
42436       ],
42437       [
42438         "United States",
42439         "us",
42440         "1",
42441         0
42442       ],
42443       [
42444         "Uruguay",
42445         "uy",
42446         "598"
42447       ],
42448       [
42449         "Uzbekistan (Oʻzbekiston)",
42450         "uz",
42451         "998"
42452       ],
42453       [
42454         "Vanuatu",
42455         "vu",
42456         "678"
42457       ],
42458       [
42459         "Vatican City (Città del Vaticano)",
42460         "va",
42461         "39",
42462         1
42463       ],
42464       [
42465         "Venezuela",
42466         "ve",
42467         "58"
42468       ],
42469       [
42470         "Vietnam (Việt Nam)",
42471         "vn",
42472         "84"
42473       ],
42474       [
42475         "Wallis and Futuna (Wallis-et-Futuna)",
42476         "wf",
42477         "681"
42478       ],
42479       [
42480         "Western Sahara (‫الصحراء الغربية‬‎)",
42481         "eh",
42482         "212",
42483         1
42484       ],
42485       [
42486         "Yemen (‫اليمن‬‎)",
42487         "ye",
42488         "967"
42489       ],
42490       [
42491         "Zambia",
42492         "zm",
42493         "260"
42494       ],
42495       [
42496         "Zimbabwe",
42497         "zw",
42498         "263"
42499       ],
42500       [
42501         "Åland Islands",
42502         "ax",
42503         "358",
42504         1
42505       ]
42506   ];
42507   
42508   return d;
42509 }/**
42510 *    This script refer to:
42511 *    Title: International Telephone Input
42512 *    Author: Jack O'Connor
42513 *    Code version:  v12.1.12
42514 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42515 **/
42516
42517 /**
42518  * @class Roo.bootstrap.PhoneInput
42519  * @extends Roo.bootstrap.TriggerField
42520  * An input with International dial-code selection
42521  
42522  * @cfg {String} defaultDialCode default '+852'
42523  * @cfg {Array} preferedCountries default []
42524   
42525  * @constructor
42526  * Create a new PhoneInput.
42527  * @param {Object} config Configuration options
42528  */
42529
42530 Roo.bootstrap.PhoneInput = function(config) {
42531     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42532 };
42533
42534 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42535         
42536         listWidth: undefined,
42537         
42538         selectedClass: 'active',
42539         
42540         invalidClass : "has-warning",
42541         
42542         validClass: 'has-success',
42543         
42544         allowed: '0123456789',
42545         
42546         max_length: 15,
42547         
42548         /**
42549          * @cfg {String} defaultDialCode The default dial code when initializing the input
42550          */
42551         defaultDialCode: '+852',
42552         
42553         /**
42554          * @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
42555          */
42556         preferedCountries: false,
42557         
42558         getAutoCreate : function()
42559         {
42560             var data = Roo.bootstrap.PhoneInputData();
42561             var align = this.labelAlign || this.parentLabelAlign();
42562             var id = Roo.id();
42563             
42564             this.allCountries = [];
42565             this.dialCodeMapping = [];
42566             
42567             for (var i = 0; i < data.length; i++) {
42568               var c = data[i];
42569               this.allCountries[i] = {
42570                 name: c[0],
42571                 iso2: c[1],
42572                 dialCode: c[2],
42573                 priority: c[3] || 0,
42574                 areaCodes: c[4] || null
42575               };
42576               this.dialCodeMapping[c[2]] = {
42577                   name: c[0],
42578                   iso2: c[1],
42579                   priority: c[3] || 0,
42580                   areaCodes: c[4] || null
42581               };
42582             }
42583             
42584             var cfg = {
42585                 cls: 'form-group',
42586                 cn: []
42587             };
42588             
42589             var input =  {
42590                 tag: 'input',
42591                 id : id,
42592                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42593                 maxlength: this.max_length,
42594                 cls : 'form-control tel-input',
42595                 autocomplete: 'new-password'
42596             };
42597             
42598             var hiddenInput = {
42599                 tag: 'input',
42600                 type: 'hidden',
42601                 cls: 'hidden-tel-input'
42602             };
42603             
42604             if (this.name) {
42605                 hiddenInput.name = this.name;
42606             }
42607             
42608             if (this.disabled) {
42609                 input.disabled = true;
42610             }
42611             
42612             var flag_container = {
42613                 tag: 'div',
42614                 cls: 'flag-box',
42615                 cn: [
42616                     {
42617                         tag: 'div',
42618                         cls: 'flag'
42619                     },
42620                     {
42621                         tag: 'div',
42622                         cls: 'caret'
42623                     }
42624                 ]
42625             };
42626             
42627             var box = {
42628                 tag: 'div',
42629                 cls: this.hasFeedback ? 'has-feedback' : '',
42630                 cn: [
42631                     hiddenInput,
42632                     input,
42633                     {
42634                         tag: 'input',
42635                         cls: 'dial-code-holder',
42636                         disabled: true
42637                     }
42638                 ]
42639             };
42640             
42641             var container = {
42642                 cls: 'roo-select2-container input-group',
42643                 cn: [
42644                     flag_container,
42645                     box
42646                 ]
42647             };
42648             
42649             if (this.fieldLabel.length) {
42650                 var indicator = {
42651                     tag: 'i',
42652                     tooltip: 'This field is required'
42653                 };
42654                 
42655                 var label = {
42656                     tag: 'label',
42657                     'for':  id,
42658                     cls: 'control-label',
42659                     cn: []
42660                 };
42661                 
42662                 var label_text = {
42663                     tag: 'span',
42664                     html: this.fieldLabel
42665                 };
42666                 
42667                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42668                 label.cn = [
42669                     indicator,
42670                     label_text
42671                 ];
42672                 
42673                 if(this.indicatorpos == 'right') {
42674                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42675                     label.cn = [
42676                         label_text,
42677                         indicator
42678                     ];
42679                 }
42680                 
42681                 if(align == 'left') {
42682                     container = {
42683                         tag: 'div',
42684                         cn: [
42685                             container
42686                         ]
42687                     };
42688                     
42689                     if(this.labelWidth > 12){
42690                         label.style = "width: " + this.labelWidth + 'px';
42691                     }
42692                     if(this.labelWidth < 13 && this.labelmd == 0){
42693                         this.labelmd = this.labelWidth;
42694                     }
42695                     if(this.labellg > 0){
42696                         label.cls += ' col-lg-' + this.labellg;
42697                         input.cls += ' col-lg-' + (12 - this.labellg);
42698                     }
42699                     if(this.labelmd > 0){
42700                         label.cls += ' col-md-' + this.labelmd;
42701                         container.cls += ' col-md-' + (12 - this.labelmd);
42702                     }
42703                     if(this.labelsm > 0){
42704                         label.cls += ' col-sm-' + this.labelsm;
42705                         container.cls += ' col-sm-' + (12 - this.labelsm);
42706                     }
42707                     if(this.labelxs > 0){
42708                         label.cls += ' col-xs-' + this.labelxs;
42709                         container.cls += ' col-xs-' + (12 - this.labelxs);
42710                     }
42711                 }
42712             }
42713             
42714             cfg.cn = [
42715                 label,
42716                 container
42717             ];
42718             
42719             var settings = this;
42720             
42721             ['xs','sm','md','lg'].map(function(size){
42722                 if (settings[size]) {
42723                     cfg.cls += ' col-' + size + '-' + settings[size];
42724                 }
42725             });
42726             
42727             this.store = new Roo.data.Store({
42728                 proxy : new Roo.data.MemoryProxy({}),
42729                 reader : new Roo.data.JsonReader({
42730                     fields : [
42731                         {
42732                             'name' : 'name',
42733                             'type' : 'string'
42734                         },
42735                         {
42736                             'name' : 'iso2',
42737                             'type' : 'string'
42738                         },
42739                         {
42740                             'name' : 'dialCode',
42741                             'type' : 'string'
42742                         },
42743                         {
42744                             'name' : 'priority',
42745                             'type' : 'string'
42746                         },
42747                         {
42748                             'name' : 'areaCodes',
42749                             'type' : 'string'
42750                         }
42751                     ]
42752                 })
42753             });
42754             
42755             if(!this.preferedCountries) {
42756                 this.preferedCountries = [
42757                     'hk',
42758                     'gb',
42759                     'us'
42760                 ];
42761             }
42762             
42763             var p = this.preferedCountries.reverse();
42764             
42765             if(p) {
42766                 for (var i = 0; i < p.length; i++) {
42767                     for (var j = 0; j < this.allCountries.length; j++) {
42768                         if(this.allCountries[j].iso2 == p[i]) {
42769                             var t = this.allCountries[j];
42770                             this.allCountries.splice(j,1);
42771                             this.allCountries.unshift(t);
42772                         }
42773                     } 
42774                 }
42775             }
42776             
42777             this.store.proxy.data = {
42778                 success: true,
42779                 data: this.allCountries
42780             };
42781             
42782             return cfg;
42783         },
42784         
42785         initEvents : function()
42786         {
42787             this.createList();
42788             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42789             
42790             this.indicator = this.indicatorEl();
42791             this.flag = this.flagEl();
42792             this.dialCodeHolder = this.dialCodeHolderEl();
42793             
42794             this.trigger = this.el.select('div.flag-box',true).first();
42795             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42796             
42797             var _this = this;
42798             
42799             (function(){
42800                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42801                 _this.list.setWidth(lw);
42802             }).defer(100);
42803             
42804             this.list.on('mouseover', this.onViewOver, this);
42805             this.list.on('mousemove', this.onViewMove, this);
42806             this.inputEl().on("keyup", this.onKeyUp, this);
42807             this.inputEl().on("keypress", this.onKeyPress, this);
42808             
42809             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42810
42811             this.view = new Roo.View(this.list, this.tpl, {
42812                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42813             });
42814             
42815             this.view.on('click', this.onViewClick, this);
42816             this.setValue(this.defaultDialCode);
42817         },
42818         
42819         onTriggerClick : function(e)
42820         {
42821             Roo.log('trigger click');
42822             if(this.disabled){
42823                 return;
42824             }
42825             
42826             if(this.isExpanded()){
42827                 this.collapse();
42828                 this.hasFocus = false;
42829             }else {
42830                 this.store.load({});
42831                 this.hasFocus = true;
42832                 this.expand();
42833             }
42834         },
42835         
42836         isExpanded : function()
42837         {
42838             return this.list.isVisible();
42839         },
42840         
42841         collapse : function()
42842         {
42843             if(!this.isExpanded()){
42844                 return;
42845             }
42846             this.list.hide();
42847             Roo.get(document).un('mousedown', this.collapseIf, this);
42848             Roo.get(document).un('mousewheel', this.collapseIf, this);
42849             this.fireEvent('collapse', this);
42850             this.validate();
42851         },
42852         
42853         expand : function()
42854         {
42855             Roo.log('expand');
42856
42857             if(this.isExpanded() || !this.hasFocus){
42858                 return;
42859             }
42860             
42861             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42862             this.list.setWidth(lw);
42863             
42864             this.list.show();
42865             this.restrictHeight();
42866             
42867             Roo.get(document).on('mousedown', this.collapseIf, this);
42868             Roo.get(document).on('mousewheel', this.collapseIf, this);
42869             
42870             this.fireEvent('expand', this);
42871         },
42872         
42873         restrictHeight : function()
42874         {
42875             this.list.alignTo(this.inputEl(), this.listAlign);
42876             this.list.alignTo(this.inputEl(), this.listAlign);
42877         },
42878         
42879         onViewOver : function(e, t)
42880         {
42881             if(this.inKeyMode){
42882                 return;
42883             }
42884             var item = this.view.findItemFromChild(t);
42885             
42886             if(item){
42887                 var index = this.view.indexOf(item);
42888                 this.select(index, false);
42889             }
42890         },
42891
42892         // private
42893         onViewClick : function(view, doFocus, el, e)
42894         {
42895             var index = this.view.getSelectedIndexes()[0];
42896             
42897             var r = this.store.getAt(index);
42898             
42899             if(r){
42900                 this.onSelect(r, index);
42901             }
42902             if(doFocus !== false && !this.blockFocus){
42903                 this.inputEl().focus();
42904             }
42905         },
42906         
42907         onViewMove : function(e, t)
42908         {
42909             this.inKeyMode = false;
42910         },
42911         
42912         select : function(index, scrollIntoView)
42913         {
42914             this.selectedIndex = index;
42915             this.view.select(index);
42916             if(scrollIntoView !== false){
42917                 var el = this.view.getNode(index);
42918                 if(el){
42919                     this.list.scrollChildIntoView(el, false);
42920                 }
42921             }
42922         },
42923         
42924         createList : function()
42925         {
42926             this.list = Roo.get(document.body).createChild({
42927                 tag: 'ul',
42928                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42929                 style: 'display:none'
42930             });
42931             
42932             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42933         },
42934         
42935         collapseIf : function(e)
42936         {
42937             var in_combo  = e.within(this.el);
42938             var in_list =  e.within(this.list);
42939             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42940             
42941             if (in_combo || in_list || is_list) {
42942                 return;
42943             }
42944             this.collapse();
42945         },
42946         
42947         onSelect : function(record, index)
42948         {
42949             if(this.fireEvent('beforeselect', this, record, index) !== false){
42950                 
42951                 this.setFlagClass(record.data.iso2);
42952                 this.setDialCode(record.data.dialCode);
42953                 this.hasFocus = false;
42954                 this.collapse();
42955                 this.fireEvent('select', this, record, index);
42956             }
42957         },
42958         
42959         flagEl : function()
42960         {
42961             var flag = this.el.select('div.flag',true).first();
42962             if(!flag){
42963                 return false;
42964             }
42965             return flag;
42966         },
42967         
42968         dialCodeHolderEl : function()
42969         {
42970             var d = this.el.select('input.dial-code-holder',true).first();
42971             if(!d){
42972                 return false;
42973             }
42974             return d;
42975         },
42976         
42977         setDialCode : function(v)
42978         {
42979             this.dialCodeHolder.dom.value = '+'+v;
42980         },
42981         
42982         setFlagClass : function(n)
42983         {
42984             this.flag.dom.className = 'flag '+n;
42985         },
42986         
42987         getValue : function()
42988         {
42989             var v = this.inputEl().getValue();
42990             if(this.dialCodeHolder) {
42991                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42992             }
42993             return v;
42994         },
42995         
42996         setValue : function(v)
42997         {
42998             var d = this.getDialCode(v);
42999             
43000             //invalid dial code
43001             if(v.length == 0 || !d || d.length == 0) {
43002                 if(this.rendered){
43003                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43004                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43005                 }
43006                 return;
43007             }
43008             
43009             //valid dial code
43010             this.setFlagClass(this.dialCodeMapping[d].iso2);
43011             this.setDialCode(d);
43012             this.inputEl().dom.value = v.replace('+'+d,'');
43013             this.hiddenEl().dom.value = this.getValue();
43014             
43015             this.validate();
43016         },
43017         
43018         getDialCode : function(v)
43019         {
43020             v = v ||  '';
43021             
43022             if (v.length == 0) {
43023                 return this.dialCodeHolder.dom.value;
43024             }
43025             
43026             var dialCode = "";
43027             if (v.charAt(0) != "+") {
43028                 return false;
43029             }
43030             var numericChars = "";
43031             for (var i = 1; i < v.length; i++) {
43032               var c = v.charAt(i);
43033               if (!isNaN(c)) {
43034                 numericChars += c;
43035                 if (this.dialCodeMapping[numericChars]) {
43036                   dialCode = v.substr(1, i);
43037                 }
43038                 if (numericChars.length == 4) {
43039                   break;
43040                 }
43041               }
43042             }
43043             return dialCode;
43044         },
43045         
43046         reset : function()
43047         {
43048             this.setValue(this.defaultDialCode);
43049             this.validate();
43050         },
43051         
43052         hiddenEl : function()
43053         {
43054             return this.el.select('input.hidden-tel-input',true).first();
43055         },
43056         
43057         // after setting val
43058         onKeyUp : function(e){
43059             this.setValue(this.getValue());
43060         },
43061         
43062         onKeyPress : function(e){
43063             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43064                 e.stopEvent();
43065             }
43066         }
43067         
43068 });
43069 /**
43070  * @class Roo.bootstrap.MoneyField
43071  * @extends Roo.bootstrap.ComboBox
43072  * Bootstrap MoneyField class
43073  * 
43074  * @constructor
43075  * Create a new MoneyField.
43076  * @param {Object} config Configuration options
43077  */
43078
43079 Roo.bootstrap.MoneyField = function(config) {
43080     
43081     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43082     
43083 };
43084
43085 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43086     
43087     /**
43088      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43089      */
43090     allowDecimals : true,
43091     /**
43092      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43093      */
43094     decimalSeparator : ".",
43095     /**
43096      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43097      */
43098     decimalPrecision : 0,
43099     /**
43100      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43101      */
43102     allowNegative : true,
43103     /**
43104      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43105      */
43106     allowZero: true,
43107     /**
43108      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43109      */
43110     minValue : Number.NEGATIVE_INFINITY,
43111     /**
43112      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43113      */
43114     maxValue : Number.MAX_VALUE,
43115     /**
43116      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43117      */
43118     minText : "The minimum value for this field is {0}",
43119     /**
43120      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43121      */
43122     maxText : "The maximum value for this field is {0}",
43123     /**
43124      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43125      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43126      */
43127     nanText : "{0} is not a valid number",
43128     /**
43129      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43130      */
43131     castInt : true,
43132     /**
43133      * @cfg {String} defaults currency of the MoneyField
43134      * value should be in lkey
43135      */
43136     defaultCurrency : false,
43137     /**
43138      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43139      */
43140     thousandsDelimiter : false,
43141     /**
43142      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43143      */
43144     max_length: false,
43145     
43146     inputlg : 9,
43147     inputmd : 9,
43148     inputsm : 9,
43149     inputxs : 6,
43150     
43151     store : false,
43152     
43153     getAutoCreate : function()
43154     {
43155         var align = this.labelAlign || this.parentLabelAlign();
43156         
43157         var id = Roo.id();
43158
43159         var cfg = {
43160             cls: 'form-group',
43161             cn: []
43162         };
43163
43164         var input =  {
43165             tag: 'input',
43166             id : id,
43167             cls : 'form-control roo-money-amount-input',
43168             autocomplete: 'new-password'
43169         };
43170         
43171         var hiddenInput = {
43172             tag: 'input',
43173             type: 'hidden',
43174             id: Roo.id(),
43175             cls: 'hidden-number-input'
43176         };
43177         
43178         if(this.max_length) {
43179             input.maxlength = this.max_length; 
43180         }
43181         
43182         if (this.name) {
43183             hiddenInput.name = this.name;
43184         }
43185
43186         if (this.disabled) {
43187             input.disabled = true;
43188         }
43189
43190         var clg = 12 - this.inputlg;
43191         var cmd = 12 - this.inputmd;
43192         var csm = 12 - this.inputsm;
43193         var cxs = 12 - this.inputxs;
43194         
43195         var container = {
43196             tag : 'div',
43197             cls : 'row roo-money-field',
43198             cn : [
43199                 {
43200                     tag : 'div',
43201                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43202                     cn : [
43203                         {
43204                             tag : 'div',
43205                             cls: 'roo-select2-container input-group',
43206                             cn: [
43207                                 {
43208                                     tag : 'input',
43209                                     cls : 'form-control roo-money-currency-input',
43210                                     autocomplete: 'new-password',
43211                                     readOnly : 1,
43212                                     name : this.currencyName
43213                                 },
43214                                 {
43215                                     tag :'span',
43216                                     cls : 'input-group-addon',
43217                                     cn : [
43218                                         {
43219                                             tag: 'span',
43220                                             cls: 'caret'
43221                                         }
43222                                     ]
43223                                 }
43224                             ]
43225                         }
43226                     ]
43227                 },
43228                 {
43229                     tag : 'div',
43230                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43231                     cn : [
43232                         {
43233                             tag: 'div',
43234                             cls: this.hasFeedback ? 'has-feedback' : '',
43235                             cn: [
43236                                 input
43237                             ]
43238                         }
43239                     ]
43240                 }
43241             ]
43242             
43243         };
43244         
43245         if (this.fieldLabel.length) {
43246             var indicator = {
43247                 tag: 'i',
43248                 tooltip: 'This field is required'
43249             };
43250
43251             var label = {
43252                 tag: 'label',
43253                 'for':  id,
43254                 cls: 'control-label',
43255                 cn: []
43256             };
43257
43258             var label_text = {
43259                 tag: 'span',
43260                 html: this.fieldLabel
43261             };
43262
43263             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43264             label.cn = [
43265                 indicator,
43266                 label_text
43267             ];
43268
43269             if(this.indicatorpos == 'right') {
43270                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43271                 label.cn = [
43272                     label_text,
43273                     indicator
43274                 ];
43275             }
43276
43277             if(align == 'left') {
43278                 container = {
43279                     tag: 'div',
43280                     cn: [
43281                         container
43282                     ]
43283                 };
43284
43285                 if(this.labelWidth > 12){
43286                     label.style = "width: " + this.labelWidth + 'px';
43287                 }
43288                 if(this.labelWidth < 13 && this.labelmd == 0){
43289                     this.labelmd = this.labelWidth;
43290                 }
43291                 if(this.labellg > 0){
43292                     label.cls += ' col-lg-' + this.labellg;
43293                     input.cls += ' col-lg-' + (12 - this.labellg);
43294                 }
43295                 if(this.labelmd > 0){
43296                     label.cls += ' col-md-' + this.labelmd;
43297                     container.cls += ' col-md-' + (12 - this.labelmd);
43298                 }
43299                 if(this.labelsm > 0){
43300                     label.cls += ' col-sm-' + this.labelsm;
43301                     container.cls += ' col-sm-' + (12 - this.labelsm);
43302                 }
43303                 if(this.labelxs > 0){
43304                     label.cls += ' col-xs-' + this.labelxs;
43305                     container.cls += ' col-xs-' + (12 - this.labelxs);
43306                 }
43307             }
43308         }
43309
43310         cfg.cn = [
43311             label,
43312             container,
43313             hiddenInput
43314         ];
43315         
43316         var settings = this;
43317
43318         ['xs','sm','md','lg'].map(function(size){
43319             if (settings[size]) {
43320                 cfg.cls += ' col-' + size + '-' + settings[size];
43321             }
43322         });
43323         
43324         return cfg;
43325     },
43326     
43327     initEvents : function()
43328     {
43329         this.indicator = this.indicatorEl();
43330         
43331         this.initCurrencyEvent();
43332         
43333         this.initNumberEvent();
43334     },
43335     
43336     initCurrencyEvent : function()
43337     {
43338         if (!this.store) {
43339             throw "can not find store for combo";
43340         }
43341         
43342         this.store = Roo.factory(this.store, Roo.data);
43343         this.store.parent = this;
43344         
43345         this.createList();
43346         
43347         this.triggerEl = this.el.select('.input-group-addon', true).first();
43348         
43349         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43350         
43351         var _this = this;
43352         
43353         (function(){
43354             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43355             _this.list.setWidth(lw);
43356         }).defer(100);
43357         
43358         this.list.on('mouseover', this.onViewOver, this);
43359         this.list.on('mousemove', this.onViewMove, this);
43360         this.list.on('scroll', this.onViewScroll, this);
43361         
43362         if(!this.tpl){
43363             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43364         }
43365         
43366         this.view = new Roo.View(this.list, this.tpl, {
43367             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43368         });
43369         
43370         this.view.on('click', this.onViewClick, this);
43371         
43372         this.store.on('beforeload', this.onBeforeLoad, this);
43373         this.store.on('load', this.onLoad, this);
43374         this.store.on('loadexception', this.onLoadException, this);
43375         
43376         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43377             "up" : function(e){
43378                 this.inKeyMode = true;
43379                 this.selectPrev();
43380             },
43381
43382             "down" : function(e){
43383                 if(!this.isExpanded()){
43384                     this.onTriggerClick();
43385                 }else{
43386                     this.inKeyMode = true;
43387                     this.selectNext();
43388                 }
43389             },
43390
43391             "enter" : function(e){
43392                 this.collapse();
43393                 
43394                 if(this.fireEvent("specialkey", this, e)){
43395                     this.onViewClick(false);
43396                 }
43397                 
43398                 return true;
43399             },
43400
43401             "esc" : function(e){
43402                 this.collapse();
43403             },
43404
43405             "tab" : function(e){
43406                 this.collapse();
43407                 
43408                 if(this.fireEvent("specialkey", this, e)){
43409                     this.onViewClick(false);
43410                 }
43411                 
43412                 return true;
43413             },
43414
43415             scope : this,
43416
43417             doRelay : function(foo, bar, hname){
43418                 if(hname == 'down' || this.scope.isExpanded()){
43419                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43420                 }
43421                 return true;
43422             },
43423
43424             forceKeyDown: true
43425         });
43426         
43427         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43428         
43429     },
43430     
43431     initNumberEvent : function(e)
43432     {
43433         this.inputEl().on("keydown" , this.fireKey,  this);
43434         this.inputEl().on("focus", this.onFocus,  this);
43435         this.inputEl().on("blur", this.onBlur,  this);
43436         
43437         this.inputEl().relayEvent('keyup', this);
43438         
43439         if(this.indicator){
43440             this.indicator.addClass('invisible');
43441         }
43442  
43443         this.originalValue = this.getValue();
43444         
43445         if(this.validationEvent == 'keyup'){
43446             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43447             this.inputEl().on('keyup', this.filterValidation, this);
43448         }
43449         else if(this.validationEvent !== false){
43450             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43451         }
43452         
43453         if(this.selectOnFocus){
43454             this.on("focus", this.preFocus, this);
43455             
43456         }
43457         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43458             this.inputEl().on("keypress", this.filterKeys, this);
43459         } else {
43460             this.inputEl().relayEvent('keypress', this);
43461         }
43462         
43463         var allowed = "0123456789";
43464         
43465         if(this.allowDecimals){
43466             allowed += this.decimalSeparator;
43467         }
43468         
43469         if(this.allowNegative){
43470             allowed += "-";
43471         }
43472         
43473         if(this.thousandsDelimiter) {
43474             allowed += ",";
43475         }
43476         
43477         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43478         
43479         var keyPress = function(e){
43480             
43481             var k = e.getKey();
43482             
43483             var c = e.getCharCode();
43484             
43485             if(
43486                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43487                     allowed.indexOf(String.fromCharCode(c)) === -1
43488             ){
43489                 e.stopEvent();
43490                 return;
43491             }
43492             
43493             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43494                 return;
43495             }
43496             
43497             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43498                 e.stopEvent();
43499             }
43500         };
43501         
43502         this.inputEl().on("keypress", keyPress, this);
43503         
43504     },
43505     
43506     onTriggerClick : function(e)
43507     {   
43508         if(this.disabled){
43509             return;
43510         }
43511         
43512         this.page = 0;
43513         this.loadNext = false;
43514         
43515         if(this.isExpanded()){
43516             this.collapse();
43517             return;
43518         }
43519         
43520         this.hasFocus = true;
43521         
43522         if(this.triggerAction == 'all') {
43523             this.doQuery(this.allQuery, true);
43524             return;
43525         }
43526         
43527         this.doQuery(this.getRawValue());
43528     },
43529     
43530     getCurrency : function()
43531     {   
43532         var v = this.currencyEl().getValue();
43533         
43534         return v;
43535     },
43536     
43537     restrictHeight : function()
43538     {
43539         this.list.alignTo(this.currencyEl(), this.listAlign);
43540         this.list.alignTo(this.currencyEl(), this.listAlign);
43541     },
43542     
43543     onViewClick : function(view, doFocus, el, e)
43544     {
43545         var index = this.view.getSelectedIndexes()[0];
43546         
43547         var r = this.store.getAt(index);
43548         
43549         if(r){
43550             this.onSelect(r, index);
43551         }
43552     },
43553     
43554     onSelect : function(record, index){
43555         
43556         if(this.fireEvent('beforeselect', this, record, index) !== false){
43557         
43558             this.setFromCurrencyData(index > -1 ? record.data : false);
43559             
43560             this.collapse();
43561             
43562             this.fireEvent('select', this, record, index);
43563         }
43564     },
43565     
43566     setFromCurrencyData : function(o)
43567     {
43568         var currency = '';
43569         
43570         this.lastCurrency = o;
43571         
43572         if (this.currencyField) {
43573             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43574         } else {
43575             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43576         }
43577         
43578         this.lastSelectionText = currency;
43579         
43580         //setting default currency
43581         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43582             this.setCurrency(this.defaultCurrency);
43583             return;
43584         }
43585         
43586         this.setCurrency(currency);
43587     },
43588     
43589     setFromData : function(o)
43590     {
43591         var c = {};
43592         
43593         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43594         
43595         this.setFromCurrencyData(c);
43596         
43597         var value = '';
43598         
43599         if (this.name) {
43600             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43601         } else {
43602             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43603         }
43604         
43605         this.setValue(value);
43606         
43607     },
43608     
43609     setCurrency : function(v)
43610     {   
43611         this.currencyValue = v;
43612         
43613         if(this.rendered){
43614             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43615             this.validate();
43616         }
43617     },
43618     
43619     setValue : function(v)
43620     {
43621         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43622         
43623         this.value = v;
43624         
43625         if(this.rendered){
43626             
43627             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43628             
43629             this.inputEl().dom.value = (v == '') ? '' :
43630                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43631             
43632             if(!this.allowZero && v === '0') {
43633                 this.hiddenEl().dom.value = '';
43634                 this.inputEl().dom.value = '';
43635             }
43636             
43637             this.validate();
43638         }
43639     },
43640     
43641     getRawValue : function()
43642     {
43643         var v = this.inputEl().getValue();
43644         
43645         return v;
43646     },
43647     
43648     getValue : function()
43649     {
43650         return this.fixPrecision(this.parseValue(this.getRawValue()));
43651     },
43652     
43653     parseValue : function(value)
43654     {
43655         if(this.thousandsDelimiter) {
43656             value += "";
43657             r = new RegExp(",", "g");
43658             value = value.replace(r, "");
43659         }
43660         
43661         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43662         return isNaN(value) ? '' : value;
43663         
43664     },
43665     
43666     fixPrecision : function(value)
43667     {
43668         if(this.thousandsDelimiter) {
43669             value += "";
43670             r = new RegExp(",", "g");
43671             value = value.replace(r, "");
43672         }
43673         
43674         var nan = isNaN(value);
43675         
43676         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43677             return nan ? '' : value;
43678         }
43679         return parseFloat(value).toFixed(this.decimalPrecision);
43680     },
43681     
43682     decimalPrecisionFcn : function(v)
43683     {
43684         return Math.floor(v);
43685     },
43686     
43687     validateValue : function(value)
43688     {
43689         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43690             return false;
43691         }
43692         
43693         var num = this.parseValue(value);
43694         
43695         if(isNaN(num)){
43696             this.markInvalid(String.format(this.nanText, value));
43697             return false;
43698         }
43699         
43700         if(num < this.minValue){
43701             this.markInvalid(String.format(this.minText, this.minValue));
43702             return false;
43703         }
43704         
43705         if(num > this.maxValue){
43706             this.markInvalid(String.format(this.maxText, this.maxValue));
43707             return false;
43708         }
43709         
43710         return true;
43711     },
43712     
43713     validate : function()
43714     {
43715         if(this.disabled || this.allowBlank){
43716             this.markValid();
43717             return true;
43718         }
43719         
43720         var currency = this.getCurrency();
43721         
43722         if(this.validateValue(this.getRawValue()) && currency.length){
43723             this.markValid();
43724             return true;
43725         }
43726         
43727         this.markInvalid();
43728         return false;
43729     },
43730     
43731     getName: function()
43732     {
43733         return this.name;
43734     },
43735     
43736     beforeBlur : function()
43737     {
43738         if(!this.castInt){
43739             return;
43740         }
43741         
43742         var v = this.parseValue(this.getRawValue());
43743         
43744         if(v || v == 0){
43745             this.setValue(v);
43746         }
43747     },
43748     
43749     onBlur : function()
43750     {
43751         this.beforeBlur();
43752         
43753         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43754             //this.el.removeClass(this.focusClass);
43755         }
43756         
43757         this.hasFocus = false;
43758         
43759         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43760             this.validate();
43761         }
43762         
43763         var v = this.getValue();
43764         
43765         if(String(v) !== String(this.startValue)){
43766             this.fireEvent('change', this, v, this.startValue);
43767         }
43768         
43769         this.fireEvent("blur", this);
43770     },
43771     
43772     inputEl : function()
43773     {
43774         return this.el.select('.roo-money-amount-input', true).first();
43775     },
43776     
43777     currencyEl : function()
43778     {
43779         return this.el.select('.roo-money-currency-input', true).first();
43780     },
43781     
43782     hiddenEl : function()
43783     {
43784         return this.el.select('input.hidden-number-input',true).first();
43785     }
43786     
43787 });/**
43788  * @class Roo.bootstrap.BezierSignature
43789  * @extends Roo.bootstrap.Component
43790  * Bootstrap BezierSignature class
43791  * This script refer to:
43792  *    Title: Signature Pad
43793  *    Author: szimek
43794  *    Availability: https://github.com/szimek/signature_pad
43795  *
43796  * @constructor
43797  * Create a new BezierSignature
43798  * @param {Object} config The config object
43799  */
43800
43801 Roo.bootstrap.BezierSignature = function(config){
43802     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43803     this.addEvents({
43804         "resize" : true
43805     });
43806 };
43807
43808 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43809 {
43810      
43811     curve_data: [],
43812     
43813     is_empty: true,
43814     
43815     mouse_btn_down: true,
43816     
43817     /**
43818      * @cfg {int} canvas height
43819      */
43820     canvas_height: '200px',
43821     
43822     /**
43823      * @cfg {float|function} Radius of a single dot.
43824      */ 
43825     dot_size: false,
43826     
43827     /**
43828      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43829      */
43830     min_width: 0.5,
43831     
43832     /**
43833      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43834      */
43835     max_width: 2.5,
43836     
43837     /**
43838      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43839      */
43840     throttle: 16,
43841     
43842     /**
43843      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43844      */
43845     min_distance: 5,
43846     
43847     /**
43848      * @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.
43849      */
43850     bg_color: 'rgba(0, 0, 0, 0)',
43851     
43852     /**
43853      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43854      */
43855     dot_color: 'black',
43856     
43857     /**
43858      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43859      */ 
43860     velocity_filter_weight: 0.7,
43861     
43862     /**
43863      * @cfg {function} Callback when stroke begin. 
43864      */
43865     onBegin: false,
43866     
43867     /**
43868      * @cfg {function} Callback when stroke end.
43869      */
43870     onEnd: false,
43871     
43872     getAutoCreate : function()
43873     {
43874         var cls = 'roo-signature column';
43875         
43876         if(this.cls){
43877             cls += ' ' + this.cls;
43878         }
43879         
43880         var col_sizes = [
43881             'lg',
43882             'md',
43883             'sm',
43884             'xs'
43885         ];
43886         
43887         for(var i = 0; i < col_sizes.length; i++) {
43888             if(this[col_sizes[i]]) {
43889                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43890             }
43891         }
43892         
43893         var cfg = {
43894             tag: 'div',
43895             cls: cls,
43896             cn: [
43897                 {
43898                     tag: 'div',
43899                     cls: 'roo-signature-body',
43900                     cn: [
43901                         {
43902                             tag: 'canvas',
43903                             cls: 'roo-signature-body-canvas',
43904                             height: this.canvas_height,
43905                             width: this.canvas_width
43906                         }
43907                     ]
43908                 },
43909                 {
43910                     tag: 'input',
43911                     type: 'file',
43912                     style: 'display: none'
43913                 }
43914             ]
43915         };
43916         
43917         return cfg;
43918     },
43919     
43920     initEvents: function() 
43921     {
43922         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43923         
43924         var canvas = this.canvasEl();
43925         
43926         // mouse && touch event swapping...
43927         canvas.dom.style.touchAction = 'none';
43928         canvas.dom.style.msTouchAction = 'none';
43929         
43930         this.mouse_btn_down = false;
43931         canvas.on('mousedown', this._handleMouseDown, this);
43932         canvas.on('mousemove', this._handleMouseMove, this);
43933         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43934         
43935         if (window.PointerEvent) {
43936             canvas.on('pointerdown', this._handleMouseDown, this);
43937             canvas.on('pointermove', this._handleMouseMove, this);
43938             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43939         }
43940         
43941         if ('ontouchstart' in window) {
43942             canvas.on('touchstart', this._handleTouchStart, this);
43943             canvas.on('touchmove', this._handleTouchMove, this);
43944             canvas.on('touchend', this._handleTouchEnd, this);
43945         }
43946         
43947         Roo.EventManager.onWindowResize(this.resize, this, true);
43948         
43949         // file input event
43950         this.fileEl().on('change', this.uploadImage, this);
43951         
43952         this.clear();
43953         
43954         this.resize();
43955     },
43956     
43957     resize: function(){
43958         
43959         var canvas = this.canvasEl().dom;
43960         var ctx = this.canvasElCtx();
43961         var img_data = false;
43962         
43963         if(canvas.width > 0) {
43964             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43965         }
43966         // setting canvas width will clean img data
43967         canvas.width = 0;
43968         
43969         var style = window.getComputedStyle ? 
43970             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43971             
43972         var padding_left = parseInt(style.paddingLeft) || 0;
43973         var padding_right = parseInt(style.paddingRight) || 0;
43974         
43975         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43976         
43977         if(img_data) {
43978             ctx.putImageData(img_data, 0, 0);
43979         }
43980     },
43981     
43982     _handleMouseDown: function(e)
43983     {
43984         if (e.browserEvent.which === 1) {
43985             this.mouse_btn_down = true;
43986             this.strokeBegin(e);
43987         }
43988     },
43989     
43990     _handleMouseMove: function (e)
43991     {
43992         if (this.mouse_btn_down) {
43993             this.strokeMoveUpdate(e);
43994         }
43995     },
43996     
43997     _handleMouseUp: function (e)
43998     {
43999         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44000             this.mouse_btn_down = false;
44001             this.strokeEnd(e);
44002         }
44003     },
44004     
44005     _handleTouchStart: function (e) {
44006         
44007         e.preventDefault();
44008         if (e.browserEvent.targetTouches.length === 1) {
44009             // var touch = e.browserEvent.changedTouches[0];
44010             // this.strokeBegin(touch);
44011             
44012              this.strokeBegin(e); // assume e catching the correct xy...
44013         }
44014     },
44015     
44016     _handleTouchMove: function (e) {
44017         e.preventDefault();
44018         // var touch = event.targetTouches[0];
44019         // _this._strokeMoveUpdate(touch);
44020         this.strokeMoveUpdate(e);
44021     },
44022     
44023     _handleTouchEnd: function (e) {
44024         var wasCanvasTouched = e.target === this.canvasEl().dom;
44025         if (wasCanvasTouched) {
44026             e.preventDefault();
44027             // var touch = event.changedTouches[0];
44028             // _this._strokeEnd(touch);
44029             this.strokeEnd(e);
44030         }
44031     },
44032     
44033     reset: function () {
44034         this._lastPoints = [];
44035         this._lastVelocity = 0;
44036         this._lastWidth = (this.min_width + this.max_width) / 2;
44037         this.canvasElCtx().fillStyle = this.dot_color;
44038     },
44039     
44040     strokeMoveUpdate: function(e)
44041     {
44042         this.strokeUpdate(e);
44043         
44044         if (this.throttle) {
44045             this.throttleStroke(this.strokeUpdate, this.throttle);
44046         }
44047         else {
44048             this.strokeUpdate(e);
44049         }
44050     },
44051     
44052     strokeBegin: function(e)
44053     {
44054         var newPointGroup = {
44055             color: this.dot_color,
44056             points: []
44057         };
44058         
44059         if (typeof this.onBegin === 'function') {
44060             this.onBegin(e);
44061         }
44062         
44063         this.curve_data.push(newPointGroup);
44064         this.reset();
44065         this.strokeUpdate(e);
44066     },
44067     
44068     strokeUpdate: function(e)
44069     {
44070         var rect = this.canvasEl().dom.getBoundingClientRect();
44071         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44072         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44073         var lastPoints = lastPointGroup.points;
44074         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44075         var isLastPointTooClose = lastPoint
44076             ? point.distanceTo(lastPoint) <= this.min_distance
44077             : false;
44078         var color = lastPointGroup.color;
44079         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44080             var curve = this.addPoint(point);
44081             if (!lastPoint) {
44082                 this.drawDot({color: color, point: point});
44083             }
44084             else if (curve) {
44085                 this.drawCurve({color: color, curve: curve});
44086             }
44087             lastPoints.push({
44088                 time: point.time,
44089                 x: point.x,
44090                 y: point.y
44091             });
44092         }
44093     },
44094     
44095     strokeEnd: function(e)
44096     {
44097         this.strokeUpdate(e);
44098         if (typeof this.onEnd === 'function') {
44099             this.onEnd(e);
44100         }
44101     },
44102     
44103     addPoint:  function (point) {
44104         var _lastPoints = this._lastPoints;
44105         _lastPoints.push(point);
44106         if (_lastPoints.length > 2) {
44107             if (_lastPoints.length === 3) {
44108                 _lastPoints.unshift(_lastPoints[0]);
44109             }
44110             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44111             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44112             _lastPoints.shift();
44113             return curve;
44114         }
44115         return null;
44116     },
44117     
44118     calculateCurveWidths: function (startPoint, endPoint) {
44119         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44120             (1 - this.velocity_filter_weight) * this._lastVelocity;
44121
44122         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44123         var widths = {
44124             end: newWidth,
44125             start: this._lastWidth
44126         };
44127         
44128         this._lastVelocity = velocity;
44129         this._lastWidth = newWidth;
44130         return widths;
44131     },
44132     
44133     drawDot: function (_a) {
44134         var color = _a.color, point = _a.point;
44135         var ctx = this.canvasElCtx();
44136         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44137         ctx.beginPath();
44138         this.drawCurveSegment(point.x, point.y, width);
44139         ctx.closePath();
44140         ctx.fillStyle = color;
44141         ctx.fill();
44142     },
44143     
44144     drawCurve: function (_a) {
44145         var color = _a.color, curve = _a.curve;
44146         var ctx = this.canvasElCtx();
44147         var widthDelta = curve.endWidth - curve.startWidth;
44148         var drawSteps = Math.floor(curve.length()) * 2;
44149         ctx.beginPath();
44150         ctx.fillStyle = color;
44151         for (var i = 0; i < drawSteps; i += 1) {
44152         var t = i / drawSteps;
44153         var tt = t * t;
44154         var ttt = tt * t;
44155         var u = 1 - t;
44156         var uu = u * u;
44157         var uuu = uu * u;
44158         var x = uuu * curve.startPoint.x;
44159         x += 3 * uu * t * curve.control1.x;
44160         x += 3 * u * tt * curve.control2.x;
44161         x += ttt * curve.endPoint.x;
44162         var y = uuu * curve.startPoint.y;
44163         y += 3 * uu * t * curve.control1.y;
44164         y += 3 * u * tt * curve.control2.y;
44165         y += ttt * curve.endPoint.y;
44166         var width = curve.startWidth + ttt * widthDelta;
44167         this.drawCurveSegment(x, y, width);
44168         }
44169         ctx.closePath();
44170         ctx.fill();
44171     },
44172     
44173     drawCurveSegment: function (x, y, width) {
44174         var ctx = this.canvasElCtx();
44175         ctx.moveTo(x, y);
44176         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44177         this.is_empty = false;
44178     },
44179     
44180     clear: function()
44181     {
44182         var ctx = this.canvasElCtx();
44183         var canvas = this.canvasEl().dom;
44184         ctx.fillStyle = this.bg_color;
44185         ctx.clearRect(0, 0, canvas.width, canvas.height);
44186         ctx.fillRect(0, 0, canvas.width, canvas.height);
44187         this.curve_data = [];
44188         this.reset();
44189         this.is_empty = true;
44190     },
44191     
44192     fileEl: function()
44193     {
44194         return  this.el.select('input',true).first();
44195     },
44196     
44197     canvasEl: function()
44198     {
44199         return this.el.select('canvas',true).first();
44200     },
44201     
44202     canvasElCtx: function()
44203     {
44204         return this.el.select('canvas',true).first().dom.getContext('2d');
44205     },
44206     
44207     getImage: function(type)
44208     {
44209         if(this.is_empty) {
44210             return false;
44211         }
44212         
44213         // encryption ?
44214         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44215     },
44216     
44217     drawFromImage: function(img_src)
44218     {
44219         var img = new Image();
44220         
44221         img.onload = function(){
44222             this.canvasElCtx().drawImage(img, 0, 0);
44223         }.bind(this);
44224         
44225         img.src = img_src;
44226         
44227         this.is_empty = false;
44228     },
44229     
44230     selectImage: function()
44231     {
44232         this.fileEl().dom.click();
44233     },
44234     
44235     uploadImage: function(e)
44236     {
44237         var reader = new FileReader();
44238         
44239         reader.onload = function(e){
44240             var img = new Image();
44241             img.onload = function(){
44242                 this.reset();
44243                 this.canvasElCtx().drawImage(img, 0, 0);
44244             }.bind(this);
44245             img.src = e.target.result;
44246         }.bind(this);
44247         
44248         reader.readAsDataURL(e.target.files[0]);
44249     },
44250     
44251     // Bezier Point Constructor
44252     Point: (function () {
44253         function Point(x, y, time) {
44254             this.x = x;
44255             this.y = y;
44256             this.time = time || Date.now();
44257         }
44258         Point.prototype.distanceTo = function (start) {
44259             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44260         };
44261         Point.prototype.equals = function (other) {
44262             return this.x === other.x && this.y === other.y && this.time === other.time;
44263         };
44264         Point.prototype.velocityFrom = function (start) {
44265             return this.time !== start.time
44266             ? this.distanceTo(start) / (this.time - start.time)
44267             : 0;
44268         };
44269         return Point;
44270     }()),
44271     
44272     
44273     // Bezier Constructor
44274     Bezier: (function () {
44275         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44276             this.startPoint = startPoint;
44277             this.control2 = control2;
44278             this.control1 = control1;
44279             this.endPoint = endPoint;
44280             this.startWidth = startWidth;
44281             this.endWidth = endWidth;
44282         }
44283         Bezier.fromPoints = function (points, widths, scope) {
44284             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44285             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44286             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44287         };
44288         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44289             var dx1 = s1.x - s2.x;
44290             var dy1 = s1.y - s2.y;
44291             var dx2 = s2.x - s3.x;
44292             var dy2 = s2.y - s3.y;
44293             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44294             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44295             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44296             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44297             var dxm = m1.x - m2.x;
44298             var dym = m1.y - m2.y;
44299             var k = l2 / (l1 + l2);
44300             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44301             var tx = s2.x - cm.x;
44302             var ty = s2.y - cm.y;
44303             return {
44304                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44305                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44306             };
44307         };
44308         Bezier.prototype.length = function () {
44309             var steps = 10;
44310             var length = 0;
44311             var px;
44312             var py;
44313             for (var i = 0; i <= steps; i += 1) {
44314                 var t = i / steps;
44315                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44316                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44317                 if (i > 0) {
44318                     var xdiff = cx - px;
44319                     var ydiff = cy - py;
44320                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44321                 }
44322                 px = cx;
44323                 py = cy;
44324             }
44325             return length;
44326         };
44327         Bezier.prototype.point = function (t, start, c1, c2, end) {
44328             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44329             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44330             + (3.0 * c2 * (1.0 - t) * t * t)
44331             + (end * t * t * t);
44332         };
44333         return Bezier;
44334     }()),
44335     
44336     throttleStroke: function(fn, wait) {
44337       if (wait === void 0) { wait = 250; }
44338       var previous = 0;
44339       var timeout = null;
44340       var result;
44341       var storedContext;
44342       var storedArgs;
44343       var later = function () {
44344           previous = Date.now();
44345           timeout = null;
44346           result = fn.apply(storedContext, storedArgs);
44347           if (!timeout) {
44348               storedContext = null;
44349               storedArgs = [];
44350           }
44351       };
44352       return function wrapper() {
44353           var args = [];
44354           for (var _i = 0; _i < arguments.length; _i++) {
44355               args[_i] = arguments[_i];
44356           }
44357           var now = Date.now();
44358           var remaining = wait - (now - previous);
44359           storedContext = this;
44360           storedArgs = args;
44361           if (remaining <= 0 || remaining > wait) {
44362               if (timeout) {
44363                   clearTimeout(timeout);
44364                   timeout = null;
44365               }
44366               previous = now;
44367               result = fn.apply(storedContext, storedArgs);
44368               if (!timeout) {
44369                   storedContext = null;
44370                   storedArgs = [];
44371               }
44372           }
44373           else if (!timeout) {
44374               timeout = window.setTimeout(later, remaining);
44375           }
44376           return result;
44377       };
44378   }
44379   
44380 });
44381
44382  
44383
44384