ef5bb90eb01f4b44db961693d0f34015b5e06f79
[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
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     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <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,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * 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
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @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)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <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
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         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){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @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
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @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
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.el.select('.popover-content',true).first();
19659     },
19660     
19661     getAutoCreate : function(){
19662          
19663         var cfg = {
19664            cls : 'popover roo-dynamic shadow roo-popover',
19665            style: 'display:block',
19666            cn : [
19667                 {
19668                     cls : 'arrow'
19669                 },
19670                 {
19671                     cls : 'popover-inner ',
19672                     cn : [
19673                         {
19674                             tag: 'h3',
19675                             cls: 'popover-title popover-header',
19676                             html : this.title || ''
19677                         },
19678                         {
19679                             cls : 'popover-content popover-body'  + this.cls,
19680                             html : this.html || ''
19681                         }
19682                     ]
19683                     
19684                 }
19685            ]
19686         };
19687         
19688         return cfg;
19689     },
19690     /**
19691      * @param {string} the title
19692      */
19693     setTitle: function(str)
19694     {
19695         this.title = str;
19696         if (this.el) {
19697             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19698         }
19699         
19700     },
19701     /**
19702      * @param {string} the body content
19703      */
19704     setContent: function(str)
19705     {
19706         this.html = str;
19707         if (this.el) {
19708             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19709         }
19710         
19711     },
19712     // as it get's added to the bottom of the page.
19713     onRender : function(ct, position)
19714     {
19715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19716         if(!this.el){
19717             var cfg = Roo.apply({},  this.getAutoCreate());
19718             cfg.id = Roo.id();
19719             
19720             if (this.cls) {
19721                 cfg.cls += ' ' + this.cls;
19722             }
19723             if (this.style) {
19724                 cfg.style = this.style;
19725             }
19726             //Roo.log("adding to ");
19727             this.el = Roo.get(document.body).createChild(cfg, position);
19728 //            Roo.log(this.el);
19729         }
19730         
19731         var nitems = [];
19732         if(typeof(this.items) != 'undefined'){
19733             var items = this.items;
19734             delete this.items;
19735
19736             for(var i =0;i < items.length;i++) {
19737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19738             }
19739         }
19740
19741         this.items = nitems;
19742         
19743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     resizeMask : function()
19751     {
19752         this.maskEl.setSize(
19753             Roo.lib.Dom.getViewWidth(true),
19754             Roo.lib.Dom.getViewHeight(true)
19755         );
19756     },
19757     
19758     initEvents : function()
19759     {
19760         
19761         Roo.bootstrap.Popover.register(this);
19762         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19763         this.el.enableDisplayMode('block');
19764         this.el.hide();
19765         if (this.over === false && !this.parent()) {
19766             return; 
19767         }
19768         if (this.triggers === false) {
19769             return;
19770         }
19771          
19772         // support parent
19773         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19774         var triggers = this.trigger ? this.trigger.split(' ') : [];
19775         Roo.each(triggers, function(trigger) {
19776         
19777             if (trigger == 'click') {
19778                 on_el.on('click', this.toggle, this);
19779             } else if (trigger != 'manual') {
19780                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19781                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19782       
19783                 on_el.on(eventIn  ,this.enter, this);
19784                 on_el.on(eventOut, this.leave, this);
19785             }
19786         }, this);
19787         
19788     },
19789     
19790     
19791     // private
19792     timeout : null,
19793     hoverState : null,
19794     
19795     toggle : function () {
19796         this.hoverState == 'in' ? this.leave() : this.enter();
19797     },
19798     
19799     enter : function () {
19800         
19801         clearTimeout(this.timeout);
19802     
19803         this.hoverState = 'in';
19804     
19805         if (!this.delay || !this.delay.show) {
19806             this.show();
19807             return;
19808         }
19809         var _t = this;
19810         this.timeout = setTimeout(function () {
19811             if (_t.hoverState == 'in') {
19812                 _t.show();
19813             }
19814         }, this.delay.show)
19815     },
19816     
19817     leave : function() {
19818         clearTimeout(this.timeout);
19819     
19820         this.hoverState = 'out';
19821     
19822         if (!this.delay || !this.delay.hide) {
19823             this.hide();
19824             return;
19825         }
19826         var _t = this;
19827         this.timeout = setTimeout(function () {
19828             if (_t.hoverState == 'out') {
19829                 _t.hide();
19830             }
19831         }, this.delay.hide)
19832     },
19833     /**
19834      * Show the popover
19835      * @param {Roo.Element|string|false} - element to align and point to.
19836      */
19837     show : function (on_el)
19838     {
19839         
19840         on_el = on_el || false; // default to false
19841         if (!on_el) {
19842             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19843                 on_el = this.parent().el;
19844             } else if (this.over) {
19845                 Roo.get(this.over);
19846             }
19847             
19848         }
19849         
19850         if (!this.el) {
19851             this.render(document.body);
19852         }
19853         
19854         
19855         this.el.removeClass([
19856             'fade','top','bottom', 'left', 'right','in',
19857             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19858         ]);
19859         
19860         if (!this.title.length) {
19861             this.el.select('.popover-title',true).hide();
19862         }
19863         
19864         
19865         var placement = typeof this.placement == 'function' ?
19866             this.placement.call(this, this.el, on_el) :
19867             this.placement;
19868             
19869         /*
19870         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19871         
19872         // I think  'auto right' - but 
19873         
19874         var autoPlace = autoToken.test(placement);
19875         if (autoPlace) {
19876             placement = placement.replace(autoToken, '') || 'top';
19877         }
19878         */
19879         
19880         
19881         this.el.show();
19882         this.el.dom.style.display='block';
19883         
19884         //this.el.appendTo(on_el);
19885         
19886         var p = this.getPosition();
19887         var box = this.el.getBox();
19888         
19889         
19890         var align = Roo.bootstrap.Popover.alignment[placement];
19891         this.el.addClass(align[2]);
19892
19893 //        Roo.log(align);
19894
19895         if (on_el) {
19896             this.el.alignTo(on_el, align[0],align[1]);
19897         } else {
19898             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19899             var es = this.el.getSize();
19900             var x = Roo.lib.Dom.getViewWidth()/2;
19901             var y = Roo.lib.Dom.getViewHeight()/2;
19902             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19903             
19904         }
19905
19906         
19907         //var arrow = this.el.select('.arrow',true).first();
19908         //arrow.set(align[2], 
19909         
19910         this.el.addClass('in');
19911         
19912         
19913         if (this.el.hasClass('fade')) {
19914             // fade it?
19915         }
19916         
19917         this.hoverState = 'in';
19918         
19919         if (this.modal) {
19920             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19921             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19922             this.maskEl.dom.style.display = 'block';
19923             this.maskEl.addClass('show');
19924         }
19925         
19926         
19927         
19928         this.fireEvent('show', this);
19929         
19930     },
19931     hide : function()
19932     {
19933         this.el.setXY([0,0]);
19934         this.el.removeClass('in');
19935         this.el.hide();
19936         this.hoverState = null;
19937         
19938         this.fireEvent('hide', this);
19939     }
19940     
19941 });
19942
19943
19944 Roo.apply(Roo.bootstrap.Popover, {
19945
19946     alignment : {
19947         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19948         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19949         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19950         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19951     },
19952     
19953     zIndex : 20001,
19954
19955     clickHander : false,
19956     
19957
19958     onMouseDown : function(e)
19959     {
19960         if (!e.getTarget(".roo-popover")) {
19961             this.hideAll();
19962         }
19963          
19964     },
19965     
19966     popups : [],
19967     
19968     register : function(popup)
19969     {
19970         if (!Roo.bootstrap.Popover.clickHandler) {
19971             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19972         }
19973         // hide other popups.
19974         this.hideAll();
19975         this.popups.push(popup);
19976     },
19977     hideAll : function()
19978     {
19979         this.popups.forEach(function(p) {
19980             p.hide();
19981         });
19982     }
19983
19984 });/*
19985  * - LGPL
19986  *
19987  * Progress
19988  * 
19989  */
19990
19991 /**
19992  * @class Roo.bootstrap.Progress
19993  * @extends Roo.bootstrap.Component
19994  * Bootstrap Progress class
19995  * @cfg {Boolean} striped striped of the progress bar
19996  * @cfg {Boolean} active animated of the progress bar
19997  * 
19998  * 
19999  * @constructor
20000  * Create a new Progress
20001  * @param {Object} config The config object
20002  */
20003
20004 Roo.bootstrap.Progress = function(config){
20005     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20006 };
20007
20008 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20009     
20010     striped : false,
20011     active: false,
20012     
20013     getAutoCreate : function(){
20014         var cfg = {
20015             tag: 'div',
20016             cls: 'progress'
20017         };
20018         
20019         
20020         if(this.striped){
20021             cfg.cls += ' progress-striped';
20022         }
20023       
20024         if(this.active){
20025             cfg.cls += ' active';
20026         }
20027         
20028         
20029         return cfg;
20030     }
20031    
20032 });
20033
20034  
20035
20036  /*
20037  * - LGPL
20038  *
20039  * ProgressBar
20040  * 
20041  */
20042
20043 /**
20044  * @class Roo.bootstrap.ProgressBar
20045  * @extends Roo.bootstrap.Component
20046  * Bootstrap ProgressBar class
20047  * @cfg {Number} aria_valuenow aria-value now
20048  * @cfg {Number} aria_valuemin aria-value min
20049  * @cfg {Number} aria_valuemax aria-value max
20050  * @cfg {String} label label for the progress bar
20051  * @cfg {String} panel (success | info | warning | danger )
20052  * @cfg {String} role role of the progress bar
20053  * @cfg {String} sr_only text
20054  * 
20055  * 
20056  * @constructor
20057  * Create a new ProgressBar
20058  * @param {Object} config The config object
20059  */
20060
20061 Roo.bootstrap.ProgressBar = function(config){
20062     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20063 };
20064
20065 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20066     
20067     aria_valuenow : 0,
20068     aria_valuemin : 0,
20069     aria_valuemax : 100,
20070     label : false,
20071     panel : false,
20072     role : false,
20073     sr_only: false,
20074     
20075     getAutoCreate : function()
20076     {
20077         
20078         var cfg = {
20079             tag: 'div',
20080             cls: 'progress-bar',
20081             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20082         };
20083         
20084         if(this.sr_only){
20085             cfg.cn = {
20086                 tag: 'span',
20087                 cls: 'sr-only',
20088                 html: this.sr_only
20089             }
20090         }
20091         
20092         if(this.role){
20093             cfg.role = this.role;
20094         }
20095         
20096         if(this.aria_valuenow){
20097             cfg['aria-valuenow'] = this.aria_valuenow;
20098         }
20099         
20100         if(this.aria_valuemin){
20101             cfg['aria-valuemin'] = this.aria_valuemin;
20102         }
20103         
20104         if(this.aria_valuemax){
20105             cfg['aria-valuemax'] = this.aria_valuemax;
20106         }
20107         
20108         if(this.label && !this.sr_only){
20109             cfg.html = this.label;
20110         }
20111         
20112         if(this.panel){
20113             cfg.cls += ' progress-bar-' + this.panel;
20114         }
20115         
20116         return cfg;
20117     },
20118     
20119     update : function(aria_valuenow)
20120     {
20121         this.aria_valuenow = aria_valuenow;
20122         
20123         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20124     }
20125    
20126 });
20127
20128  
20129
20130  /*
20131  * - LGPL
20132  *
20133  * column
20134  * 
20135  */
20136
20137 /**
20138  * @class Roo.bootstrap.TabGroup
20139  * @extends Roo.bootstrap.Column
20140  * Bootstrap Column class
20141  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20142  * @cfg {Boolean} carousel true to make the group behave like a carousel
20143  * @cfg {Boolean} bullets show bullets for the panels
20144  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20145  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20146  * @cfg {Boolean} showarrow (true|false) show arrow default true
20147  * 
20148  * @constructor
20149  * Create a new TabGroup
20150  * @param {Object} config The config object
20151  */
20152
20153 Roo.bootstrap.TabGroup = function(config){
20154     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20155     if (!this.navId) {
20156         this.navId = Roo.id();
20157     }
20158     this.tabs = [];
20159     Roo.bootstrap.TabGroup.register(this);
20160     
20161 };
20162
20163 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20164     
20165     carousel : false,
20166     transition : false,
20167     bullets : 0,
20168     timer : 0,
20169     autoslide : false,
20170     slideFn : false,
20171     slideOnTouch : false,
20172     showarrow : true,
20173     
20174     getAutoCreate : function()
20175     {
20176         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20177         
20178         cfg.cls += ' tab-content';
20179         
20180         if (this.carousel) {
20181             cfg.cls += ' carousel slide';
20182             
20183             cfg.cn = [{
20184                cls : 'carousel-inner',
20185                cn : []
20186             }];
20187         
20188             if(this.bullets  && !Roo.isTouch){
20189                 
20190                 var bullets = {
20191                     cls : 'carousel-bullets',
20192                     cn : []
20193                 };
20194                
20195                 if(this.bullets_cls){
20196                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20197                 }
20198                 
20199                 bullets.cn.push({
20200                     cls : 'clear'
20201                 });
20202                 
20203                 cfg.cn[0].cn.push(bullets);
20204             }
20205             
20206             if(this.showarrow){
20207                 cfg.cn[0].cn.push({
20208                     tag : 'div',
20209                     class : 'carousel-arrow',
20210                     cn : [
20211                         {
20212                             tag : 'div',
20213                             class : 'carousel-prev',
20214                             cn : [
20215                                 {
20216                                     tag : 'i',
20217                                     class : 'fa fa-chevron-left'
20218                                 }
20219                             ]
20220                         },
20221                         {
20222                             tag : 'div',
20223                             class : 'carousel-next',
20224                             cn : [
20225                                 {
20226                                     tag : 'i',
20227                                     class : 'fa fa-chevron-right'
20228                                 }
20229                             ]
20230                         }
20231                     ]
20232                 });
20233             }
20234             
20235         }
20236         
20237         return cfg;
20238     },
20239     
20240     initEvents:  function()
20241     {
20242 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20243 //            this.el.on("touchstart", this.onTouchStart, this);
20244 //        }
20245         
20246         if(this.autoslide){
20247             var _this = this;
20248             
20249             this.slideFn = window.setInterval(function() {
20250                 _this.showPanelNext();
20251             }, this.timer);
20252         }
20253         
20254         if(this.showarrow){
20255             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20256             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20257         }
20258         
20259         
20260     },
20261     
20262 //    onTouchStart : function(e, el, o)
20263 //    {
20264 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20265 //            return;
20266 //        }
20267 //        
20268 //        this.showPanelNext();
20269 //    },
20270     
20271     
20272     getChildContainer : function()
20273     {
20274         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20275     },
20276     
20277     /**
20278     * register a Navigation item
20279     * @param {Roo.bootstrap.NavItem} the navitem to add
20280     */
20281     register : function(item)
20282     {
20283         this.tabs.push( item);
20284         item.navId = this.navId; // not really needed..
20285         this.addBullet();
20286     
20287     },
20288     
20289     getActivePanel : function()
20290     {
20291         var r = false;
20292         Roo.each(this.tabs, function(t) {
20293             if (t.active) {
20294                 r = t;
20295                 return false;
20296             }
20297             return null;
20298         });
20299         return r;
20300         
20301     },
20302     getPanelByName : function(n)
20303     {
20304         var r = false;
20305         Roo.each(this.tabs, function(t) {
20306             if (t.tabId == n) {
20307                 r = t;
20308                 return false;
20309             }
20310             return null;
20311         });
20312         return r;
20313     },
20314     indexOfPanel : function(p)
20315     {
20316         var r = false;
20317         Roo.each(this.tabs, function(t,i) {
20318             if (t.tabId == p.tabId) {
20319                 r = i;
20320                 return false;
20321             }
20322             return null;
20323         });
20324         return r;
20325     },
20326     /**
20327      * show a specific panel
20328      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20329      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20330      */
20331     showPanel : function (pan)
20332     {
20333         if(this.transition || typeof(pan) == 'undefined'){
20334             Roo.log("waiting for the transitionend");
20335             return false;
20336         }
20337         
20338         if (typeof(pan) == 'number') {
20339             pan = this.tabs[pan];
20340         }
20341         
20342         if (typeof(pan) == 'string') {
20343             pan = this.getPanelByName(pan);
20344         }
20345         
20346         var cur = this.getActivePanel();
20347         
20348         if(!pan || !cur){
20349             Roo.log('pan or acitve pan is undefined');
20350             return false;
20351         }
20352         
20353         if (pan.tabId == this.getActivePanel().tabId) {
20354             return true;
20355         }
20356         
20357         if (false === cur.fireEvent('beforedeactivate')) {
20358             return false;
20359         }
20360         
20361         if(this.bullets > 0 && !Roo.isTouch){
20362             this.setActiveBullet(this.indexOfPanel(pan));
20363         }
20364         
20365         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20366             
20367             //class="carousel-item carousel-item-next carousel-item-left"
20368             
20369             this.transition = true;
20370             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20371             var lr = dir == 'next' ? 'left' : 'right';
20372             pan.el.addClass(dir); // or prev
20373             pan.el.addClass('carousel-item-' + dir); // or prev
20374             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20375             cur.el.addClass(lr); // or right
20376             pan.el.addClass(lr);
20377             cur.el.addClass('carousel-item-' +lr); // or right
20378             pan.el.addClass('carousel-item-' +lr);
20379             
20380             
20381             var _this = this;
20382             cur.el.on('transitionend', function() {
20383                 Roo.log("trans end?");
20384                 
20385                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20386                 pan.setActive(true);
20387                 
20388                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20389                 cur.setActive(false);
20390                 
20391                 _this.transition = false;
20392                 
20393             }, this, { single:  true } );
20394             
20395             return true;
20396         }
20397         
20398         cur.setActive(false);
20399         pan.setActive(true);
20400         
20401         return true;
20402         
20403     },
20404     showPanelNext : function()
20405     {
20406         var i = this.indexOfPanel(this.getActivePanel());
20407         
20408         if (i >= this.tabs.length - 1 && !this.autoslide) {
20409             return;
20410         }
20411         
20412         if (i >= this.tabs.length - 1 && this.autoslide) {
20413             i = -1;
20414         }
20415         
20416         this.showPanel(this.tabs[i+1]);
20417     },
20418     
20419     showPanelPrev : function()
20420     {
20421         var i = this.indexOfPanel(this.getActivePanel());
20422         
20423         if (i  < 1 && !this.autoslide) {
20424             return;
20425         }
20426         
20427         if (i < 1 && this.autoslide) {
20428             i = this.tabs.length;
20429         }
20430         
20431         this.showPanel(this.tabs[i-1]);
20432     },
20433     
20434     
20435     addBullet: function()
20436     {
20437         if(!this.bullets || Roo.isTouch){
20438             return;
20439         }
20440         var ctr = this.el.select('.carousel-bullets',true).first();
20441         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20442         var bullet = ctr.createChild({
20443             cls : 'bullet bullet-' + i
20444         },ctr.dom.lastChild);
20445         
20446         
20447         var _this = this;
20448         
20449         bullet.on('click', (function(e, el, o, ii, t){
20450
20451             e.preventDefault();
20452
20453             this.showPanel(ii);
20454
20455             if(this.autoslide && this.slideFn){
20456                 clearInterval(this.slideFn);
20457                 this.slideFn = window.setInterval(function() {
20458                     _this.showPanelNext();
20459                 }, this.timer);
20460             }
20461
20462         }).createDelegate(this, [i, bullet], true));
20463                 
20464         
20465     },
20466      
20467     setActiveBullet : function(i)
20468     {
20469         if(Roo.isTouch){
20470             return;
20471         }
20472         
20473         Roo.each(this.el.select('.bullet', true).elements, function(el){
20474             el.removeClass('selected');
20475         });
20476
20477         var bullet = this.el.select('.bullet-' + i, true).first();
20478         
20479         if(!bullet){
20480             return;
20481         }
20482         
20483         bullet.addClass('selected');
20484     }
20485     
20486     
20487   
20488 });
20489
20490  
20491
20492  
20493  
20494 Roo.apply(Roo.bootstrap.TabGroup, {
20495     
20496     groups: {},
20497      /**
20498     * register a Navigation Group
20499     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20500     */
20501     register : function(navgrp)
20502     {
20503         this.groups[navgrp.navId] = navgrp;
20504         
20505     },
20506     /**
20507     * fetch a Navigation Group based on the navigation ID
20508     * if one does not exist , it will get created.
20509     * @param {string} the navgroup to add
20510     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20511     */
20512     get: function(navId) {
20513         if (typeof(this.groups[navId]) == 'undefined') {
20514             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20515         }
20516         return this.groups[navId] ;
20517     }
20518     
20519     
20520     
20521 });
20522
20523  /*
20524  * - LGPL
20525  *
20526  * TabPanel
20527  * 
20528  */
20529
20530 /**
20531  * @class Roo.bootstrap.TabPanel
20532  * @extends Roo.bootstrap.Component
20533  * Bootstrap TabPanel class
20534  * @cfg {Boolean} active panel active
20535  * @cfg {String} html panel content
20536  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20537  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20538  * @cfg {String} href click to link..
20539  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20540  * 
20541  * 
20542  * @constructor
20543  * Create a new TabPanel
20544  * @param {Object} config The config object
20545  */
20546
20547 Roo.bootstrap.TabPanel = function(config){
20548     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20549     this.addEvents({
20550         /**
20551              * @event changed
20552              * Fires when the active status changes
20553              * @param {Roo.bootstrap.TabPanel} this
20554              * @param {Boolean} state the new state
20555             
20556          */
20557         'changed': true,
20558         /**
20559              * @event beforedeactivate
20560              * Fires before a tab is de-activated - can be used to do validation on a form.
20561              * @param {Roo.bootstrap.TabPanel} this
20562              * @return {Boolean} false if there is an error
20563             
20564          */
20565         'beforedeactivate': true
20566      });
20567     
20568     this.tabId = this.tabId || Roo.id();
20569   
20570 };
20571
20572 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20573     
20574     active: false,
20575     html: false,
20576     tabId: false,
20577     navId : false,
20578     href : '',
20579     touchSlide : false,
20580     getAutoCreate : function(){
20581         
20582         
20583         var cfg = {
20584             tag: 'div',
20585             // item is needed for carousel - not sure if it has any effect otherwise
20586             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20587             html: this.html || ''
20588         };
20589         
20590         if(this.active){
20591             cfg.cls += ' active';
20592         }
20593         
20594         if(this.tabId){
20595             cfg.tabId = this.tabId;
20596         }
20597         
20598         
20599         
20600         return cfg;
20601     },
20602     
20603     initEvents:  function()
20604     {
20605         var p = this.parent();
20606         
20607         this.navId = this.navId || p.navId;
20608         
20609         if (typeof(this.navId) != 'undefined') {
20610             // not really needed.. but just in case.. parent should be a NavGroup.
20611             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20612             
20613             tg.register(this);
20614             
20615             var i = tg.tabs.length - 1;
20616             
20617             if(this.active && tg.bullets > 0 && i < tg.bullets){
20618                 tg.setActiveBullet(i);
20619             }
20620         }
20621         
20622         this.el.on('click', this.onClick, this);
20623         
20624         if(Roo.isTouch && this.touchSlide){
20625             this.el.on("touchstart", this.onTouchStart, this);
20626             this.el.on("touchmove", this.onTouchMove, this);
20627             this.el.on("touchend", this.onTouchEnd, this);
20628         }
20629         
20630     },
20631     
20632     onRender : function(ct, position)
20633     {
20634         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20635     },
20636     
20637     setActive : function(state)
20638     {
20639         Roo.log("panel - set active " + this.tabId + "=" + state);
20640         
20641         this.active = state;
20642         if (!state) {
20643             this.el.removeClass('active');
20644             
20645         } else  if (!this.el.hasClass('active')) {
20646             this.el.addClass('active');
20647         }
20648         
20649         this.fireEvent('changed', this, state);
20650     },
20651     
20652     onClick : function(e)
20653     {
20654         e.preventDefault();
20655         
20656         if(!this.href.length){
20657             return;
20658         }
20659         
20660         window.location.href = this.href;
20661     },
20662     
20663     startX : 0,
20664     startY : 0,
20665     endX : 0,
20666     endY : 0,
20667     swiping : false,
20668     
20669     onTouchStart : function(e)
20670     {
20671         this.swiping = false;
20672         
20673         this.startX = e.browserEvent.touches[0].clientX;
20674         this.startY = e.browserEvent.touches[0].clientY;
20675     },
20676     
20677     onTouchMove : function(e)
20678     {
20679         this.swiping = true;
20680         
20681         this.endX = e.browserEvent.touches[0].clientX;
20682         this.endY = e.browserEvent.touches[0].clientY;
20683     },
20684     
20685     onTouchEnd : function(e)
20686     {
20687         if(!this.swiping){
20688             this.onClick(e);
20689             return;
20690         }
20691         
20692         var tabGroup = this.parent();
20693         
20694         if(this.endX > this.startX){ // swiping right
20695             tabGroup.showPanelPrev();
20696             return;
20697         }
20698         
20699         if(this.startX > this.endX){ // swiping left
20700             tabGroup.showPanelNext();
20701             return;
20702         }
20703     }
20704     
20705     
20706 });
20707  
20708
20709  
20710
20711  /*
20712  * - LGPL
20713  *
20714  * DateField
20715  * 
20716  */
20717
20718 /**
20719  * @class Roo.bootstrap.DateField
20720  * @extends Roo.bootstrap.Input
20721  * Bootstrap DateField class
20722  * @cfg {Number} weekStart default 0
20723  * @cfg {String} viewMode default empty, (months|years)
20724  * @cfg {String} minViewMode default empty, (months|years)
20725  * @cfg {Number} startDate default -Infinity
20726  * @cfg {Number} endDate default Infinity
20727  * @cfg {Boolean} todayHighlight default false
20728  * @cfg {Boolean} todayBtn default false
20729  * @cfg {Boolean} calendarWeeks default false
20730  * @cfg {Object} daysOfWeekDisabled default empty
20731  * @cfg {Boolean} singleMode default false (true | false)
20732  * 
20733  * @cfg {Boolean} keyboardNavigation default true
20734  * @cfg {String} language default en
20735  * 
20736  * @constructor
20737  * Create a new DateField
20738  * @param {Object} config The config object
20739  */
20740
20741 Roo.bootstrap.DateField = function(config){
20742     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20743      this.addEvents({
20744             /**
20745              * @event show
20746              * Fires when this field show.
20747              * @param {Roo.bootstrap.DateField} this
20748              * @param {Mixed} date The date value
20749              */
20750             show : true,
20751             /**
20752              * @event show
20753              * Fires when this field hide.
20754              * @param {Roo.bootstrap.DateField} this
20755              * @param {Mixed} date The date value
20756              */
20757             hide : true,
20758             /**
20759              * @event select
20760              * Fires when select a date.
20761              * @param {Roo.bootstrap.DateField} this
20762              * @param {Mixed} date The date value
20763              */
20764             select : true,
20765             /**
20766              * @event beforeselect
20767              * Fires when before select a date.
20768              * @param {Roo.bootstrap.DateField} this
20769              * @param {Mixed} date The date value
20770              */
20771             beforeselect : true
20772         });
20773 };
20774
20775 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20776     
20777     /**
20778      * @cfg {String} format
20779      * The default date format string which can be overriden for localization support.  The format must be
20780      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20781      */
20782     format : "m/d/y",
20783     /**
20784      * @cfg {String} altFormats
20785      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20786      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20787      */
20788     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20789     
20790     weekStart : 0,
20791     
20792     viewMode : '',
20793     
20794     minViewMode : '',
20795     
20796     todayHighlight : false,
20797     
20798     todayBtn: false,
20799     
20800     language: 'en',
20801     
20802     keyboardNavigation: true,
20803     
20804     calendarWeeks: false,
20805     
20806     startDate: -Infinity,
20807     
20808     endDate: Infinity,
20809     
20810     daysOfWeekDisabled: [],
20811     
20812     _events: [],
20813     
20814     singleMode : false,
20815     
20816     UTCDate: function()
20817     {
20818         return new Date(Date.UTC.apply(Date, arguments));
20819     },
20820     
20821     UTCToday: function()
20822     {
20823         var today = new Date();
20824         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20825     },
20826     
20827     getDate: function() {
20828             var d = this.getUTCDate();
20829             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20830     },
20831     
20832     getUTCDate: function() {
20833             return this.date;
20834     },
20835     
20836     setDate: function(d) {
20837             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20838     },
20839     
20840     setUTCDate: function(d) {
20841             this.date = d;
20842             this.setValue(this.formatDate(this.date));
20843     },
20844         
20845     onRender: function(ct, position)
20846     {
20847         
20848         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20849         
20850         this.language = this.language || 'en';
20851         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20852         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20853         
20854         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20855         this.format = this.format || 'm/d/y';
20856         this.isInline = false;
20857         this.isInput = true;
20858         this.component = this.el.select('.add-on', true).first() || false;
20859         this.component = (this.component && this.component.length === 0) ? false : this.component;
20860         this.hasInput = this.component && this.inputEl().length;
20861         
20862         if (typeof(this.minViewMode === 'string')) {
20863             switch (this.minViewMode) {
20864                 case 'months':
20865                     this.minViewMode = 1;
20866                     break;
20867                 case 'years':
20868                     this.minViewMode = 2;
20869                     break;
20870                 default:
20871                     this.minViewMode = 0;
20872                     break;
20873             }
20874         }
20875         
20876         if (typeof(this.viewMode === 'string')) {
20877             switch (this.viewMode) {
20878                 case 'months':
20879                     this.viewMode = 1;
20880                     break;
20881                 case 'years':
20882                     this.viewMode = 2;
20883                     break;
20884                 default:
20885                     this.viewMode = 0;
20886                     break;
20887             }
20888         }
20889                 
20890         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20891         
20892 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20893         
20894         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20895         
20896         this.picker().on('mousedown', this.onMousedown, this);
20897         this.picker().on('click', this.onClick, this);
20898         
20899         this.picker().addClass('datepicker-dropdown');
20900         
20901         this.startViewMode = this.viewMode;
20902         
20903         if(this.singleMode){
20904             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20905                 v.setVisibilityMode(Roo.Element.DISPLAY);
20906                 v.hide();
20907             });
20908             
20909             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20910                 v.setStyle('width', '189px');
20911             });
20912         }
20913         
20914         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20915             if(!this.calendarWeeks){
20916                 v.remove();
20917                 return;
20918             }
20919             
20920             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20921             v.attr('colspan', function(i, val){
20922                 return parseInt(val) + 1;
20923             });
20924         });
20925                         
20926         
20927         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20928         
20929         this.setStartDate(this.startDate);
20930         this.setEndDate(this.endDate);
20931         
20932         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20933         
20934         this.fillDow();
20935         this.fillMonths();
20936         this.update();
20937         this.showMode();
20938         
20939         if(this.isInline) {
20940             this.showPopup();
20941         }
20942     },
20943     
20944     picker : function()
20945     {
20946         return this.pickerEl;
20947 //        return this.el.select('.datepicker', true).first();
20948     },
20949     
20950     fillDow: function()
20951     {
20952         var dowCnt = this.weekStart;
20953         
20954         var dow = {
20955             tag: 'tr',
20956             cn: [
20957                 
20958             ]
20959         };
20960         
20961         if(this.calendarWeeks){
20962             dow.cn.push({
20963                 tag: 'th',
20964                 cls: 'cw',
20965                 html: '&nbsp;'
20966             })
20967         }
20968         
20969         while (dowCnt < this.weekStart + 7) {
20970             dow.cn.push({
20971                 tag: 'th',
20972                 cls: 'dow',
20973                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20974             });
20975         }
20976         
20977         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20978     },
20979     
20980     fillMonths: function()
20981     {    
20982         var i = 0;
20983         var months = this.picker().select('>.datepicker-months td', true).first();
20984         
20985         months.dom.innerHTML = '';
20986         
20987         while (i < 12) {
20988             var month = {
20989                 tag: 'span',
20990                 cls: 'month',
20991                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20992             };
20993             
20994             months.createChild(month);
20995         }
20996         
20997     },
20998     
20999     update: function()
21000     {
21001         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;
21002         
21003         if (this.date < this.startDate) {
21004             this.viewDate = new Date(this.startDate);
21005         } else if (this.date > this.endDate) {
21006             this.viewDate = new Date(this.endDate);
21007         } else {
21008             this.viewDate = new Date(this.date);
21009         }
21010         
21011         this.fill();
21012     },
21013     
21014     fill: function() 
21015     {
21016         var d = new Date(this.viewDate),
21017                 year = d.getUTCFullYear(),
21018                 month = d.getUTCMonth(),
21019                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21020                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21021                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21022                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21023                 currentDate = this.date && this.date.valueOf(),
21024                 today = this.UTCToday();
21025         
21026         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21027         
21028 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21029         
21030 //        this.picker.select('>tfoot th.today').
21031 //                                              .text(dates[this.language].today)
21032 //                                              .toggle(this.todayBtn !== false);
21033     
21034         this.updateNavArrows();
21035         this.fillMonths();
21036                                                 
21037         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21038         
21039         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21040          
21041         prevMonth.setUTCDate(day);
21042         
21043         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21044         
21045         var nextMonth = new Date(prevMonth);
21046         
21047         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21048         
21049         nextMonth = nextMonth.valueOf();
21050         
21051         var fillMonths = false;
21052         
21053         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21054         
21055         while(prevMonth.valueOf() <= nextMonth) {
21056             var clsName = '';
21057             
21058             if (prevMonth.getUTCDay() === this.weekStart) {
21059                 if(fillMonths){
21060                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21061                 }
21062                     
21063                 fillMonths = {
21064                     tag: 'tr',
21065                     cn: []
21066                 };
21067                 
21068                 if(this.calendarWeeks){
21069                     // ISO 8601: First week contains first thursday.
21070                     // ISO also states week starts on Monday, but we can be more abstract here.
21071                     var
21072                     // Start of current week: based on weekstart/current date
21073                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21074                     // Thursday of this week
21075                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21076                     // First Thursday of year, year from thursday
21077                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21078                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21079                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21080                     
21081                     fillMonths.cn.push({
21082                         tag: 'td',
21083                         cls: 'cw',
21084                         html: calWeek
21085                     });
21086                 }
21087             }
21088             
21089             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21090                 clsName += ' old';
21091             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21092                 clsName += ' new';
21093             }
21094             if (this.todayHighlight &&
21095                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21096                 prevMonth.getUTCMonth() == today.getMonth() &&
21097                 prevMonth.getUTCDate() == today.getDate()) {
21098                 clsName += ' today';
21099             }
21100             
21101             if (currentDate && prevMonth.valueOf() === currentDate) {
21102                 clsName += ' active';
21103             }
21104             
21105             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21106                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21107                     clsName += ' disabled';
21108             }
21109             
21110             fillMonths.cn.push({
21111                 tag: 'td',
21112                 cls: 'day ' + clsName,
21113                 html: prevMonth.getDate()
21114             });
21115             
21116             prevMonth.setDate(prevMonth.getDate()+1);
21117         }
21118           
21119         var currentYear = this.date && this.date.getUTCFullYear();
21120         var currentMonth = this.date && this.date.getUTCMonth();
21121         
21122         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21123         
21124         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21125             v.removeClass('active');
21126             
21127             if(currentYear === year && k === currentMonth){
21128                 v.addClass('active');
21129             }
21130             
21131             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21132                 v.addClass('disabled');
21133             }
21134             
21135         });
21136         
21137         
21138         year = parseInt(year/10, 10) * 10;
21139         
21140         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21141         
21142         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21143         
21144         year -= 1;
21145         for (var i = -1; i < 11; i++) {
21146             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21147                 tag: 'span',
21148                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21149                 html: year
21150             });
21151             
21152             year += 1;
21153         }
21154     },
21155     
21156     showMode: function(dir) 
21157     {
21158         if (dir) {
21159             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21160         }
21161         
21162         Roo.each(this.picker().select('>div',true).elements, function(v){
21163             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21164             v.hide();
21165         });
21166         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21167     },
21168     
21169     place: function()
21170     {
21171         if(this.isInline) {
21172             return;
21173         }
21174         
21175         this.picker().removeClass(['bottom', 'top']);
21176         
21177         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21178             /*
21179              * place to the top of element!
21180              *
21181              */
21182             
21183             this.picker().addClass('top');
21184             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21185             
21186             return;
21187         }
21188         
21189         this.picker().addClass('bottom');
21190         
21191         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21192     },
21193     
21194     parseDate : function(value)
21195     {
21196         if(!value || value instanceof Date){
21197             return value;
21198         }
21199         var v = Date.parseDate(value, this.format);
21200         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21201             v = Date.parseDate(value, 'Y-m-d');
21202         }
21203         if(!v && this.altFormats){
21204             if(!this.altFormatsArray){
21205                 this.altFormatsArray = this.altFormats.split("|");
21206             }
21207             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21208                 v = Date.parseDate(value, this.altFormatsArray[i]);
21209             }
21210         }
21211         return v;
21212     },
21213     
21214     formatDate : function(date, fmt)
21215     {   
21216         return (!date || !(date instanceof Date)) ?
21217         date : date.dateFormat(fmt || this.format);
21218     },
21219     
21220     onFocus : function()
21221     {
21222         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21223         this.showPopup();
21224     },
21225     
21226     onBlur : function()
21227     {
21228         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21229         
21230         var d = this.inputEl().getValue();
21231         
21232         this.setValue(d);
21233                 
21234         this.hidePopup();
21235     },
21236     
21237     showPopup : function()
21238     {
21239         this.picker().show();
21240         this.update();
21241         this.place();
21242         
21243         this.fireEvent('showpopup', this, this.date);
21244     },
21245     
21246     hidePopup : function()
21247     {
21248         if(this.isInline) {
21249             return;
21250         }
21251         this.picker().hide();
21252         this.viewMode = this.startViewMode;
21253         this.showMode();
21254         
21255         this.fireEvent('hidepopup', this, this.date);
21256         
21257     },
21258     
21259     onMousedown: function(e)
21260     {
21261         e.stopPropagation();
21262         e.preventDefault();
21263     },
21264     
21265     keyup: function(e)
21266     {
21267         Roo.bootstrap.DateField.superclass.keyup.call(this);
21268         this.update();
21269     },
21270
21271     setValue: function(v)
21272     {
21273         if(this.fireEvent('beforeselect', this, v) !== false){
21274             var d = new Date(this.parseDate(v) ).clearTime();
21275         
21276             if(isNaN(d.getTime())){
21277                 this.date = this.viewDate = '';
21278                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21279                 return;
21280             }
21281
21282             v = this.formatDate(d);
21283
21284             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21285
21286             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21287
21288             this.update();
21289
21290             this.fireEvent('select', this, this.date);
21291         }
21292     },
21293     
21294     getValue: function()
21295     {
21296         return this.formatDate(this.date);
21297     },
21298     
21299     fireKey: function(e)
21300     {
21301         if (!this.picker().isVisible()){
21302             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21303                 this.showPopup();
21304             }
21305             return;
21306         }
21307         
21308         var dateChanged = false,
21309         dir, day, month,
21310         newDate, newViewDate;
21311         
21312         switch(e.keyCode){
21313             case 27: // escape
21314                 this.hidePopup();
21315                 e.preventDefault();
21316                 break;
21317             case 37: // left
21318             case 39: // right
21319                 if (!this.keyboardNavigation) {
21320                     break;
21321                 }
21322                 dir = e.keyCode == 37 ? -1 : 1;
21323                 
21324                 if (e.ctrlKey){
21325                     newDate = this.moveYear(this.date, dir);
21326                     newViewDate = this.moveYear(this.viewDate, dir);
21327                 } else if (e.shiftKey){
21328                     newDate = this.moveMonth(this.date, dir);
21329                     newViewDate = this.moveMonth(this.viewDate, dir);
21330                 } else {
21331                     newDate = new Date(this.date);
21332                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21333                     newViewDate = new Date(this.viewDate);
21334                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21335                 }
21336                 if (this.dateWithinRange(newDate)){
21337                     this.date = newDate;
21338                     this.viewDate = newViewDate;
21339                     this.setValue(this.formatDate(this.date));
21340 //                    this.update();
21341                     e.preventDefault();
21342                     dateChanged = true;
21343                 }
21344                 break;
21345             case 38: // up
21346             case 40: // down
21347                 if (!this.keyboardNavigation) {
21348                     break;
21349                 }
21350                 dir = e.keyCode == 38 ? -1 : 1;
21351                 if (e.ctrlKey){
21352                     newDate = this.moveYear(this.date, dir);
21353                     newViewDate = this.moveYear(this.viewDate, dir);
21354                 } else if (e.shiftKey){
21355                     newDate = this.moveMonth(this.date, dir);
21356                     newViewDate = this.moveMonth(this.viewDate, dir);
21357                 } else {
21358                     newDate = new Date(this.date);
21359                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21360                     newViewDate = new Date(this.viewDate);
21361                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21362                 }
21363                 if (this.dateWithinRange(newDate)){
21364                     this.date = newDate;
21365                     this.viewDate = newViewDate;
21366                     this.setValue(this.formatDate(this.date));
21367 //                    this.update();
21368                     e.preventDefault();
21369                     dateChanged = true;
21370                 }
21371                 break;
21372             case 13: // enter
21373                 this.setValue(this.formatDate(this.date));
21374                 this.hidePopup();
21375                 e.preventDefault();
21376                 break;
21377             case 9: // tab
21378                 this.setValue(this.formatDate(this.date));
21379                 this.hidePopup();
21380                 break;
21381             case 16: // shift
21382             case 17: // ctrl
21383             case 18: // alt
21384                 break;
21385             default :
21386                 this.hidePopup();
21387                 
21388         }
21389     },
21390     
21391     
21392     onClick: function(e) 
21393     {
21394         e.stopPropagation();
21395         e.preventDefault();
21396         
21397         var target = e.getTarget();
21398         
21399         if(target.nodeName.toLowerCase() === 'i'){
21400             target = Roo.get(target).dom.parentNode;
21401         }
21402         
21403         var nodeName = target.nodeName;
21404         var className = target.className;
21405         var html = target.innerHTML;
21406         //Roo.log(nodeName);
21407         
21408         switch(nodeName.toLowerCase()) {
21409             case 'th':
21410                 switch(className) {
21411                     case 'switch':
21412                         this.showMode(1);
21413                         break;
21414                     case 'prev':
21415                     case 'next':
21416                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21417                         switch(this.viewMode){
21418                                 case 0:
21419                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21420                                         break;
21421                                 case 1:
21422                                 case 2:
21423                                         this.viewDate = this.moveYear(this.viewDate, dir);
21424                                         break;
21425                         }
21426                         this.fill();
21427                         break;
21428                     case 'today':
21429                         var date = new Date();
21430                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21431 //                        this.fill()
21432                         this.setValue(this.formatDate(this.date));
21433                         
21434                         this.hidePopup();
21435                         break;
21436                 }
21437                 break;
21438             case 'span':
21439                 if (className.indexOf('disabled') < 0) {
21440                     this.viewDate.setUTCDate(1);
21441                     if (className.indexOf('month') > -1) {
21442                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21443                     } else {
21444                         var year = parseInt(html, 10) || 0;
21445                         this.viewDate.setUTCFullYear(year);
21446                         
21447                     }
21448                     
21449                     if(this.singleMode){
21450                         this.setValue(this.formatDate(this.viewDate));
21451                         this.hidePopup();
21452                         return;
21453                     }
21454                     
21455                     this.showMode(-1);
21456                     this.fill();
21457                 }
21458                 break;
21459                 
21460             case 'td':
21461                 //Roo.log(className);
21462                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21463                     var day = parseInt(html, 10) || 1;
21464                     var year = this.viewDate.getUTCFullYear(),
21465                         month = this.viewDate.getUTCMonth();
21466
21467                     if (className.indexOf('old') > -1) {
21468                         if(month === 0 ){
21469                             month = 11;
21470                             year -= 1;
21471                         }else{
21472                             month -= 1;
21473                         }
21474                     } else if (className.indexOf('new') > -1) {
21475                         if (month == 11) {
21476                             month = 0;
21477                             year += 1;
21478                         } else {
21479                             month += 1;
21480                         }
21481                     }
21482                     //Roo.log([year,month,day]);
21483                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21484                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21485 //                    this.fill();
21486                     //Roo.log(this.formatDate(this.date));
21487                     this.setValue(this.formatDate(this.date));
21488                     this.hidePopup();
21489                 }
21490                 break;
21491         }
21492     },
21493     
21494     setStartDate: function(startDate)
21495     {
21496         this.startDate = startDate || -Infinity;
21497         if (this.startDate !== -Infinity) {
21498             this.startDate = this.parseDate(this.startDate);
21499         }
21500         this.update();
21501         this.updateNavArrows();
21502     },
21503
21504     setEndDate: function(endDate)
21505     {
21506         this.endDate = endDate || Infinity;
21507         if (this.endDate !== Infinity) {
21508             this.endDate = this.parseDate(this.endDate);
21509         }
21510         this.update();
21511         this.updateNavArrows();
21512     },
21513     
21514     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21515     {
21516         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21517         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21518             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21519         }
21520         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21521             return parseInt(d, 10);
21522         });
21523         this.update();
21524         this.updateNavArrows();
21525     },
21526     
21527     updateNavArrows: function() 
21528     {
21529         if(this.singleMode){
21530             return;
21531         }
21532         
21533         var d = new Date(this.viewDate),
21534         year = d.getUTCFullYear(),
21535         month = d.getUTCMonth();
21536         
21537         Roo.each(this.picker().select('.prev', true).elements, function(v){
21538             v.show();
21539             switch (this.viewMode) {
21540                 case 0:
21541
21542                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21543                         v.hide();
21544                     }
21545                     break;
21546                 case 1:
21547                 case 2:
21548                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21549                         v.hide();
21550                     }
21551                     break;
21552             }
21553         });
21554         
21555         Roo.each(this.picker().select('.next', true).elements, function(v){
21556             v.show();
21557             switch (this.viewMode) {
21558                 case 0:
21559
21560                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21561                         v.hide();
21562                     }
21563                     break;
21564                 case 1:
21565                 case 2:
21566                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21567                         v.hide();
21568                     }
21569                     break;
21570             }
21571         })
21572     },
21573     
21574     moveMonth: function(date, dir)
21575     {
21576         if (!dir) {
21577             return date;
21578         }
21579         var new_date = new Date(date.valueOf()),
21580         day = new_date.getUTCDate(),
21581         month = new_date.getUTCMonth(),
21582         mag = Math.abs(dir),
21583         new_month, test;
21584         dir = dir > 0 ? 1 : -1;
21585         if (mag == 1){
21586             test = dir == -1
21587             // If going back one month, make sure month is not current month
21588             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21589             ? function(){
21590                 return new_date.getUTCMonth() == month;
21591             }
21592             // If going forward one month, make sure month is as expected
21593             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21594             : function(){
21595                 return new_date.getUTCMonth() != new_month;
21596             };
21597             new_month = month + dir;
21598             new_date.setUTCMonth(new_month);
21599             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21600             if (new_month < 0 || new_month > 11) {
21601                 new_month = (new_month + 12) % 12;
21602             }
21603         } else {
21604             // For magnitudes >1, move one month at a time...
21605             for (var i=0; i<mag; i++) {
21606                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21607                 new_date = this.moveMonth(new_date, dir);
21608             }
21609             // ...then reset the day, keeping it in the new month
21610             new_month = new_date.getUTCMonth();
21611             new_date.setUTCDate(day);
21612             test = function(){
21613                 return new_month != new_date.getUTCMonth();
21614             };
21615         }
21616         // Common date-resetting loop -- if date is beyond end of month, make it
21617         // end of month
21618         while (test()){
21619             new_date.setUTCDate(--day);
21620             new_date.setUTCMonth(new_month);
21621         }
21622         return new_date;
21623     },
21624
21625     moveYear: function(date, dir)
21626     {
21627         return this.moveMonth(date, dir*12);
21628     },
21629
21630     dateWithinRange: function(date)
21631     {
21632         return date >= this.startDate && date <= this.endDate;
21633     },
21634
21635     
21636     remove: function() 
21637     {
21638         this.picker().remove();
21639     },
21640     
21641     validateValue : function(value)
21642     {
21643         if(this.getVisibilityEl().hasClass('hidden')){
21644             return true;
21645         }
21646         
21647         if(value.length < 1)  {
21648             if(this.allowBlank){
21649                 return true;
21650             }
21651             return false;
21652         }
21653         
21654         if(value.length < this.minLength){
21655             return false;
21656         }
21657         if(value.length > this.maxLength){
21658             return false;
21659         }
21660         if(this.vtype){
21661             var vt = Roo.form.VTypes;
21662             if(!vt[this.vtype](value, this)){
21663                 return false;
21664             }
21665         }
21666         if(typeof this.validator == "function"){
21667             var msg = this.validator(value);
21668             if(msg !== true){
21669                 return false;
21670             }
21671         }
21672         
21673         if(this.regex && !this.regex.test(value)){
21674             return false;
21675         }
21676         
21677         if(typeof(this.parseDate(value)) == 'undefined'){
21678             return false;
21679         }
21680         
21681         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21682             return false;
21683         }      
21684         
21685         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21686             return false;
21687         } 
21688         
21689         
21690         return true;
21691     },
21692     
21693     reset : function()
21694     {
21695         this.date = this.viewDate = '';
21696         
21697         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21698     }
21699    
21700 });
21701
21702 Roo.apply(Roo.bootstrap.DateField,  {
21703     
21704     head : {
21705         tag: 'thead',
21706         cn: [
21707         {
21708             tag: 'tr',
21709             cn: [
21710             {
21711                 tag: 'th',
21712                 cls: 'prev',
21713                 html: '<i class="fa fa-arrow-left"/>'
21714             },
21715             {
21716                 tag: 'th',
21717                 cls: 'switch',
21718                 colspan: '5'
21719             },
21720             {
21721                 tag: 'th',
21722                 cls: 'next',
21723                 html: '<i class="fa fa-arrow-right"/>'
21724             }
21725
21726             ]
21727         }
21728         ]
21729     },
21730     
21731     content : {
21732         tag: 'tbody',
21733         cn: [
21734         {
21735             tag: 'tr',
21736             cn: [
21737             {
21738                 tag: 'td',
21739                 colspan: '7'
21740             }
21741             ]
21742         }
21743         ]
21744     },
21745     
21746     footer : {
21747         tag: 'tfoot',
21748         cn: [
21749         {
21750             tag: 'tr',
21751             cn: [
21752             {
21753                 tag: 'th',
21754                 colspan: '7',
21755                 cls: 'today'
21756             }
21757                     
21758             ]
21759         }
21760         ]
21761     },
21762     
21763     dates:{
21764         en: {
21765             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21766             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21767             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21768             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21769             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21770             today: "Today"
21771         }
21772     },
21773     
21774     modes: [
21775     {
21776         clsName: 'days',
21777         navFnc: 'Month',
21778         navStep: 1
21779     },
21780     {
21781         clsName: 'months',
21782         navFnc: 'FullYear',
21783         navStep: 1
21784     },
21785     {
21786         clsName: 'years',
21787         navFnc: 'FullYear',
21788         navStep: 10
21789     }]
21790 });
21791
21792 Roo.apply(Roo.bootstrap.DateField,  {
21793   
21794     template : {
21795         tag: 'div',
21796         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21797         cn: [
21798         {
21799             tag: 'div',
21800             cls: 'datepicker-days',
21801             cn: [
21802             {
21803                 tag: 'table',
21804                 cls: 'table-condensed',
21805                 cn:[
21806                 Roo.bootstrap.DateField.head,
21807                 {
21808                     tag: 'tbody'
21809                 },
21810                 Roo.bootstrap.DateField.footer
21811                 ]
21812             }
21813             ]
21814         },
21815         {
21816             tag: 'div',
21817             cls: 'datepicker-months',
21818             cn: [
21819             {
21820                 tag: 'table',
21821                 cls: 'table-condensed',
21822                 cn:[
21823                 Roo.bootstrap.DateField.head,
21824                 Roo.bootstrap.DateField.content,
21825                 Roo.bootstrap.DateField.footer
21826                 ]
21827             }
21828             ]
21829         },
21830         {
21831             tag: 'div',
21832             cls: 'datepicker-years',
21833             cn: [
21834             {
21835                 tag: 'table',
21836                 cls: 'table-condensed',
21837                 cn:[
21838                 Roo.bootstrap.DateField.head,
21839                 Roo.bootstrap.DateField.content,
21840                 Roo.bootstrap.DateField.footer
21841                 ]
21842             }
21843             ]
21844         }
21845         ]
21846     }
21847 });
21848
21849  
21850
21851  /*
21852  * - LGPL
21853  *
21854  * TimeField
21855  * 
21856  */
21857
21858 /**
21859  * @class Roo.bootstrap.TimeField
21860  * @extends Roo.bootstrap.Input
21861  * Bootstrap DateField class
21862  * 
21863  * 
21864  * @constructor
21865  * Create a new TimeField
21866  * @param {Object} config The config object
21867  */
21868
21869 Roo.bootstrap.TimeField = function(config){
21870     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21871     this.addEvents({
21872             /**
21873              * @event show
21874              * Fires when this field show.
21875              * @param {Roo.bootstrap.DateField} thisthis
21876              * @param {Mixed} date The date value
21877              */
21878             show : true,
21879             /**
21880              * @event show
21881              * Fires when this field hide.
21882              * @param {Roo.bootstrap.DateField} this
21883              * @param {Mixed} date The date value
21884              */
21885             hide : true,
21886             /**
21887              * @event select
21888              * Fires when select a date.
21889              * @param {Roo.bootstrap.DateField} this
21890              * @param {Mixed} date The date value
21891              */
21892             select : true
21893         });
21894 };
21895
21896 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21897     
21898     /**
21899      * @cfg {String} format
21900      * The default time format string which can be overriden for localization support.  The format must be
21901      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21902      */
21903     format : "H:i",
21904        
21905     onRender: function(ct, position)
21906     {
21907         
21908         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21909                 
21910         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21911         
21912         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21913         
21914         this.pop = this.picker().select('>.datepicker-time',true).first();
21915         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21916         
21917         this.picker().on('mousedown', this.onMousedown, this);
21918         this.picker().on('click', this.onClick, this);
21919         
21920         this.picker().addClass('datepicker-dropdown');
21921     
21922         this.fillTime();
21923         this.update();
21924             
21925         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21926         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21927         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21928         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21929         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21930         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21931
21932     },
21933     
21934     fireKey: function(e){
21935         if (!this.picker().isVisible()){
21936             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21937                 this.show();
21938             }
21939             return;
21940         }
21941
21942         e.preventDefault();
21943         
21944         switch(e.keyCode){
21945             case 27: // escape
21946                 this.hide();
21947                 break;
21948             case 37: // left
21949             case 39: // right
21950                 this.onTogglePeriod();
21951                 break;
21952             case 38: // up
21953                 this.onIncrementMinutes();
21954                 break;
21955             case 40: // down
21956                 this.onDecrementMinutes();
21957                 break;
21958             case 13: // enter
21959             case 9: // tab
21960                 this.setTime();
21961                 break;
21962         }
21963     },
21964     
21965     onClick: function(e) {
21966         e.stopPropagation();
21967         e.preventDefault();
21968     },
21969     
21970     picker : function()
21971     {
21972         return this.el.select('.datepicker', true).first();
21973     },
21974     
21975     fillTime: function()
21976     {    
21977         var time = this.pop.select('tbody', true).first();
21978         
21979         time.dom.innerHTML = '';
21980         
21981         time.createChild({
21982             tag: 'tr',
21983             cn: [
21984                 {
21985                     tag: 'td',
21986                     cn: [
21987                         {
21988                             tag: 'a',
21989                             href: '#',
21990                             cls: 'btn',
21991                             cn: [
21992                                 {
21993                                     tag: 'span',
21994                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21995                                 }
21996                             ]
21997                         } 
21998                     ]
21999                 },
22000                 {
22001                     tag: 'td',
22002                     cls: 'separator'
22003                 },
22004                 {
22005                     tag: 'td',
22006                     cn: [
22007                         {
22008                             tag: 'a',
22009                             href: '#',
22010                             cls: 'btn',
22011                             cn: [
22012                                 {
22013                                     tag: 'span',
22014                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22015                                 }
22016                             ]
22017                         }
22018                     ]
22019                 },
22020                 {
22021                     tag: 'td',
22022                     cls: 'separator'
22023                 }
22024             ]
22025         });
22026         
22027         time.createChild({
22028             tag: 'tr',
22029             cn: [
22030                 {
22031                     tag: 'td',
22032                     cn: [
22033                         {
22034                             tag: 'span',
22035                             cls: 'timepicker-hour',
22036                             html: '00'
22037                         }  
22038                     ]
22039                 },
22040                 {
22041                     tag: 'td',
22042                     cls: 'separator',
22043                     html: ':'
22044                 },
22045                 {
22046                     tag: 'td',
22047                     cn: [
22048                         {
22049                             tag: 'span',
22050                             cls: 'timepicker-minute',
22051                             html: '00'
22052                         }  
22053                     ]
22054                 },
22055                 {
22056                     tag: 'td',
22057                     cls: 'separator'
22058                 },
22059                 {
22060                     tag: 'td',
22061                     cn: [
22062                         {
22063                             tag: 'button',
22064                             type: 'button',
22065                             cls: 'btn btn-primary period',
22066                             html: 'AM'
22067                             
22068                         }
22069                     ]
22070                 }
22071             ]
22072         });
22073         
22074         time.createChild({
22075             tag: 'tr',
22076             cn: [
22077                 {
22078                     tag: 'td',
22079                     cn: [
22080                         {
22081                             tag: 'a',
22082                             href: '#',
22083                             cls: 'btn',
22084                             cn: [
22085                                 {
22086                                     tag: 'span',
22087                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22088                                 }
22089                             ]
22090                         }
22091                     ]
22092                 },
22093                 {
22094                     tag: 'td',
22095                     cls: 'separator'
22096                 },
22097                 {
22098                     tag: 'td',
22099                     cn: [
22100                         {
22101                             tag: 'a',
22102                             href: '#',
22103                             cls: 'btn',
22104                             cn: [
22105                                 {
22106                                     tag: 'span',
22107                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22108                                 }
22109                             ]
22110                         }
22111                     ]
22112                 },
22113                 {
22114                     tag: 'td',
22115                     cls: 'separator'
22116                 }
22117             ]
22118         });
22119         
22120     },
22121     
22122     update: function()
22123     {
22124         
22125         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22126         
22127         this.fill();
22128     },
22129     
22130     fill: function() 
22131     {
22132         var hours = this.time.getHours();
22133         var minutes = this.time.getMinutes();
22134         var period = 'AM';
22135         
22136         if(hours > 11){
22137             period = 'PM';
22138         }
22139         
22140         if(hours == 0){
22141             hours = 12;
22142         }
22143         
22144         
22145         if(hours > 12){
22146             hours = hours - 12;
22147         }
22148         
22149         if(hours < 10){
22150             hours = '0' + hours;
22151         }
22152         
22153         if(minutes < 10){
22154             minutes = '0' + minutes;
22155         }
22156         
22157         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22158         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22159         this.pop.select('button', true).first().dom.innerHTML = period;
22160         
22161     },
22162     
22163     place: function()
22164     {   
22165         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22166         
22167         var cls = ['bottom'];
22168         
22169         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22170             cls.pop();
22171             cls.push('top');
22172         }
22173         
22174         cls.push('right');
22175         
22176         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22177             cls.pop();
22178             cls.push('left');
22179         }
22180         
22181         this.picker().addClass(cls.join('-'));
22182         
22183         var _this = this;
22184         
22185         Roo.each(cls, function(c){
22186             if(c == 'bottom'){
22187                 _this.picker().setTop(_this.inputEl().getHeight());
22188                 return;
22189             }
22190             if(c == 'top'){
22191                 _this.picker().setTop(0 - _this.picker().getHeight());
22192                 return;
22193             }
22194             
22195             if(c == 'left'){
22196                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22197                 return;
22198             }
22199             if(c == 'right'){
22200                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22201                 return;
22202             }
22203         });
22204         
22205     },
22206   
22207     onFocus : function()
22208     {
22209         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22210         this.show();
22211     },
22212     
22213     onBlur : function()
22214     {
22215         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22216         this.hide();
22217     },
22218     
22219     show : function()
22220     {
22221         this.picker().show();
22222         this.pop.show();
22223         this.update();
22224         this.place();
22225         
22226         this.fireEvent('show', this, this.date);
22227     },
22228     
22229     hide : function()
22230     {
22231         this.picker().hide();
22232         this.pop.hide();
22233         
22234         this.fireEvent('hide', this, this.date);
22235     },
22236     
22237     setTime : function()
22238     {
22239         this.hide();
22240         this.setValue(this.time.format(this.format));
22241         
22242         this.fireEvent('select', this, this.date);
22243         
22244         
22245     },
22246     
22247     onMousedown: function(e){
22248         e.stopPropagation();
22249         e.preventDefault();
22250     },
22251     
22252     onIncrementHours: function()
22253     {
22254         Roo.log('onIncrementHours');
22255         this.time = this.time.add(Date.HOUR, 1);
22256         this.update();
22257         
22258     },
22259     
22260     onDecrementHours: function()
22261     {
22262         Roo.log('onDecrementHours');
22263         this.time = this.time.add(Date.HOUR, -1);
22264         this.update();
22265     },
22266     
22267     onIncrementMinutes: function()
22268     {
22269         Roo.log('onIncrementMinutes');
22270         this.time = this.time.add(Date.MINUTE, 1);
22271         this.update();
22272     },
22273     
22274     onDecrementMinutes: function()
22275     {
22276         Roo.log('onDecrementMinutes');
22277         this.time = this.time.add(Date.MINUTE, -1);
22278         this.update();
22279     },
22280     
22281     onTogglePeriod: function()
22282     {
22283         Roo.log('onTogglePeriod');
22284         this.time = this.time.add(Date.HOUR, 12);
22285         this.update();
22286     }
22287     
22288    
22289 });
22290
22291 Roo.apply(Roo.bootstrap.TimeField,  {
22292     
22293     content : {
22294         tag: 'tbody',
22295         cn: [
22296             {
22297                 tag: 'tr',
22298                 cn: [
22299                 {
22300                     tag: 'td',
22301                     colspan: '7'
22302                 }
22303                 ]
22304             }
22305         ]
22306     },
22307     
22308     footer : {
22309         tag: 'tfoot',
22310         cn: [
22311             {
22312                 tag: 'tr',
22313                 cn: [
22314                 {
22315                     tag: 'th',
22316                     colspan: '7',
22317                     cls: '',
22318                     cn: [
22319                         {
22320                             tag: 'button',
22321                             cls: 'btn btn-info ok',
22322                             html: 'OK'
22323                         }
22324                     ]
22325                 }
22326
22327                 ]
22328             }
22329         ]
22330     }
22331 });
22332
22333 Roo.apply(Roo.bootstrap.TimeField,  {
22334   
22335     template : {
22336         tag: 'div',
22337         cls: 'datepicker dropdown-menu',
22338         cn: [
22339             {
22340                 tag: 'div',
22341                 cls: 'datepicker-time',
22342                 cn: [
22343                 {
22344                     tag: 'table',
22345                     cls: 'table-condensed',
22346                     cn:[
22347                     Roo.bootstrap.TimeField.content,
22348                     Roo.bootstrap.TimeField.footer
22349                     ]
22350                 }
22351                 ]
22352             }
22353         ]
22354     }
22355 });
22356
22357  
22358
22359  /*
22360  * - LGPL
22361  *
22362  * MonthField
22363  * 
22364  */
22365
22366 /**
22367  * @class Roo.bootstrap.MonthField
22368  * @extends Roo.bootstrap.Input
22369  * Bootstrap MonthField class
22370  * 
22371  * @cfg {String} language default en
22372  * 
22373  * @constructor
22374  * Create a new MonthField
22375  * @param {Object} config The config object
22376  */
22377
22378 Roo.bootstrap.MonthField = function(config){
22379     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22380     
22381     this.addEvents({
22382         /**
22383          * @event show
22384          * Fires when this field show.
22385          * @param {Roo.bootstrap.MonthField} this
22386          * @param {Mixed} date The date value
22387          */
22388         show : true,
22389         /**
22390          * @event show
22391          * Fires when this field hide.
22392          * @param {Roo.bootstrap.MonthField} this
22393          * @param {Mixed} date The date value
22394          */
22395         hide : true,
22396         /**
22397          * @event select
22398          * Fires when select a date.
22399          * @param {Roo.bootstrap.MonthField} this
22400          * @param {String} oldvalue The old value
22401          * @param {String} newvalue The new value
22402          */
22403         select : true
22404     });
22405 };
22406
22407 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22408     
22409     onRender: function(ct, position)
22410     {
22411         
22412         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22413         
22414         this.language = this.language || 'en';
22415         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22416         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22417         
22418         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22419         this.isInline = false;
22420         this.isInput = true;
22421         this.component = this.el.select('.add-on', true).first() || false;
22422         this.component = (this.component && this.component.length === 0) ? false : this.component;
22423         this.hasInput = this.component && this.inputEL().length;
22424         
22425         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22426         
22427         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22428         
22429         this.picker().on('mousedown', this.onMousedown, this);
22430         this.picker().on('click', this.onClick, this);
22431         
22432         this.picker().addClass('datepicker-dropdown');
22433         
22434         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22435             v.setStyle('width', '189px');
22436         });
22437         
22438         this.fillMonths();
22439         
22440         this.update();
22441         
22442         if(this.isInline) {
22443             this.show();
22444         }
22445         
22446     },
22447     
22448     setValue: function(v, suppressEvent)
22449     {   
22450         var o = this.getValue();
22451         
22452         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22453         
22454         this.update();
22455
22456         if(suppressEvent !== true){
22457             this.fireEvent('select', this, o, v);
22458         }
22459         
22460     },
22461     
22462     getValue: function()
22463     {
22464         return this.value;
22465     },
22466     
22467     onClick: function(e) 
22468     {
22469         e.stopPropagation();
22470         e.preventDefault();
22471         
22472         var target = e.getTarget();
22473         
22474         if(target.nodeName.toLowerCase() === 'i'){
22475             target = Roo.get(target).dom.parentNode;
22476         }
22477         
22478         var nodeName = target.nodeName;
22479         var className = target.className;
22480         var html = target.innerHTML;
22481         
22482         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22483             return;
22484         }
22485         
22486         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22487         
22488         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22489         
22490         this.hide();
22491                         
22492     },
22493     
22494     picker : function()
22495     {
22496         return this.pickerEl;
22497     },
22498     
22499     fillMonths: function()
22500     {    
22501         var i = 0;
22502         var months = this.picker().select('>.datepicker-months td', true).first();
22503         
22504         months.dom.innerHTML = '';
22505         
22506         while (i < 12) {
22507             var month = {
22508                 tag: 'span',
22509                 cls: 'month',
22510                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22511             };
22512             
22513             months.createChild(month);
22514         }
22515         
22516     },
22517     
22518     update: function()
22519     {
22520         var _this = this;
22521         
22522         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22523             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22524         }
22525         
22526         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22527             e.removeClass('active');
22528             
22529             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22530                 e.addClass('active');
22531             }
22532         })
22533     },
22534     
22535     place: function()
22536     {
22537         if(this.isInline) {
22538             return;
22539         }
22540         
22541         this.picker().removeClass(['bottom', 'top']);
22542         
22543         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22544             /*
22545              * place to the top of element!
22546              *
22547              */
22548             
22549             this.picker().addClass('top');
22550             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22551             
22552             return;
22553         }
22554         
22555         this.picker().addClass('bottom');
22556         
22557         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22558     },
22559     
22560     onFocus : function()
22561     {
22562         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22563         this.show();
22564     },
22565     
22566     onBlur : function()
22567     {
22568         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22569         
22570         var d = this.inputEl().getValue();
22571         
22572         this.setValue(d);
22573                 
22574         this.hide();
22575     },
22576     
22577     show : function()
22578     {
22579         this.picker().show();
22580         this.picker().select('>.datepicker-months', true).first().show();
22581         this.update();
22582         this.place();
22583         
22584         this.fireEvent('show', this, this.date);
22585     },
22586     
22587     hide : function()
22588     {
22589         if(this.isInline) {
22590             return;
22591         }
22592         this.picker().hide();
22593         this.fireEvent('hide', this, this.date);
22594         
22595     },
22596     
22597     onMousedown: function(e)
22598     {
22599         e.stopPropagation();
22600         e.preventDefault();
22601     },
22602     
22603     keyup: function(e)
22604     {
22605         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22606         this.update();
22607     },
22608
22609     fireKey: function(e)
22610     {
22611         if (!this.picker().isVisible()){
22612             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22613                 this.show();
22614             }
22615             return;
22616         }
22617         
22618         var dir;
22619         
22620         switch(e.keyCode){
22621             case 27: // escape
22622                 this.hide();
22623                 e.preventDefault();
22624                 break;
22625             case 37: // left
22626             case 39: // right
22627                 dir = e.keyCode == 37 ? -1 : 1;
22628                 
22629                 this.vIndex = this.vIndex + dir;
22630                 
22631                 if(this.vIndex < 0){
22632                     this.vIndex = 0;
22633                 }
22634                 
22635                 if(this.vIndex > 11){
22636                     this.vIndex = 11;
22637                 }
22638                 
22639                 if(isNaN(this.vIndex)){
22640                     this.vIndex = 0;
22641                 }
22642                 
22643                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22644                 
22645                 break;
22646             case 38: // up
22647             case 40: // down
22648                 
22649                 dir = e.keyCode == 38 ? -1 : 1;
22650                 
22651                 this.vIndex = this.vIndex + dir * 4;
22652                 
22653                 if(this.vIndex < 0){
22654                     this.vIndex = 0;
22655                 }
22656                 
22657                 if(this.vIndex > 11){
22658                     this.vIndex = 11;
22659                 }
22660                 
22661                 if(isNaN(this.vIndex)){
22662                     this.vIndex = 0;
22663                 }
22664                 
22665                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22666                 break;
22667                 
22668             case 13: // enter
22669                 
22670                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22671                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22672                 }
22673                 
22674                 this.hide();
22675                 e.preventDefault();
22676                 break;
22677             case 9: // tab
22678                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22679                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22680                 }
22681                 this.hide();
22682                 break;
22683             case 16: // shift
22684             case 17: // ctrl
22685             case 18: // alt
22686                 break;
22687             default :
22688                 this.hide();
22689                 
22690         }
22691     },
22692     
22693     remove: function() 
22694     {
22695         this.picker().remove();
22696     }
22697    
22698 });
22699
22700 Roo.apply(Roo.bootstrap.MonthField,  {
22701     
22702     content : {
22703         tag: 'tbody',
22704         cn: [
22705         {
22706             tag: 'tr',
22707             cn: [
22708             {
22709                 tag: 'td',
22710                 colspan: '7'
22711             }
22712             ]
22713         }
22714         ]
22715     },
22716     
22717     dates:{
22718         en: {
22719             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22720             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22721         }
22722     }
22723 });
22724
22725 Roo.apply(Roo.bootstrap.MonthField,  {
22726   
22727     template : {
22728         tag: 'div',
22729         cls: 'datepicker dropdown-menu roo-dynamic',
22730         cn: [
22731             {
22732                 tag: 'div',
22733                 cls: 'datepicker-months',
22734                 cn: [
22735                 {
22736                     tag: 'table',
22737                     cls: 'table-condensed',
22738                     cn:[
22739                         Roo.bootstrap.DateField.content
22740                     ]
22741                 }
22742                 ]
22743             }
22744         ]
22745     }
22746 });
22747
22748  
22749
22750  
22751  /*
22752  * - LGPL
22753  *
22754  * CheckBox
22755  * 
22756  */
22757
22758 /**
22759  * @class Roo.bootstrap.CheckBox
22760  * @extends Roo.bootstrap.Input
22761  * Bootstrap CheckBox class
22762  * 
22763  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22764  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22765  * @cfg {String} boxLabel The text that appears beside the checkbox
22766  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22767  * @cfg {Boolean} checked initnal the element
22768  * @cfg {Boolean} inline inline the element (default false)
22769  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22770  * @cfg {String} tooltip label tooltip
22771  * 
22772  * @constructor
22773  * Create a new CheckBox
22774  * @param {Object} config The config object
22775  */
22776
22777 Roo.bootstrap.CheckBox = function(config){
22778     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22779    
22780     this.addEvents({
22781         /**
22782         * @event check
22783         * Fires when the element is checked or unchecked.
22784         * @param {Roo.bootstrap.CheckBox} this This input
22785         * @param {Boolean} checked The new checked value
22786         */
22787        check : true,
22788        /**
22789         * @event click
22790         * Fires when the element is click.
22791         * @param {Roo.bootstrap.CheckBox} this This input
22792         */
22793        click : true
22794     });
22795     
22796 };
22797
22798 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22799   
22800     inputType: 'checkbox',
22801     inputValue: 1,
22802     valueOff: 0,
22803     boxLabel: false,
22804     checked: false,
22805     weight : false,
22806     inline: false,
22807     tooltip : '',
22808     
22809     // checkbox success does not make any sense really.. 
22810     invalidClass : "",
22811     validClass : "",
22812     
22813     
22814     getAutoCreate : function()
22815     {
22816         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22817         
22818         var id = Roo.id();
22819         
22820         var cfg = {};
22821         
22822         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22823         
22824         if(this.inline){
22825             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22826         }
22827         
22828         var input =  {
22829             tag: 'input',
22830             id : id,
22831             type : this.inputType,
22832             value : this.inputValue,
22833             cls : 'roo-' + this.inputType, //'form-box',
22834             placeholder : this.placeholder || ''
22835             
22836         };
22837         
22838         if(this.inputType != 'radio'){
22839             var hidden =  {
22840                 tag: 'input',
22841                 type : 'hidden',
22842                 cls : 'roo-hidden-value',
22843                 value : this.checked ? this.inputValue : this.valueOff
22844             };
22845         }
22846         
22847             
22848         if (this.weight) { // Validity check?
22849             cfg.cls += " " + this.inputType + "-" + this.weight;
22850         }
22851         
22852         if (this.disabled) {
22853             input.disabled=true;
22854         }
22855         
22856         if(this.checked){
22857             input.checked = this.checked;
22858         }
22859         
22860         if (this.name) {
22861             
22862             input.name = this.name;
22863             
22864             if(this.inputType != 'radio'){
22865                 hidden.name = this.name;
22866                 input.name = '_hidden_' + this.name;
22867             }
22868         }
22869         
22870         if (this.size) {
22871             input.cls += ' input-' + this.size;
22872         }
22873         
22874         var settings=this;
22875         
22876         ['xs','sm','md','lg'].map(function(size){
22877             if (settings[size]) {
22878                 cfg.cls += ' col-' + size + '-' + settings[size];
22879             }
22880         });
22881         
22882         var inputblock = input;
22883          
22884         if (this.before || this.after) {
22885             
22886             inputblock = {
22887                 cls : 'input-group',
22888                 cn :  [] 
22889             };
22890             
22891             if (this.before) {
22892                 inputblock.cn.push({
22893                     tag :'span',
22894                     cls : 'input-group-addon',
22895                     html : this.before
22896                 });
22897             }
22898             
22899             inputblock.cn.push(input);
22900             
22901             if(this.inputType != 'radio'){
22902                 inputblock.cn.push(hidden);
22903             }
22904             
22905             if (this.after) {
22906                 inputblock.cn.push({
22907                     tag :'span',
22908                     cls : 'input-group-addon',
22909                     html : this.after
22910                 });
22911             }
22912             
22913         }
22914         var boxLabelCfg = false;
22915         
22916         if(this.boxLabel){
22917            
22918             boxLabelCfg = {
22919                 tag: 'label',
22920                 //'for': id, // box label is handled by onclick - so no for...
22921                 cls: 'box-label',
22922                 html: this.boxLabel
22923             };
22924             if(this.tooltip){
22925                 boxLabelCfg.tooltip = this.tooltip;
22926             }
22927              
22928         }
22929         
22930         
22931         if (align ==='left' && this.fieldLabel.length) {
22932 //                Roo.log("left and has label");
22933             cfg.cn = [
22934                 {
22935                     tag: 'label',
22936                     'for' :  id,
22937                     cls : 'control-label',
22938                     html : this.fieldLabel
22939                 },
22940                 {
22941                     cls : "", 
22942                     cn: [
22943                         inputblock
22944                     ]
22945                 }
22946             ];
22947             
22948             if (boxLabelCfg) {
22949                 cfg.cn[1].cn.push(boxLabelCfg);
22950             }
22951             
22952             if(this.labelWidth > 12){
22953                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22954             }
22955             
22956             if(this.labelWidth < 13 && this.labelmd == 0){
22957                 this.labelmd = this.labelWidth;
22958             }
22959             
22960             if(this.labellg > 0){
22961                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22962                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22963             }
22964             
22965             if(this.labelmd > 0){
22966                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22967                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22968             }
22969             
22970             if(this.labelsm > 0){
22971                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22972                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22973             }
22974             
22975             if(this.labelxs > 0){
22976                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22977                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22978             }
22979             
22980         } else if ( this.fieldLabel.length) {
22981 //                Roo.log(" label");
22982                 cfg.cn = [
22983                    
22984                     {
22985                         tag: this.boxLabel ? 'span' : 'label',
22986                         'for': id,
22987                         cls: 'control-label box-input-label',
22988                         //cls : 'input-group-addon',
22989                         html : this.fieldLabel
22990                     },
22991                     
22992                     inputblock
22993                     
22994                 ];
22995                 if (boxLabelCfg) {
22996                     cfg.cn.push(boxLabelCfg);
22997                 }
22998
22999         } else {
23000             
23001 //                Roo.log(" no label && no align");
23002                 cfg.cn = [  inputblock ] ;
23003                 if (boxLabelCfg) {
23004                     cfg.cn.push(boxLabelCfg);
23005                 }
23006
23007                 
23008         }
23009         
23010        
23011         
23012         if(this.inputType != 'radio'){
23013             cfg.cn.push(hidden);
23014         }
23015         
23016         return cfg;
23017         
23018     },
23019     
23020     /**
23021      * return the real input element.
23022      */
23023     inputEl: function ()
23024     {
23025         return this.el.select('input.roo-' + this.inputType,true).first();
23026     },
23027     hiddenEl: function ()
23028     {
23029         return this.el.select('input.roo-hidden-value',true).first();
23030     },
23031     
23032     labelEl: function()
23033     {
23034         return this.el.select('label.control-label',true).first();
23035     },
23036     /* depricated... */
23037     
23038     label: function()
23039     {
23040         return this.labelEl();
23041     },
23042     
23043     boxLabelEl: function()
23044     {
23045         return this.el.select('label.box-label',true).first();
23046     },
23047     
23048     initEvents : function()
23049     {
23050 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23051         
23052         this.inputEl().on('click', this.onClick,  this);
23053         
23054         if (this.boxLabel) { 
23055             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23056         }
23057         
23058         this.startValue = this.getValue();
23059         
23060         if(this.groupId){
23061             Roo.bootstrap.CheckBox.register(this);
23062         }
23063     },
23064     
23065     onClick : function(e)
23066     {   
23067         if(this.fireEvent('click', this, e) !== false){
23068             this.setChecked(!this.checked);
23069         }
23070         
23071     },
23072     
23073     setChecked : function(state,suppressEvent)
23074     {
23075         this.startValue = this.getValue();
23076
23077         if(this.inputType == 'radio'){
23078             
23079             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23080                 e.dom.checked = false;
23081             });
23082             
23083             this.inputEl().dom.checked = true;
23084             
23085             this.inputEl().dom.value = this.inputValue;
23086             
23087             if(suppressEvent !== true){
23088                 this.fireEvent('check', this, true);
23089             }
23090             
23091             this.validate();
23092             
23093             return;
23094         }
23095         
23096         this.checked = state;
23097         
23098         this.inputEl().dom.checked = state;
23099         
23100         
23101         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23102         
23103         if(suppressEvent !== true){
23104             this.fireEvent('check', this, state);
23105         }
23106         
23107         this.validate();
23108     },
23109     
23110     getValue : function()
23111     {
23112         if(this.inputType == 'radio'){
23113             return this.getGroupValue();
23114         }
23115         
23116         return this.hiddenEl().dom.value;
23117         
23118     },
23119     
23120     getGroupValue : function()
23121     {
23122         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23123             return '';
23124         }
23125         
23126         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23127     },
23128     
23129     setValue : function(v,suppressEvent)
23130     {
23131         if(this.inputType == 'radio'){
23132             this.setGroupValue(v, suppressEvent);
23133             return;
23134         }
23135         
23136         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23137         
23138         this.validate();
23139     },
23140     
23141     setGroupValue : function(v, suppressEvent)
23142     {
23143         this.startValue = this.getValue();
23144         
23145         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23146             e.dom.checked = false;
23147             
23148             if(e.dom.value == v){
23149                 e.dom.checked = true;
23150             }
23151         });
23152         
23153         if(suppressEvent !== true){
23154             this.fireEvent('check', this, true);
23155         }
23156
23157         this.validate();
23158         
23159         return;
23160     },
23161     
23162     validate : function()
23163     {
23164         if(this.getVisibilityEl().hasClass('hidden')){
23165             return true;
23166         }
23167         
23168         if(
23169                 this.disabled || 
23170                 (this.inputType == 'radio' && this.validateRadio()) ||
23171                 (this.inputType == 'checkbox' && this.validateCheckbox())
23172         ){
23173             this.markValid();
23174             return true;
23175         }
23176         
23177         this.markInvalid();
23178         return false;
23179     },
23180     
23181     validateRadio : function()
23182     {
23183         if(this.getVisibilityEl().hasClass('hidden')){
23184             return true;
23185         }
23186         
23187         if(this.allowBlank){
23188             return true;
23189         }
23190         
23191         var valid = false;
23192         
23193         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23194             if(!e.dom.checked){
23195                 return;
23196             }
23197             
23198             valid = true;
23199             
23200             return false;
23201         });
23202         
23203         return valid;
23204     },
23205     
23206     validateCheckbox : function()
23207     {
23208         if(!this.groupId){
23209             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23210             //return (this.getValue() == this.inputValue) ? true : false;
23211         }
23212         
23213         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23214         
23215         if(!group){
23216             return false;
23217         }
23218         
23219         var r = false;
23220         
23221         for(var i in group){
23222             if(group[i].el.isVisible(true)){
23223                 r = false;
23224                 break;
23225             }
23226             
23227             r = true;
23228         }
23229         
23230         for(var i in group){
23231             if(r){
23232                 break;
23233             }
23234             
23235             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23236         }
23237         
23238         return r;
23239     },
23240     
23241     /**
23242      * Mark this field as valid
23243      */
23244     markValid : function()
23245     {
23246         var _this = this;
23247         
23248         this.fireEvent('valid', this);
23249         
23250         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23251         
23252         if(this.groupId){
23253             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23254         }
23255         
23256         if(label){
23257             label.markValid();
23258         }
23259
23260         if(this.inputType == 'radio'){
23261             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23262                 var fg = e.findParent('.form-group', false, true);
23263                 if (Roo.bootstrap.version == 3) {
23264                     fg.removeClass([_this.invalidClass, _this.validClass]);
23265                     fg.addClass(_this.validClass);
23266                 } else {
23267                     fg.removeClass(['is-valid', 'is-invalid']);
23268                     fg.addClass('is-valid');
23269                 }
23270             });
23271             
23272             return;
23273         }
23274
23275         if(!this.groupId){
23276             var fg = this.el.findParent('.form-group', false, true);
23277             if (Roo.bootstrap.version == 3) {
23278                 fg.removeClass([this.invalidClass, this.validClass]);
23279                 fg.addClass(this.validClass);
23280             } else {
23281                 fg.removeClass(['is-valid', 'is-invalid']);
23282                 fg.addClass('is-valid');
23283             }
23284             return;
23285         }
23286         
23287         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23288         
23289         if(!group){
23290             return;
23291         }
23292         
23293         for(var i in group){
23294             var fg = group[i].el.findParent('.form-group', false, true);
23295             if (Roo.bootstrap.version == 3) {
23296                 fg.removeClass([this.invalidClass, this.validClass]);
23297                 fg.addClass(this.validClass);
23298             } else {
23299                 fg.removeClass(['is-valid', 'is-invalid']);
23300                 fg.addClass('is-valid');
23301             }
23302         }
23303     },
23304     
23305      /**
23306      * Mark this field as invalid
23307      * @param {String} msg The validation message
23308      */
23309     markInvalid : function(msg)
23310     {
23311         if(this.allowBlank){
23312             return;
23313         }
23314         
23315         var _this = this;
23316         
23317         this.fireEvent('invalid', this, msg);
23318         
23319         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23320         
23321         if(this.groupId){
23322             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23323         }
23324         
23325         if(label){
23326             label.markInvalid();
23327         }
23328             
23329         if(this.inputType == 'radio'){
23330             
23331             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23332                 var fg = e.findParent('.form-group', false, true);
23333                 if (Roo.bootstrap.version == 3) {
23334                     fg.removeClass([_this.invalidClass, _this.validClass]);
23335                     fg.addClass(_this.invalidClass);
23336                 } else {
23337                     fg.removeClass(['is-invalid', 'is-valid']);
23338                     fg.addClass('is-invalid');
23339                 }
23340             });
23341             
23342             return;
23343         }
23344         
23345         if(!this.groupId){
23346             var fg = this.el.findParent('.form-group', false, true);
23347             if (Roo.bootstrap.version == 3) {
23348                 fg.removeClass([_this.invalidClass, _this.validClass]);
23349                 fg.addClass(_this.invalidClass);
23350             } else {
23351                 fg.removeClass(['is-invalid', 'is-valid']);
23352                 fg.addClass('is-invalid');
23353             }
23354             return;
23355         }
23356         
23357         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23358         
23359         if(!group){
23360             return;
23361         }
23362         
23363         for(var i in group){
23364             var fg = group[i].el.findParent('.form-group', false, true);
23365             if (Roo.bootstrap.version == 3) {
23366                 fg.removeClass([_this.invalidClass, _this.validClass]);
23367                 fg.addClass(_this.invalidClass);
23368             } else {
23369                 fg.removeClass(['is-invalid', 'is-valid']);
23370                 fg.addClass('is-invalid');
23371             }
23372         }
23373         
23374     },
23375     
23376     clearInvalid : function()
23377     {
23378         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23379         
23380         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23381         
23382         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23383         
23384         if (label && label.iconEl) {
23385             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23386             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23387         }
23388     },
23389     
23390     disable : function()
23391     {
23392         if(this.inputType != 'radio'){
23393             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23394             return;
23395         }
23396         
23397         var _this = this;
23398         
23399         if(this.rendered){
23400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23401                 _this.getActionEl().addClass(this.disabledClass);
23402                 e.dom.disabled = true;
23403             });
23404         }
23405         
23406         this.disabled = true;
23407         this.fireEvent("disable", this);
23408         return this;
23409     },
23410
23411     enable : function()
23412     {
23413         if(this.inputType != 'radio'){
23414             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23415             return;
23416         }
23417         
23418         var _this = this;
23419         
23420         if(this.rendered){
23421             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23422                 _this.getActionEl().removeClass(this.disabledClass);
23423                 e.dom.disabled = false;
23424             });
23425         }
23426         
23427         this.disabled = false;
23428         this.fireEvent("enable", this);
23429         return this;
23430     },
23431     
23432     setBoxLabel : function(v)
23433     {
23434         this.boxLabel = v;
23435         
23436         if(this.rendered){
23437             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23438         }
23439     }
23440
23441 });
23442
23443 Roo.apply(Roo.bootstrap.CheckBox, {
23444     
23445     groups: {},
23446     
23447      /**
23448     * register a CheckBox Group
23449     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23450     */
23451     register : function(checkbox)
23452     {
23453         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23454             this.groups[checkbox.groupId] = {};
23455         }
23456         
23457         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23458             return;
23459         }
23460         
23461         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23462         
23463     },
23464     /**
23465     * fetch a CheckBox Group based on the group ID
23466     * @param {string} the group ID
23467     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23468     */
23469     get: function(groupId) {
23470         if (typeof(this.groups[groupId]) == 'undefined') {
23471             return false;
23472         }
23473         
23474         return this.groups[groupId] ;
23475     }
23476     
23477     
23478 });
23479 /*
23480  * - LGPL
23481  *
23482  * RadioItem
23483  * 
23484  */
23485
23486 /**
23487  * @class Roo.bootstrap.Radio
23488  * @extends Roo.bootstrap.Component
23489  * Bootstrap Radio class
23490  * @cfg {String} boxLabel - the label associated
23491  * @cfg {String} value - the value of radio
23492  * 
23493  * @constructor
23494  * Create a new Radio
23495  * @param {Object} config The config object
23496  */
23497 Roo.bootstrap.Radio = function(config){
23498     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23499     
23500 };
23501
23502 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23503     
23504     boxLabel : '',
23505     
23506     value : '',
23507     
23508     getAutoCreate : function()
23509     {
23510         var cfg = {
23511             tag : 'div',
23512             cls : 'form-group radio',
23513             cn : [
23514                 {
23515                     tag : 'label',
23516                     cls : 'box-label',
23517                     html : this.boxLabel
23518                 }
23519             ]
23520         };
23521         
23522         return cfg;
23523     },
23524     
23525     initEvents : function() 
23526     {
23527         this.parent().register(this);
23528         
23529         this.el.on('click', this.onClick, this);
23530         
23531     },
23532     
23533     onClick : function(e)
23534     {
23535         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23536             this.setChecked(true);
23537         }
23538     },
23539     
23540     setChecked : function(state, suppressEvent)
23541     {
23542         this.parent().setValue(this.value, suppressEvent);
23543         
23544     },
23545     
23546     setBoxLabel : function(v)
23547     {
23548         this.boxLabel = v;
23549         
23550         if(this.rendered){
23551             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23552         }
23553     }
23554     
23555 });
23556  
23557
23558  /*
23559  * - LGPL
23560  *
23561  * Input
23562  * 
23563  */
23564
23565 /**
23566  * @class Roo.bootstrap.SecurePass
23567  * @extends Roo.bootstrap.Input
23568  * Bootstrap SecurePass class
23569  *
23570  * 
23571  * @constructor
23572  * Create a new SecurePass
23573  * @param {Object} config The config object
23574  */
23575  
23576 Roo.bootstrap.SecurePass = function (config) {
23577     // these go here, so the translation tool can replace them..
23578     this.errors = {
23579         PwdEmpty: "Please type a password, and then retype it to confirm.",
23580         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23581         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23582         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23583         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23584         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23585         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23586         TooWeak: "Your password is Too Weak."
23587     },
23588     this.meterLabel = "Password strength:";
23589     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23590     this.meterClass = [
23591         "roo-password-meter-tooweak", 
23592         "roo-password-meter-weak", 
23593         "roo-password-meter-medium", 
23594         "roo-password-meter-strong", 
23595         "roo-password-meter-grey"
23596     ];
23597     
23598     this.errors = {};
23599     
23600     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23601 }
23602
23603 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23604     /**
23605      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23606      * {
23607      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23608      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23609      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23610      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23611      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23612      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23613      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23614      * })
23615      */
23616     // private
23617     
23618     meterWidth: 300,
23619     errorMsg :'',    
23620     errors: false,
23621     imageRoot: '/',
23622     /**
23623      * @cfg {String/Object} Label for the strength meter (defaults to
23624      * 'Password strength:')
23625      */
23626     // private
23627     meterLabel: '',
23628     /**
23629      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23630      * ['Weak', 'Medium', 'Strong'])
23631      */
23632     // private    
23633     pwdStrengths: false,    
23634     // private
23635     strength: 0,
23636     // private
23637     _lastPwd: null,
23638     // private
23639     kCapitalLetter: 0,
23640     kSmallLetter: 1,
23641     kDigit: 2,
23642     kPunctuation: 3,
23643     
23644     insecure: false,
23645     // private
23646     initEvents: function ()
23647     {
23648         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23649
23650         if (this.el.is('input[type=password]') && Roo.isSafari) {
23651             this.el.on('keydown', this.SafariOnKeyDown, this);
23652         }
23653
23654         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23655     },
23656     // private
23657     onRender: function (ct, position)
23658     {
23659         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23660         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23661         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23662
23663         this.trigger.createChild({
23664                    cn: [
23665                     {
23666                     //id: 'PwdMeter',
23667                     tag: 'div',
23668                     cls: 'roo-password-meter-grey col-xs-12',
23669                     style: {
23670                         //width: 0,
23671                         //width: this.meterWidth + 'px'                                                
23672                         }
23673                     },
23674                     {                            
23675                          cls: 'roo-password-meter-text'                          
23676                     }
23677                 ]            
23678         });
23679
23680          
23681         if (this.hideTrigger) {
23682             this.trigger.setDisplayed(false);
23683         }
23684         this.setSize(this.width || '', this.height || '');
23685     },
23686     // private
23687     onDestroy: function ()
23688     {
23689         if (this.trigger) {
23690             this.trigger.removeAllListeners();
23691             this.trigger.remove();
23692         }
23693         if (this.wrap) {
23694             this.wrap.remove();
23695         }
23696         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23697     },
23698     // private
23699     checkStrength: function ()
23700     {
23701         var pwd = this.inputEl().getValue();
23702         if (pwd == this._lastPwd) {
23703             return;
23704         }
23705
23706         var strength;
23707         if (this.ClientSideStrongPassword(pwd)) {
23708             strength = 3;
23709         } else if (this.ClientSideMediumPassword(pwd)) {
23710             strength = 2;
23711         } else if (this.ClientSideWeakPassword(pwd)) {
23712             strength = 1;
23713         } else {
23714             strength = 0;
23715         }
23716         
23717         Roo.log('strength1: ' + strength);
23718         
23719         //var pm = this.trigger.child('div/div/div').dom;
23720         var pm = this.trigger.child('div/div');
23721         pm.removeClass(this.meterClass);
23722         pm.addClass(this.meterClass[strength]);
23723                 
23724         
23725         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23726                 
23727         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23728         
23729         this._lastPwd = pwd;
23730     },
23731     reset: function ()
23732     {
23733         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23734         
23735         this._lastPwd = '';
23736         
23737         var pm = this.trigger.child('div/div');
23738         pm.removeClass(this.meterClass);
23739         pm.addClass('roo-password-meter-grey');        
23740         
23741         
23742         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23743         
23744         pt.innerHTML = '';
23745         this.inputEl().dom.type='password';
23746     },
23747     // private
23748     validateValue: function (value)
23749     {
23750         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23751             return false;
23752         }
23753         if (value.length == 0) {
23754             if (this.allowBlank) {
23755                 this.clearInvalid();
23756                 return true;
23757             }
23758
23759             this.markInvalid(this.errors.PwdEmpty);
23760             this.errorMsg = this.errors.PwdEmpty;
23761             return false;
23762         }
23763         
23764         if(this.insecure){
23765             return true;
23766         }
23767         
23768         if (!value.match(/[\x21-\x7e]+/)) {
23769             this.markInvalid(this.errors.PwdBadChar);
23770             this.errorMsg = this.errors.PwdBadChar;
23771             return false;
23772         }
23773         if (value.length < 6) {
23774             this.markInvalid(this.errors.PwdShort);
23775             this.errorMsg = this.errors.PwdShort;
23776             return false;
23777         }
23778         if (value.length > 16) {
23779             this.markInvalid(this.errors.PwdLong);
23780             this.errorMsg = this.errors.PwdLong;
23781             return false;
23782         }
23783         var strength;
23784         if (this.ClientSideStrongPassword(value)) {
23785             strength = 3;
23786         } else if (this.ClientSideMediumPassword(value)) {
23787             strength = 2;
23788         } else if (this.ClientSideWeakPassword(value)) {
23789             strength = 1;
23790         } else {
23791             strength = 0;
23792         }
23793
23794         
23795         if (strength < 2) {
23796             //this.markInvalid(this.errors.TooWeak);
23797             this.errorMsg = this.errors.TooWeak;
23798             //return false;
23799         }
23800         
23801         
23802         console.log('strength2: ' + strength);
23803         
23804         //var pm = this.trigger.child('div/div/div').dom;
23805         
23806         var pm = this.trigger.child('div/div');
23807         pm.removeClass(this.meterClass);
23808         pm.addClass(this.meterClass[strength]);
23809                 
23810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23811                 
23812         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23813         
23814         this.errorMsg = ''; 
23815         return true;
23816     },
23817     // private
23818     CharacterSetChecks: function (type)
23819     {
23820         this.type = type;
23821         this.fResult = false;
23822     },
23823     // private
23824     isctype: function (character, type)
23825     {
23826         switch (type) {  
23827             case this.kCapitalLetter:
23828                 if (character >= 'A' && character <= 'Z') {
23829                     return true;
23830                 }
23831                 break;
23832             
23833             case this.kSmallLetter:
23834                 if (character >= 'a' && character <= 'z') {
23835                     return true;
23836                 }
23837                 break;
23838             
23839             case this.kDigit:
23840                 if (character >= '0' && character <= '9') {
23841                     return true;
23842                 }
23843                 break;
23844             
23845             case this.kPunctuation:
23846                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23847                     return true;
23848                 }
23849                 break;
23850             
23851             default:
23852                 return false;
23853         }
23854
23855     },
23856     // private
23857     IsLongEnough: function (pwd, size)
23858     {
23859         return !(pwd == null || isNaN(size) || pwd.length < size);
23860     },
23861     // private
23862     SpansEnoughCharacterSets: function (word, nb)
23863     {
23864         if (!this.IsLongEnough(word, nb))
23865         {
23866             return false;
23867         }
23868
23869         var characterSetChecks = new Array(
23870             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23871             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23872         );
23873         
23874         for (var index = 0; index < word.length; ++index) {
23875             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23876                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23877                     characterSetChecks[nCharSet].fResult = true;
23878                     break;
23879                 }
23880             }
23881         }
23882
23883         var nCharSets = 0;
23884         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23885             if (characterSetChecks[nCharSet].fResult) {
23886                 ++nCharSets;
23887             }
23888         }
23889
23890         if (nCharSets < nb) {
23891             return false;
23892         }
23893         return true;
23894     },
23895     // private
23896     ClientSideStrongPassword: function (pwd)
23897     {
23898         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23899     },
23900     // private
23901     ClientSideMediumPassword: function (pwd)
23902     {
23903         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23904     },
23905     // private
23906     ClientSideWeakPassword: function (pwd)
23907     {
23908         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23909     }
23910           
23911 })//<script type="text/javascript">
23912
23913 /*
23914  * Based  Ext JS Library 1.1.1
23915  * Copyright(c) 2006-2007, Ext JS, LLC.
23916  * LGPL
23917  *
23918  */
23919  
23920 /**
23921  * @class Roo.HtmlEditorCore
23922  * @extends Roo.Component
23923  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23924  *
23925  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23926  */
23927
23928 Roo.HtmlEditorCore = function(config){
23929     
23930     
23931     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23932     
23933     
23934     this.addEvents({
23935         /**
23936          * @event initialize
23937          * Fires when the editor is fully initialized (including the iframe)
23938          * @param {Roo.HtmlEditorCore} this
23939          */
23940         initialize: true,
23941         /**
23942          * @event activate
23943          * Fires when the editor is first receives the focus. Any insertion must wait
23944          * until after this event.
23945          * @param {Roo.HtmlEditorCore} this
23946          */
23947         activate: true,
23948          /**
23949          * @event beforesync
23950          * Fires before the textarea is updated with content from the editor iframe. Return false
23951          * to cancel the sync.
23952          * @param {Roo.HtmlEditorCore} this
23953          * @param {String} html
23954          */
23955         beforesync: true,
23956          /**
23957          * @event beforepush
23958          * Fires before the iframe editor is updated with content from the textarea. Return false
23959          * to cancel the push.
23960          * @param {Roo.HtmlEditorCore} this
23961          * @param {String} html
23962          */
23963         beforepush: true,
23964          /**
23965          * @event sync
23966          * Fires when the textarea is updated with content from the editor iframe.
23967          * @param {Roo.HtmlEditorCore} this
23968          * @param {String} html
23969          */
23970         sync: true,
23971          /**
23972          * @event push
23973          * Fires when the iframe editor is updated with content from the textarea.
23974          * @param {Roo.HtmlEditorCore} this
23975          * @param {String} html
23976          */
23977         push: true,
23978         
23979         /**
23980          * @event editorevent
23981          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23982          * @param {Roo.HtmlEditorCore} this
23983          */
23984         editorevent: true
23985         
23986     });
23987     
23988     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23989     
23990     // defaults : white / black...
23991     this.applyBlacklists();
23992     
23993     
23994     
23995 };
23996
23997
23998 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23999
24000
24001      /**
24002      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24003      */
24004     
24005     owner : false,
24006     
24007      /**
24008      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24009      *                        Roo.resizable.
24010      */
24011     resizable : false,
24012      /**
24013      * @cfg {Number} height (in pixels)
24014      */   
24015     height: 300,
24016    /**
24017      * @cfg {Number} width (in pixels)
24018      */   
24019     width: 500,
24020     
24021     /**
24022      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24023      * 
24024      */
24025     stylesheets: false,
24026     
24027     // id of frame..
24028     frameId: false,
24029     
24030     // private properties
24031     validationEvent : false,
24032     deferHeight: true,
24033     initialized : false,
24034     activated : false,
24035     sourceEditMode : false,
24036     onFocus : Roo.emptyFn,
24037     iframePad:3,
24038     hideMode:'offsets',
24039     
24040     clearUp: true,
24041     
24042     // blacklist + whitelisted elements..
24043     black: false,
24044     white: false,
24045      
24046     bodyCls : '',
24047
24048     /**
24049      * Protected method that will not generally be called directly. It
24050      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24051      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24052      */
24053     getDocMarkup : function(){
24054         // body styles..
24055         var st = '';
24056         
24057         // inherit styels from page...?? 
24058         if (this.stylesheets === false) {
24059             
24060             Roo.get(document.head).select('style').each(function(node) {
24061                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24062             });
24063             
24064             Roo.get(document.head).select('link').each(function(node) { 
24065                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24066             });
24067             
24068         } else if (!this.stylesheets.length) {
24069                 // simple..
24070                 st = '<style type="text/css">' +
24071                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24072                    '</style>';
24073         } else {
24074             for (var i in this.stylesheets) { 
24075                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24076             }
24077             
24078         }
24079         
24080         st +=  '<style type="text/css">' +
24081             'IMG { cursor: pointer } ' +
24082         '</style>';
24083
24084         var cls = 'roo-htmleditor-body';
24085         
24086         if(this.bodyCls.length){
24087             cls += ' ' + this.bodyCls;
24088         }
24089         
24090         return '<html><head>' + st  +
24091             //<style type="text/css">' +
24092             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24093             //'</style>' +
24094             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24095     },
24096
24097     // private
24098     onRender : function(ct, position)
24099     {
24100         var _t = this;
24101         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24102         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24103         
24104         
24105         this.el.dom.style.border = '0 none';
24106         this.el.dom.setAttribute('tabIndex', -1);
24107         this.el.addClass('x-hidden hide');
24108         
24109         
24110         
24111         if(Roo.isIE){ // fix IE 1px bogus margin
24112             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24113         }
24114        
24115         
24116         this.frameId = Roo.id();
24117         
24118          
24119         
24120         var iframe = this.owner.wrap.createChild({
24121             tag: 'iframe',
24122             cls: 'form-control', // bootstrap..
24123             id: this.frameId,
24124             name: this.frameId,
24125             frameBorder : 'no',
24126             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24127         }, this.el
24128         );
24129         
24130         
24131         this.iframe = iframe.dom;
24132
24133          this.assignDocWin();
24134         
24135         this.doc.designMode = 'on';
24136        
24137         this.doc.open();
24138         this.doc.write(this.getDocMarkup());
24139         this.doc.close();
24140
24141         
24142         var task = { // must defer to wait for browser to be ready
24143             run : function(){
24144                 //console.log("run task?" + this.doc.readyState);
24145                 this.assignDocWin();
24146                 if(this.doc.body || this.doc.readyState == 'complete'){
24147                     try {
24148                         this.doc.designMode="on";
24149                     } catch (e) {
24150                         return;
24151                     }
24152                     Roo.TaskMgr.stop(task);
24153                     this.initEditor.defer(10, this);
24154                 }
24155             },
24156             interval : 10,
24157             duration: 10000,
24158             scope: this
24159         };
24160         Roo.TaskMgr.start(task);
24161
24162     },
24163
24164     // private
24165     onResize : function(w, h)
24166     {
24167          Roo.log('resize: ' +w + ',' + h );
24168         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24169         if(!this.iframe){
24170             return;
24171         }
24172         if(typeof w == 'number'){
24173             
24174             this.iframe.style.width = w + 'px';
24175         }
24176         if(typeof h == 'number'){
24177             
24178             this.iframe.style.height = h + 'px';
24179             if(this.doc){
24180                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24181             }
24182         }
24183         
24184     },
24185
24186     /**
24187      * Toggles the editor between standard and source edit mode.
24188      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24189      */
24190     toggleSourceEdit : function(sourceEditMode){
24191         
24192         this.sourceEditMode = sourceEditMode === true;
24193         
24194         if(this.sourceEditMode){
24195  
24196             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24197             
24198         }else{
24199             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24200             //this.iframe.className = '';
24201             this.deferFocus();
24202         }
24203         //this.setSize(this.owner.wrap.getSize());
24204         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24205     },
24206
24207     
24208   
24209
24210     /**
24211      * Protected method that will not generally be called directly. If you need/want
24212      * custom HTML cleanup, this is the method you should override.
24213      * @param {String} html The HTML to be cleaned
24214      * return {String} The cleaned HTML
24215      */
24216     cleanHtml : function(html){
24217         html = String(html);
24218         if(html.length > 5){
24219             if(Roo.isSafari){ // strip safari nonsense
24220                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24221             }
24222         }
24223         if(html == '&nbsp;'){
24224             html = '';
24225         }
24226         return html;
24227     },
24228
24229     /**
24230      * HTML Editor -> Textarea
24231      * Protected method that will not generally be called directly. Syncs the contents
24232      * of the editor iframe with the textarea.
24233      */
24234     syncValue : function(){
24235         if(this.initialized){
24236             var bd = (this.doc.body || this.doc.documentElement);
24237             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24238             var html = bd.innerHTML;
24239             if(Roo.isSafari){
24240                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24241                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24242                 if(m && m[1]){
24243                     html = '<div style="'+m[0]+'">' + html + '</div>';
24244                 }
24245             }
24246             html = this.cleanHtml(html);
24247             // fix up the special chars.. normaly like back quotes in word...
24248             // however we do not want to do this with chinese..
24249             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24250                 
24251                 var cc = match.charCodeAt();
24252
24253                 // Get the character value, handling surrogate pairs
24254                 if (match.length == 2) {
24255                     // It's a surrogate pair, calculate the Unicode code point
24256                     var high = match.charCodeAt(0) - 0xD800;
24257                     var low  = match.charCodeAt(1) - 0xDC00;
24258                     cc = (high * 0x400) + low + 0x10000;
24259                 }  else if (
24260                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24261                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24262                     (cc >= 0xf900 && cc < 0xfb00 )
24263                 ) {
24264                         return match;
24265                 }  
24266          
24267                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24268                 return "&#" + cc + ";";
24269                 
24270                 
24271             });
24272             
24273             
24274              
24275             if(this.owner.fireEvent('beforesync', this, html) !== false){
24276                 this.el.dom.value = html;
24277                 this.owner.fireEvent('sync', this, html);
24278             }
24279         }
24280     },
24281
24282     /**
24283      * Protected method that will not generally be called directly. Pushes the value of the textarea
24284      * into the iframe editor.
24285      */
24286     pushValue : function(){
24287         if(this.initialized){
24288             var v = this.el.dom.value.trim();
24289             
24290 //            if(v.length < 1){
24291 //                v = '&#160;';
24292 //            }
24293             
24294             if(this.owner.fireEvent('beforepush', this, v) !== false){
24295                 var d = (this.doc.body || this.doc.documentElement);
24296                 d.innerHTML = v;
24297                 this.cleanUpPaste();
24298                 this.el.dom.value = d.innerHTML;
24299                 this.owner.fireEvent('push', this, v);
24300             }
24301         }
24302     },
24303
24304     // private
24305     deferFocus : function(){
24306         this.focus.defer(10, this);
24307     },
24308
24309     // doc'ed in Field
24310     focus : function(){
24311         if(this.win && !this.sourceEditMode){
24312             this.win.focus();
24313         }else{
24314             this.el.focus();
24315         }
24316     },
24317     
24318     assignDocWin: function()
24319     {
24320         var iframe = this.iframe;
24321         
24322          if(Roo.isIE){
24323             this.doc = iframe.contentWindow.document;
24324             this.win = iframe.contentWindow;
24325         } else {
24326 //            if (!Roo.get(this.frameId)) {
24327 //                return;
24328 //            }
24329 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24330 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24331             
24332             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24333                 return;
24334             }
24335             
24336             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24337             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24338         }
24339     },
24340     
24341     // private
24342     initEditor : function(){
24343         //console.log("INIT EDITOR");
24344         this.assignDocWin();
24345         
24346         
24347         
24348         this.doc.designMode="on";
24349         this.doc.open();
24350         this.doc.write(this.getDocMarkup());
24351         this.doc.close();
24352         
24353         var dbody = (this.doc.body || this.doc.documentElement);
24354         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24355         // this copies styles from the containing element into thsi one..
24356         // not sure why we need all of this..
24357         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24358         
24359         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24360         //ss['background-attachment'] = 'fixed'; // w3c
24361         dbody.bgProperties = 'fixed'; // ie
24362         //Roo.DomHelper.applyStyles(dbody, ss);
24363         Roo.EventManager.on(this.doc, {
24364             //'mousedown': this.onEditorEvent,
24365             'mouseup': this.onEditorEvent,
24366             'dblclick': this.onEditorEvent,
24367             'click': this.onEditorEvent,
24368             'keyup': this.onEditorEvent,
24369             buffer:100,
24370             scope: this
24371         });
24372         if(Roo.isGecko){
24373             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24374         }
24375         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24376             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24377         }
24378         this.initialized = true;
24379
24380         this.owner.fireEvent('initialize', this);
24381         this.pushValue();
24382     },
24383
24384     // private
24385     onDestroy : function(){
24386         
24387         
24388         
24389         if(this.rendered){
24390             
24391             //for (var i =0; i < this.toolbars.length;i++) {
24392             //    // fixme - ask toolbars for heights?
24393             //    this.toolbars[i].onDestroy();
24394            // }
24395             
24396             //this.wrap.dom.innerHTML = '';
24397             //this.wrap.remove();
24398         }
24399     },
24400
24401     // private
24402     onFirstFocus : function(){
24403         
24404         this.assignDocWin();
24405         
24406         
24407         this.activated = true;
24408          
24409     
24410         if(Roo.isGecko){ // prevent silly gecko errors
24411             this.win.focus();
24412             var s = this.win.getSelection();
24413             if(!s.focusNode || s.focusNode.nodeType != 3){
24414                 var r = s.getRangeAt(0);
24415                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24416                 r.collapse(true);
24417                 this.deferFocus();
24418             }
24419             try{
24420                 this.execCmd('useCSS', true);
24421                 this.execCmd('styleWithCSS', false);
24422             }catch(e){}
24423         }
24424         this.owner.fireEvent('activate', this);
24425     },
24426
24427     // private
24428     adjustFont: function(btn){
24429         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24430         //if(Roo.isSafari){ // safari
24431         //    adjust *= 2;
24432        // }
24433         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24434         if(Roo.isSafari){ // safari
24435             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24436             v =  (v < 10) ? 10 : v;
24437             v =  (v > 48) ? 48 : v;
24438             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24439             
24440         }
24441         
24442         
24443         v = Math.max(1, v+adjust);
24444         
24445         this.execCmd('FontSize', v  );
24446     },
24447
24448     onEditorEvent : function(e)
24449     {
24450         this.owner.fireEvent('editorevent', this, e);
24451       //  this.updateToolbar();
24452         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24453     },
24454
24455     insertTag : function(tg)
24456     {
24457         // could be a bit smarter... -> wrap the current selected tRoo..
24458         if (tg.toLowerCase() == 'span' ||
24459             tg.toLowerCase() == 'code' ||
24460             tg.toLowerCase() == 'sup' ||
24461             tg.toLowerCase() == 'sub' 
24462             ) {
24463             
24464             range = this.createRange(this.getSelection());
24465             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24466             wrappingNode.appendChild(range.extractContents());
24467             range.insertNode(wrappingNode);
24468
24469             return;
24470             
24471             
24472             
24473         }
24474         this.execCmd("formatblock",   tg);
24475         
24476     },
24477     
24478     insertText : function(txt)
24479     {
24480         
24481         
24482         var range = this.createRange();
24483         range.deleteContents();
24484                //alert(Sender.getAttribute('label'));
24485                
24486         range.insertNode(this.doc.createTextNode(txt));
24487     } ,
24488     
24489      
24490
24491     /**
24492      * Executes a Midas editor command on the editor document and performs necessary focus and
24493      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24494      * @param {String} cmd The Midas command
24495      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24496      */
24497     relayCmd : function(cmd, value){
24498         this.win.focus();
24499         this.execCmd(cmd, value);
24500         this.owner.fireEvent('editorevent', this);
24501         //this.updateToolbar();
24502         this.owner.deferFocus();
24503     },
24504
24505     /**
24506      * Executes a Midas editor command directly on the editor document.
24507      * For visual commands, you should use {@link #relayCmd} instead.
24508      * <b>This should only be called after the editor is initialized.</b>
24509      * @param {String} cmd The Midas command
24510      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24511      */
24512     execCmd : function(cmd, value){
24513         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24514         this.syncValue();
24515     },
24516  
24517  
24518    
24519     /**
24520      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24521      * to insert tRoo.
24522      * @param {String} text | dom node.. 
24523      */
24524     insertAtCursor : function(text)
24525     {
24526         
24527         if(!this.activated){
24528             return;
24529         }
24530         /*
24531         if(Roo.isIE){
24532             this.win.focus();
24533             var r = this.doc.selection.createRange();
24534             if(r){
24535                 r.collapse(true);
24536                 r.pasteHTML(text);
24537                 this.syncValue();
24538                 this.deferFocus();
24539             
24540             }
24541             return;
24542         }
24543         */
24544         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24545             this.win.focus();
24546             
24547             
24548             // from jquery ui (MIT licenced)
24549             var range, node;
24550             var win = this.win;
24551             
24552             if (win.getSelection && win.getSelection().getRangeAt) {
24553                 range = win.getSelection().getRangeAt(0);
24554                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24555                 range.insertNode(node);
24556             } else if (win.document.selection && win.document.selection.createRange) {
24557                 // no firefox support
24558                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24559                 win.document.selection.createRange().pasteHTML(txt);
24560             } else {
24561                 // no firefox support
24562                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24563                 this.execCmd('InsertHTML', txt);
24564             } 
24565             
24566             this.syncValue();
24567             
24568             this.deferFocus();
24569         }
24570     },
24571  // private
24572     mozKeyPress : function(e){
24573         if(e.ctrlKey){
24574             var c = e.getCharCode(), cmd;
24575           
24576             if(c > 0){
24577                 c = String.fromCharCode(c).toLowerCase();
24578                 switch(c){
24579                     case 'b':
24580                         cmd = 'bold';
24581                         break;
24582                     case 'i':
24583                         cmd = 'italic';
24584                         break;
24585                     
24586                     case 'u':
24587                         cmd = 'underline';
24588                         break;
24589                     
24590                     case 'v':
24591                         this.cleanUpPaste.defer(100, this);
24592                         return;
24593                         
24594                 }
24595                 if(cmd){
24596                     this.win.focus();
24597                     this.execCmd(cmd);
24598                     this.deferFocus();
24599                     e.preventDefault();
24600                 }
24601                 
24602             }
24603         }
24604     },
24605
24606     // private
24607     fixKeys : function(){ // load time branching for fastest keydown performance
24608         if(Roo.isIE){
24609             return function(e){
24610                 var k = e.getKey(), r;
24611                 if(k == e.TAB){
24612                     e.stopEvent();
24613                     r = this.doc.selection.createRange();
24614                     if(r){
24615                         r.collapse(true);
24616                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24617                         this.deferFocus();
24618                     }
24619                     return;
24620                 }
24621                 
24622                 if(k == e.ENTER){
24623                     r = this.doc.selection.createRange();
24624                     if(r){
24625                         var target = r.parentElement();
24626                         if(!target || target.tagName.toLowerCase() != 'li'){
24627                             e.stopEvent();
24628                             r.pasteHTML('<br />');
24629                             r.collapse(false);
24630                             r.select();
24631                         }
24632                     }
24633                 }
24634                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24635                     this.cleanUpPaste.defer(100, this);
24636                     return;
24637                 }
24638                 
24639                 
24640             };
24641         }else if(Roo.isOpera){
24642             return function(e){
24643                 var k = e.getKey();
24644                 if(k == e.TAB){
24645                     e.stopEvent();
24646                     this.win.focus();
24647                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24648                     this.deferFocus();
24649                 }
24650                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24651                     this.cleanUpPaste.defer(100, this);
24652                     return;
24653                 }
24654                 
24655             };
24656         }else if(Roo.isSafari){
24657             return function(e){
24658                 var k = e.getKey();
24659                 
24660                 if(k == e.TAB){
24661                     e.stopEvent();
24662                     this.execCmd('InsertText','\t');
24663                     this.deferFocus();
24664                     return;
24665                 }
24666                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24667                     this.cleanUpPaste.defer(100, this);
24668                     return;
24669                 }
24670                 
24671              };
24672         }
24673     }(),
24674     
24675     getAllAncestors: function()
24676     {
24677         var p = this.getSelectedNode();
24678         var a = [];
24679         if (!p) {
24680             a.push(p); // push blank onto stack..
24681             p = this.getParentElement();
24682         }
24683         
24684         
24685         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24686             a.push(p);
24687             p = p.parentNode;
24688         }
24689         a.push(this.doc.body);
24690         return a;
24691     },
24692     lastSel : false,
24693     lastSelNode : false,
24694     
24695     
24696     getSelection : function() 
24697     {
24698         this.assignDocWin();
24699         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24700     },
24701     
24702     getSelectedNode: function() 
24703     {
24704         // this may only work on Gecko!!!
24705         
24706         // should we cache this!!!!
24707         
24708         
24709         
24710          
24711         var range = this.createRange(this.getSelection()).cloneRange();
24712         
24713         if (Roo.isIE) {
24714             var parent = range.parentElement();
24715             while (true) {
24716                 var testRange = range.duplicate();
24717                 testRange.moveToElementText(parent);
24718                 if (testRange.inRange(range)) {
24719                     break;
24720                 }
24721                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24722                     break;
24723                 }
24724                 parent = parent.parentElement;
24725             }
24726             return parent;
24727         }
24728         
24729         // is ancestor a text element.
24730         var ac =  range.commonAncestorContainer;
24731         if (ac.nodeType == 3) {
24732             ac = ac.parentNode;
24733         }
24734         
24735         var ar = ac.childNodes;
24736          
24737         var nodes = [];
24738         var other_nodes = [];
24739         var has_other_nodes = false;
24740         for (var i=0;i<ar.length;i++) {
24741             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24742                 continue;
24743             }
24744             // fullly contained node.
24745             
24746             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24747                 nodes.push(ar[i]);
24748                 continue;
24749             }
24750             
24751             // probably selected..
24752             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24753                 other_nodes.push(ar[i]);
24754                 continue;
24755             }
24756             // outer..
24757             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24758                 continue;
24759             }
24760             
24761             
24762             has_other_nodes = true;
24763         }
24764         if (!nodes.length && other_nodes.length) {
24765             nodes= other_nodes;
24766         }
24767         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24768             return false;
24769         }
24770         
24771         return nodes[0];
24772     },
24773     createRange: function(sel)
24774     {
24775         // this has strange effects when using with 
24776         // top toolbar - not sure if it's a great idea.
24777         //this.editor.contentWindow.focus();
24778         if (typeof sel != "undefined") {
24779             try {
24780                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24781             } catch(e) {
24782                 return this.doc.createRange();
24783             }
24784         } else {
24785             return this.doc.createRange();
24786         }
24787     },
24788     getParentElement: function()
24789     {
24790         
24791         this.assignDocWin();
24792         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24793         
24794         var range = this.createRange(sel);
24795          
24796         try {
24797             var p = range.commonAncestorContainer;
24798             while (p.nodeType == 3) { // text node
24799                 p = p.parentNode;
24800             }
24801             return p;
24802         } catch (e) {
24803             return null;
24804         }
24805     
24806     },
24807     /***
24808      *
24809      * Range intersection.. the hard stuff...
24810      *  '-1' = before
24811      *  '0' = hits..
24812      *  '1' = after.
24813      *         [ -- selected range --- ]
24814      *   [fail]                        [fail]
24815      *
24816      *    basically..
24817      *      if end is before start or  hits it. fail.
24818      *      if start is after end or hits it fail.
24819      *
24820      *   if either hits (but other is outside. - then it's not 
24821      *   
24822      *    
24823      **/
24824     
24825     
24826     // @see http://www.thismuchiknow.co.uk/?p=64.
24827     rangeIntersectsNode : function(range, node)
24828     {
24829         var nodeRange = node.ownerDocument.createRange();
24830         try {
24831             nodeRange.selectNode(node);
24832         } catch (e) {
24833             nodeRange.selectNodeContents(node);
24834         }
24835     
24836         var rangeStartRange = range.cloneRange();
24837         rangeStartRange.collapse(true);
24838     
24839         var rangeEndRange = range.cloneRange();
24840         rangeEndRange.collapse(false);
24841     
24842         var nodeStartRange = nodeRange.cloneRange();
24843         nodeStartRange.collapse(true);
24844     
24845         var nodeEndRange = nodeRange.cloneRange();
24846         nodeEndRange.collapse(false);
24847     
24848         return rangeStartRange.compareBoundaryPoints(
24849                  Range.START_TO_START, nodeEndRange) == -1 &&
24850                rangeEndRange.compareBoundaryPoints(
24851                  Range.START_TO_START, nodeStartRange) == 1;
24852         
24853          
24854     },
24855     rangeCompareNode : function(range, node)
24856     {
24857         var nodeRange = node.ownerDocument.createRange();
24858         try {
24859             nodeRange.selectNode(node);
24860         } catch (e) {
24861             nodeRange.selectNodeContents(node);
24862         }
24863         
24864         
24865         range.collapse(true);
24866     
24867         nodeRange.collapse(true);
24868      
24869         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24870         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24871          
24872         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24873         
24874         var nodeIsBefore   =  ss == 1;
24875         var nodeIsAfter    = ee == -1;
24876         
24877         if (nodeIsBefore && nodeIsAfter) {
24878             return 0; // outer
24879         }
24880         if (!nodeIsBefore && nodeIsAfter) {
24881             return 1; //right trailed.
24882         }
24883         
24884         if (nodeIsBefore && !nodeIsAfter) {
24885             return 2;  // left trailed.
24886         }
24887         // fully contined.
24888         return 3;
24889     },
24890
24891     // private? - in a new class?
24892     cleanUpPaste :  function()
24893     {
24894         // cleans up the whole document..
24895         Roo.log('cleanuppaste');
24896         
24897         this.cleanUpChildren(this.doc.body);
24898         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24899         if (clean != this.doc.body.innerHTML) {
24900             this.doc.body.innerHTML = clean;
24901         }
24902         
24903     },
24904     
24905     cleanWordChars : function(input) {// change the chars to hex code
24906         var he = Roo.HtmlEditorCore;
24907         
24908         var output = input;
24909         Roo.each(he.swapCodes, function(sw) { 
24910             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24911             
24912             output = output.replace(swapper, sw[1]);
24913         });
24914         
24915         return output;
24916     },
24917     
24918     
24919     cleanUpChildren : function (n)
24920     {
24921         if (!n.childNodes.length) {
24922             return;
24923         }
24924         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24925            this.cleanUpChild(n.childNodes[i]);
24926         }
24927     },
24928     
24929     
24930         
24931     
24932     cleanUpChild : function (node)
24933     {
24934         var ed = this;
24935         //console.log(node);
24936         if (node.nodeName == "#text") {
24937             // clean up silly Windows -- stuff?
24938             return; 
24939         }
24940         if (node.nodeName == "#comment") {
24941             node.parentNode.removeChild(node);
24942             // clean up silly Windows -- stuff?
24943             return; 
24944         }
24945         var lcname = node.tagName.toLowerCase();
24946         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24947         // whitelist of tags..
24948         
24949         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24950             // remove node.
24951             node.parentNode.removeChild(node);
24952             return;
24953             
24954         }
24955         
24956         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24957         
24958         // spans with no attributes - just remove them..
24959         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24960             remove_keep_children = true;
24961         }
24962         
24963         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24964         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24965         
24966         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24967         //    remove_keep_children = true;
24968         //}
24969         
24970         if (remove_keep_children) {
24971             this.cleanUpChildren(node);
24972             // inserts everything just before this node...
24973             while (node.childNodes.length) {
24974                 var cn = node.childNodes[0];
24975                 node.removeChild(cn);
24976                 node.parentNode.insertBefore(cn, node);
24977             }
24978             node.parentNode.removeChild(node);
24979             return;
24980         }
24981         
24982         if (!node.attributes || !node.attributes.length) {
24983             
24984           
24985             
24986             
24987             this.cleanUpChildren(node);
24988             return;
24989         }
24990         
24991         function cleanAttr(n,v)
24992         {
24993             
24994             if (v.match(/^\./) || v.match(/^\//)) {
24995                 return;
24996             }
24997             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24998                 return;
24999             }
25000             if (v.match(/^#/)) {
25001                 return;
25002             }
25003             if (v.match(/^\{/)) { // allow template editing.
25004                 return;
25005             }
25006 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25007             node.removeAttribute(n);
25008             
25009         }
25010         
25011         var cwhite = this.cwhite;
25012         var cblack = this.cblack;
25013             
25014         function cleanStyle(n,v)
25015         {
25016             if (v.match(/expression/)) { //XSS?? should we even bother..
25017                 node.removeAttribute(n);
25018                 return;
25019             }
25020             
25021             var parts = v.split(/;/);
25022             var clean = [];
25023             
25024             Roo.each(parts, function(p) {
25025                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25026                 if (!p.length) {
25027                     return true;
25028                 }
25029                 var l = p.split(':').shift().replace(/\s+/g,'');
25030                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25031                 
25032                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25033 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25034                     //node.removeAttribute(n);
25035                     return true;
25036                 }
25037                 //Roo.log()
25038                 // only allow 'c whitelisted system attributes'
25039                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25040 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25041                     //node.removeAttribute(n);
25042                     return true;
25043                 }
25044                 
25045                 
25046                  
25047                 
25048                 clean.push(p);
25049                 return true;
25050             });
25051             if (clean.length) { 
25052                 node.setAttribute(n, clean.join(';'));
25053             } else {
25054                 node.removeAttribute(n);
25055             }
25056             
25057         }
25058         
25059         
25060         for (var i = node.attributes.length-1; i > -1 ; i--) {
25061             var a = node.attributes[i];
25062             //console.log(a);
25063             
25064             if (a.name.toLowerCase().substr(0,2)=='on')  {
25065                 node.removeAttribute(a.name);
25066                 continue;
25067             }
25068             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25069                 node.removeAttribute(a.name);
25070                 continue;
25071             }
25072             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25073                 cleanAttr(a.name,a.value); // fixme..
25074                 continue;
25075             }
25076             if (a.name == 'style') {
25077                 cleanStyle(a.name,a.value);
25078                 continue;
25079             }
25080             /// clean up MS crap..
25081             // tecnically this should be a list of valid class'es..
25082             
25083             
25084             if (a.name == 'class') {
25085                 if (a.value.match(/^Mso/)) {
25086                     node.removeAttribute('class');
25087                 }
25088                 
25089                 if (a.value.match(/^body$/)) {
25090                     node.removeAttribute('class');
25091                 }
25092                 continue;
25093             }
25094             
25095             // style cleanup!?
25096             // class cleanup?
25097             
25098         }
25099         
25100         
25101         this.cleanUpChildren(node);
25102         
25103         
25104     },
25105     
25106     /**
25107      * Clean up MS wordisms...
25108      */
25109     cleanWord : function(node)
25110     {
25111         if (!node) {
25112             this.cleanWord(this.doc.body);
25113             return;
25114         }
25115         
25116         if(
25117                 node.nodeName == 'SPAN' &&
25118                 !node.hasAttributes() &&
25119                 node.childNodes.length == 1 &&
25120                 node.firstChild.nodeName == "#text"  
25121         ) {
25122             var textNode = node.firstChild;
25123             node.removeChild(textNode);
25124             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25125                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25126             }
25127             node.parentNode.insertBefore(textNode, node);
25128             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25129                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25130             }
25131             node.parentNode.removeChild(node);
25132         }
25133         
25134         if (node.nodeName == "#text") {
25135             // clean up silly Windows -- stuff?
25136             return; 
25137         }
25138         if (node.nodeName == "#comment") {
25139             node.parentNode.removeChild(node);
25140             // clean up silly Windows -- stuff?
25141             return; 
25142         }
25143         
25144         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25145             node.parentNode.removeChild(node);
25146             return;
25147         }
25148         //Roo.log(node.tagName);
25149         // remove - but keep children..
25150         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25151             //Roo.log('-- removed');
25152             while (node.childNodes.length) {
25153                 var cn = node.childNodes[0];
25154                 node.removeChild(cn);
25155                 node.parentNode.insertBefore(cn, node);
25156                 // move node to parent - and clean it..
25157                 this.cleanWord(cn);
25158             }
25159             node.parentNode.removeChild(node);
25160             /// no need to iterate chidlren = it's got none..
25161             //this.iterateChildren(node, this.cleanWord);
25162             return;
25163         }
25164         // clean styles
25165         if (node.className.length) {
25166             
25167             var cn = node.className.split(/\W+/);
25168             var cna = [];
25169             Roo.each(cn, function(cls) {
25170                 if (cls.match(/Mso[a-zA-Z]+/)) {
25171                     return;
25172                 }
25173                 cna.push(cls);
25174             });
25175             node.className = cna.length ? cna.join(' ') : '';
25176             if (!cna.length) {
25177                 node.removeAttribute("class");
25178             }
25179         }
25180         
25181         if (node.hasAttribute("lang")) {
25182             node.removeAttribute("lang");
25183         }
25184         
25185         if (node.hasAttribute("style")) {
25186             
25187             var styles = node.getAttribute("style").split(";");
25188             var nstyle = [];
25189             Roo.each(styles, function(s) {
25190                 if (!s.match(/:/)) {
25191                     return;
25192                 }
25193                 var kv = s.split(":");
25194                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25195                     return;
25196                 }
25197                 // what ever is left... we allow.
25198                 nstyle.push(s);
25199             });
25200             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25201             if (!nstyle.length) {
25202                 node.removeAttribute('style');
25203             }
25204         }
25205         this.iterateChildren(node, this.cleanWord);
25206         
25207         
25208         
25209     },
25210     /**
25211      * iterateChildren of a Node, calling fn each time, using this as the scole..
25212      * @param {DomNode} node node to iterate children of.
25213      * @param {Function} fn method of this class to call on each item.
25214      */
25215     iterateChildren : function(node, fn)
25216     {
25217         if (!node.childNodes.length) {
25218                 return;
25219         }
25220         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25221            fn.call(this, node.childNodes[i])
25222         }
25223     },
25224     
25225     
25226     /**
25227      * cleanTableWidths.
25228      *
25229      * Quite often pasting from word etc.. results in tables with column and widths.
25230      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25231      *
25232      */
25233     cleanTableWidths : function(node)
25234     {
25235          
25236          
25237         if (!node) {
25238             this.cleanTableWidths(this.doc.body);
25239             return;
25240         }
25241         
25242         // ignore list...
25243         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25244             return; 
25245         }
25246         Roo.log(node.tagName);
25247         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25248             this.iterateChildren(node, this.cleanTableWidths);
25249             return;
25250         }
25251         if (node.hasAttribute('width')) {
25252             node.removeAttribute('width');
25253         }
25254         
25255          
25256         if (node.hasAttribute("style")) {
25257             // pretty basic...
25258             
25259             var styles = node.getAttribute("style").split(";");
25260             var nstyle = [];
25261             Roo.each(styles, function(s) {
25262                 if (!s.match(/:/)) {
25263                     return;
25264                 }
25265                 var kv = s.split(":");
25266                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25267                     return;
25268                 }
25269                 // what ever is left... we allow.
25270                 nstyle.push(s);
25271             });
25272             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25273             if (!nstyle.length) {
25274                 node.removeAttribute('style');
25275             }
25276         }
25277         
25278         this.iterateChildren(node, this.cleanTableWidths);
25279         
25280         
25281     },
25282     
25283     
25284     
25285     
25286     domToHTML : function(currentElement, depth, nopadtext) {
25287         
25288         depth = depth || 0;
25289         nopadtext = nopadtext || false;
25290     
25291         if (!currentElement) {
25292             return this.domToHTML(this.doc.body);
25293         }
25294         
25295         //Roo.log(currentElement);
25296         var j;
25297         var allText = false;
25298         var nodeName = currentElement.nodeName;
25299         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25300         
25301         if  (nodeName == '#text') {
25302             
25303             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25304         }
25305         
25306         
25307         var ret = '';
25308         if (nodeName != 'BODY') {
25309              
25310             var i = 0;
25311             // Prints the node tagName, such as <A>, <IMG>, etc
25312             if (tagName) {
25313                 var attr = [];
25314                 for(i = 0; i < currentElement.attributes.length;i++) {
25315                     // quoting?
25316                     var aname = currentElement.attributes.item(i).name;
25317                     if (!currentElement.attributes.item(i).value.length) {
25318                         continue;
25319                     }
25320                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25321                 }
25322                 
25323                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25324             } 
25325             else {
25326                 
25327                 // eack
25328             }
25329         } else {
25330             tagName = false;
25331         }
25332         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25333             return ret;
25334         }
25335         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25336             nopadtext = true;
25337         }
25338         
25339         
25340         // Traverse the tree
25341         i = 0;
25342         var currentElementChild = currentElement.childNodes.item(i);
25343         var allText = true;
25344         var innerHTML  = '';
25345         lastnode = '';
25346         while (currentElementChild) {
25347             // Formatting code (indent the tree so it looks nice on the screen)
25348             var nopad = nopadtext;
25349             if (lastnode == 'SPAN') {
25350                 nopad  = true;
25351             }
25352             // text
25353             if  (currentElementChild.nodeName == '#text') {
25354                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25355                 toadd = nopadtext ? toadd : toadd.trim();
25356                 if (!nopad && toadd.length > 80) {
25357                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25358                 }
25359                 innerHTML  += toadd;
25360                 
25361                 i++;
25362                 currentElementChild = currentElement.childNodes.item(i);
25363                 lastNode = '';
25364                 continue;
25365             }
25366             allText = false;
25367             
25368             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25369                 
25370             // Recursively traverse the tree structure of the child node
25371             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25372             lastnode = currentElementChild.nodeName;
25373             i++;
25374             currentElementChild=currentElement.childNodes.item(i);
25375         }
25376         
25377         ret += innerHTML;
25378         
25379         if (!allText) {
25380                 // The remaining code is mostly for formatting the tree
25381             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25382         }
25383         
25384         
25385         if (tagName) {
25386             ret+= "</"+tagName+">";
25387         }
25388         return ret;
25389         
25390     },
25391         
25392     applyBlacklists : function()
25393     {
25394         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25395         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25396         
25397         this.white = [];
25398         this.black = [];
25399         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25400             if (b.indexOf(tag) > -1) {
25401                 return;
25402             }
25403             this.white.push(tag);
25404             
25405         }, this);
25406         
25407         Roo.each(w, function(tag) {
25408             if (b.indexOf(tag) > -1) {
25409                 return;
25410             }
25411             if (this.white.indexOf(tag) > -1) {
25412                 return;
25413             }
25414             this.white.push(tag);
25415             
25416         }, this);
25417         
25418         
25419         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25420             if (w.indexOf(tag) > -1) {
25421                 return;
25422             }
25423             this.black.push(tag);
25424             
25425         }, this);
25426         
25427         Roo.each(b, function(tag) {
25428             if (w.indexOf(tag) > -1) {
25429                 return;
25430             }
25431             if (this.black.indexOf(tag) > -1) {
25432                 return;
25433             }
25434             this.black.push(tag);
25435             
25436         }, this);
25437         
25438         
25439         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25440         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25441         
25442         this.cwhite = [];
25443         this.cblack = [];
25444         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25445             if (b.indexOf(tag) > -1) {
25446                 return;
25447             }
25448             this.cwhite.push(tag);
25449             
25450         }, this);
25451         
25452         Roo.each(w, function(tag) {
25453             if (b.indexOf(tag) > -1) {
25454                 return;
25455             }
25456             if (this.cwhite.indexOf(tag) > -1) {
25457                 return;
25458             }
25459             this.cwhite.push(tag);
25460             
25461         }, this);
25462         
25463         
25464         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25465             if (w.indexOf(tag) > -1) {
25466                 return;
25467             }
25468             this.cblack.push(tag);
25469             
25470         }, this);
25471         
25472         Roo.each(b, function(tag) {
25473             if (w.indexOf(tag) > -1) {
25474                 return;
25475             }
25476             if (this.cblack.indexOf(tag) > -1) {
25477                 return;
25478             }
25479             this.cblack.push(tag);
25480             
25481         }, this);
25482     },
25483     
25484     setStylesheets : function(stylesheets)
25485     {
25486         if(typeof(stylesheets) == 'string'){
25487             Roo.get(this.iframe.contentDocument.head).createChild({
25488                 tag : 'link',
25489                 rel : 'stylesheet',
25490                 type : 'text/css',
25491                 href : stylesheets
25492             });
25493             
25494             return;
25495         }
25496         var _this = this;
25497      
25498         Roo.each(stylesheets, function(s) {
25499             if(!s.length){
25500                 return;
25501             }
25502             
25503             Roo.get(_this.iframe.contentDocument.head).createChild({
25504                 tag : 'link',
25505                 rel : 'stylesheet',
25506                 type : 'text/css',
25507                 href : s
25508             });
25509         });
25510
25511         
25512     },
25513     
25514     removeStylesheets : function()
25515     {
25516         var _this = this;
25517         
25518         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25519             s.remove();
25520         });
25521     },
25522     
25523     setStyle : function(style)
25524     {
25525         Roo.get(this.iframe.contentDocument.head).createChild({
25526             tag : 'style',
25527             type : 'text/css',
25528             html : style
25529         });
25530
25531         return;
25532     }
25533     
25534     // hide stuff that is not compatible
25535     /**
25536      * @event blur
25537      * @hide
25538      */
25539     /**
25540      * @event change
25541      * @hide
25542      */
25543     /**
25544      * @event focus
25545      * @hide
25546      */
25547     /**
25548      * @event specialkey
25549      * @hide
25550      */
25551     /**
25552      * @cfg {String} fieldClass @hide
25553      */
25554     /**
25555      * @cfg {String} focusClass @hide
25556      */
25557     /**
25558      * @cfg {String} autoCreate @hide
25559      */
25560     /**
25561      * @cfg {String} inputType @hide
25562      */
25563     /**
25564      * @cfg {String} invalidClass @hide
25565      */
25566     /**
25567      * @cfg {String} invalidText @hide
25568      */
25569     /**
25570      * @cfg {String} msgFx @hide
25571      */
25572     /**
25573      * @cfg {String} validateOnBlur @hide
25574      */
25575 });
25576
25577 Roo.HtmlEditorCore.white = [
25578         'area', 'br', 'img', 'input', 'hr', 'wbr',
25579         
25580        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25581        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25582        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25583        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25584        'table',   'ul',         'xmp', 
25585        
25586        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25587       'thead',   'tr', 
25588      
25589       'dir', 'menu', 'ol', 'ul', 'dl',
25590        
25591       'embed',  'object'
25592 ];
25593
25594
25595 Roo.HtmlEditorCore.black = [
25596     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25597         'applet', // 
25598         'base',   'basefont', 'bgsound', 'blink',  'body', 
25599         'frame',  'frameset', 'head',    'html',   'ilayer', 
25600         'iframe', 'layer',  'link',     'meta',    'object',   
25601         'script', 'style' ,'title',  'xml' // clean later..
25602 ];
25603 Roo.HtmlEditorCore.clean = [
25604     'script', 'style', 'title', 'xml'
25605 ];
25606 Roo.HtmlEditorCore.remove = [
25607     'font'
25608 ];
25609 // attributes..
25610
25611 Roo.HtmlEditorCore.ablack = [
25612     'on'
25613 ];
25614     
25615 Roo.HtmlEditorCore.aclean = [ 
25616     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25617 ];
25618
25619 // protocols..
25620 Roo.HtmlEditorCore.pwhite= [
25621         'http',  'https',  'mailto'
25622 ];
25623
25624 // white listed style attributes.
25625 Roo.HtmlEditorCore.cwhite= [
25626       //  'text-align', /// default is to allow most things..
25627       
25628          
25629 //        'font-size'//??
25630 ];
25631
25632 // black listed style attributes.
25633 Roo.HtmlEditorCore.cblack= [
25634       //  'font-size' -- this can be set by the project 
25635 ];
25636
25637
25638 Roo.HtmlEditorCore.swapCodes   =[ 
25639     [    8211, "--" ], 
25640     [    8212, "--" ], 
25641     [    8216,  "'" ],  
25642     [    8217, "'" ],  
25643     [    8220, '"' ],  
25644     [    8221, '"' ],  
25645     [    8226, "*" ],  
25646     [    8230, "..." ]
25647 ]; 
25648
25649     /*
25650  * - LGPL
25651  *
25652  * HtmlEditor
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.HtmlEditor
25658  * @extends Roo.bootstrap.TextArea
25659  * Bootstrap HtmlEditor class
25660
25661  * @constructor
25662  * Create a new HtmlEditor
25663  * @param {Object} config The config object
25664  */
25665
25666 Roo.bootstrap.HtmlEditor = function(config){
25667     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25668     if (!this.toolbars) {
25669         this.toolbars = [];
25670     }
25671     
25672     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25673     this.addEvents({
25674             /**
25675              * @event initialize
25676              * Fires when the editor is fully initialized (including the iframe)
25677              * @param {HtmlEditor} this
25678              */
25679             initialize: true,
25680             /**
25681              * @event activate
25682              * Fires when the editor is first receives the focus. Any insertion must wait
25683              * until after this event.
25684              * @param {HtmlEditor} this
25685              */
25686             activate: true,
25687              /**
25688              * @event beforesync
25689              * Fires before the textarea is updated with content from the editor iframe. Return false
25690              * to cancel the sync.
25691              * @param {HtmlEditor} this
25692              * @param {String} html
25693              */
25694             beforesync: true,
25695              /**
25696              * @event beforepush
25697              * Fires before the iframe editor is updated with content from the textarea. Return false
25698              * to cancel the push.
25699              * @param {HtmlEditor} this
25700              * @param {String} html
25701              */
25702             beforepush: true,
25703              /**
25704              * @event sync
25705              * Fires when the textarea is updated with content from the editor iframe.
25706              * @param {HtmlEditor} this
25707              * @param {String} html
25708              */
25709             sync: true,
25710              /**
25711              * @event push
25712              * Fires when the iframe editor is updated with content from the textarea.
25713              * @param {HtmlEditor} this
25714              * @param {String} html
25715              */
25716             push: true,
25717              /**
25718              * @event editmodechange
25719              * Fires when the editor switches edit modes
25720              * @param {HtmlEditor} this
25721              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25722              */
25723             editmodechange: true,
25724             /**
25725              * @event editorevent
25726              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25727              * @param {HtmlEditor} this
25728              */
25729             editorevent: true,
25730             /**
25731              * @event firstfocus
25732              * Fires when on first focus - needed by toolbars..
25733              * @param {HtmlEditor} this
25734              */
25735             firstfocus: true,
25736             /**
25737              * @event autosave
25738              * Auto save the htmlEditor value as a file into Events
25739              * @param {HtmlEditor} this
25740              */
25741             autosave: true,
25742             /**
25743              * @event savedpreview
25744              * preview the saved version of htmlEditor
25745              * @param {HtmlEditor} this
25746              */
25747             savedpreview: true
25748         });
25749 };
25750
25751
25752 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25753     
25754     
25755       /**
25756      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25757      */
25758     toolbars : false,
25759     
25760      /**
25761     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25762     */
25763     btns : [],
25764    
25765      /**
25766      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25767      *                        Roo.resizable.
25768      */
25769     resizable : false,
25770      /**
25771      * @cfg {Number} height (in pixels)
25772      */   
25773     height: 300,
25774    /**
25775      * @cfg {Number} width (in pixels)
25776      */   
25777     width: false,
25778     
25779     /**
25780      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25781      * 
25782      */
25783     stylesheets: false,
25784     
25785     // id of frame..
25786     frameId: false,
25787     
25788     // private properties
25789     validationEvent : false,
25790     deferHeight: true,
25791     initialized : false,
25792     activated : false,
25793     
25794     onFocus : Roo.emptyFn,
25795     iframePad:3,
25796     hideMode:'offsets',
25797     
25798     tbContainer : false,
25799     
25800     bodyCls : '',
25801     
25802     toolbarContainer :function() {
25803         return this.wrap.select('.x-html-editor-tb',true).first();
25804     },
25805
25806     /**
25807      * Protected method that will not generally be called directly. It
25808      * is called when the editor creates its toolbar. Override this method if you need to
25809      * add custom toolbar buttons.
25810      * @param {HtmlEditor} editor
25811      */
25812     createToolbar : function(){
25813         Roo.log('renewing');
25814         Roo.log("create toolbars");
25815         
25816         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25817         this.toolbars[0].render(this.toolbarContainer());
25818         
25819         return;
25820         
25821 //        if (!editor.toolbars || !editor.toolbars.length) {
25822 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25823 //        }
25824 //        
25825 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25826 //            editor.toolbars[i] = Roo.factory(
25827 //                    typeof(editor.toolbars[i]) == 'string' ?
25828 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25829 //                Roo.bootstrap.HtmlEditor);
25830 //            editor.toolbars[i].init(editor);
25831 //        }
25832     },
25833
25834      
25835     // private
25836     onRender : function(ct, position)
25837     {
25838        // Roo.log("Call onRender: " + this.xtype);
25839         var _t = this;
25840         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25841       
25842         this.wrap = this.inputEl().wrap({
25843             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25844         });
25845         
25846         this.editorcore.onRender(ct, position);
25847          
25848         if (this.resizable) {
25849             this.resizeEl = new Roo.Resizable(this.wrap, {
25850                 pinned : true,
25851                 wrap: true,
25852                 dynamic : true,
25853                 minHeight : this.height,
25854                 height: this.height,
25855                 handles : this.resizable,
25856                 width: this.width,
25857                 listeners : {
25858                     resize : function(r, w, h) {
25859                         _t.onResize(w,h); // -something
25860                     }
25861                 }
25862             });
25863             
25864         }
25865         this.createToolbar(this);
25866        
25867         
25868         if(!this.width && this.resizable){
25869             this.setSize(this.wrap.getSize());
25870         }
25871         if (this.resizeEl) {
25872             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25873             // should trigger onReize..
25874         }
25875         
25876     },
25877
25878     // private
25879     onResize : function(w, h)
25880     {
25881         Roo.log('resize: ' +w + ',' + h );
25882         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25883         var ew = false;
25884         var eh = false;
25885         
25886         if(this.inputEl() ){
25887             if(typeof w == 'number'){
25888                 var aw = w - this.wrap.getFrameWidth('lr');
25889                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25890                 ew = aw;
25891             }
25892             if(typeof h == 'number'){
25893                  var tbh = -11;  // fixme it needs to tool bar size!
25894                 for (var i =0; i < this.toolbars.length;i++) {
25895                     // fixme - ask toolbars for heights?
25896                     tbh += this.toolbars[i].el.getHeight();
25897                     //if (this.toolbars[i].footer) {
25898                     //    tbh += this.toolbars[i].footer.el.getHeight();
25899                     //}
25900                 }
25901               
25902                 
25903                 
25904                 
25905                 
25906                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25907                 ah -= 5; // knock a few pixes off for look..
25908                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25909                 var eh = ah;
25910             }
25911         }
25912         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25913         this.editorcore.onResize(ew,eh);
25914         
25915     },
25916
25917     /**
25918      * Toggles the editor between standard and source edit mode.
25919      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25920      */
25921     toggleSourceEdit : function(sourceEditMode)
25922     {
25923         this.editorcore.toggleSourceEdit(sourceEditMode);
25924         
25925         if(this.editorcore.sourceEditMode){
25926             Roo.log('editor - showing textarea');
25927             
25928 //            Roo.log('in');
25929 //            Roo.log(this.syncValue());
25930             this.syncValue();
25931             this.inputEl().removeClass(['hide', 'x-hidden']);
25932             this.inputEl().dom.removeAttribute('tabIndex');
25933             this.inputEl().focus();
25934         }else{
25935             Roo.log('editor - hiding textarea');
25936 //            Roo.log('out')
25937 //            Roo.log(this.pushValue()); 
25938             this.pushValue();
25939             
25940             this.inputEl().addClass(['hide', 'x-hidden']);
25941             this.inputEl().dom.setAttribute('tabIndex', -1);
25942             //this.deferFocus();
25943         }
25944          
25945         if(this.resizable){
25946             this.setSize(this.wrap.getSize());
25947         }
25948         
25949         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25950     },
25951  
25952     // private (for BoxComponent)
25953     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25954
25955     // private (for BoxComponent)
25956     getResizeEl : function(){
25957         return this.wrap;
25958     },
25959
25960     // private (for BoxComponent)
25961     getPositionEl : function(){
25962         return this.wrap;
25963     },
25964
25965     // private
25966     initEvents : function(){
25967         this.originalValue = this.getValue();
25968     },
25969
25970 //    /**
25971 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25972 //     * @method
25973 //     */
25974 //    markInvalid : Roo.emptyFn,
25975 //    /**
25976 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25977 //     * @method
25978 //     */
25979 //    clearInvalid : Roo.emptyFn,
25980
25981     setValue : function(v){
25982         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25983         this.editorcore.pushValue();
25984     },
25985
25986      
25987     // private
25988     deferFocus : function(){
25989         this.focus.defer(10, this);
25990     },
25991
25992     // doc'ed in Field
25993     focus : function(){
25994         this.editorcore.focus();
25995         
25996     },
25997       
25998
25999     // private
26000     onDestroy : function(){
26001         
26002         
26003         
26004         if(this.rendered){
26005             
26006             for (var i =0; i < this.toolbars.length;i++) {
26007                 // fixme - ask toolbars for heights?
26008                 this.toolbars[i].onDestroy();
26009             }
26010             
26011             this.wrap.dom.innerHTML = '';
26012             this.wrap.remove();
26013         }
26014     },
26015
26016     // private
26017     onFirstFocus : function(){
26018         //Roo.log("onFirstFocus");
26019         this.editorcore.onFirstFocus();
26020          for (var i =0; i < this.toolbars.length;i++) {
26021             this.toolbars[i].onFirstFocus();
26022         }
26023         
26024     },
26025     
26026     // private
26027     syncValue : function()
26028     {   
26029         this.editorcore.syncValue();
26030     },
26031     
26032     pushValue : function()
26033     {   
26034         this.editorcore.pushValue();
26035     }
26036      
26037     
26038     // hide stuff that is not compatible
26039     /**
26040      * @event blur
26041      * @hide
26042      */
26043     /**
26044      * @event change
26045      * @hide
26046      */
26047     /**
26048      * @event focus
26049      * @hide
26050      */
26051     /**
26052      * @event specialkey
26053      * @hide
26054      */
26055     /**
26056      * @cfg {String} fieldClass @hide
26057      */
26058     /**
26059      * @cfg {String} focusClass @hide
26060      */
26061     /**
26062      * @cfg {String} autoCreate @hide
26063      */
26064     /**
26065      * @cfg {String} inputType @hide
26066      */
26067      
26068     /**
26069      * @cfg {String} invalidText @hide
26070      */
26071     /**
26072      * @cfg {String} msgFx @hide
26073      */
26074     /**
26075      * @cfg {String} validateOnBlur @hide
26076      */
26077 });
26078  
26079     
26080    
26081    
26082    
26083       
26084 Roo.namespace('Roo.bootstrap.htmleditor');
26085 /**
26086  * @class Roo.bootstrap.HtmlEditorToolbar1
26087  * Basic Toolbar
26088  * 
26089  * @example
26090  * Usage:
26091  *
26092  new Roo.bootstrap.HtmlEditor({
26093     ....
26094     toolbars : [
26095         new Roo.bootstrap.HtmlEditorToolbar1({
26096             disable : { fonts: 1 , format: 1, ..., ... , ...],
26097             btns : [ .... ]
26098         })
26099     }
26100      
26101  * 
26102  * @cfg {Object} disable List of elements to disable..
26103  * @cfg {Array} btns List of additional buttons.
26104  * 
26105  * 
26106  * NEEDS Extra CSS? 
26107  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26108  */
26109  
26110 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26111 {
26112     
26113     Roo.apply(this, config);
26114     
26115     // default disabled, based on 'good practice'..
26116     this.disable = this.disable || {};
26117     Roo.applyIf(this.disable, {
26118         fontSize : true,
26119         colors : true,
26120         specialElements : true
26121     });
26122     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26123     
26124     this.editor = config.editor;
26125     this.editorcore = config.editor.editorcore;
26126     
26127     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26128     
26129     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26130     // dont call parent... till later.
26131 }
26132 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26133      
26134     bar : true,
26135     
26136     editor : false,
26137     editorcore : false,
26138     
26139     
26140     formats : [
26141         "p" ,  
26142         "h1","h2","h3","h4","h5","h6", 
26143         "pre", "code", 
26144         "abbr", "acronym", "address", "cite", "samp", "var",
26145         'div','span'
26146     ],
26147     
26148     onRender : function(ct, position)
26149     {
26150        // Roo.log("Call onRender: " + this.xtype);
26151         
26152        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26153        Roo.log(this.el);
26154        this.el.dom.style.marginBottom = '0';
26155        var _this = this;
26156        var editorcore = this.editorcore;
26157        var editor= this.editor;
26158        
26159        var children = [];
26160        var btn = function(id,cmd , toggle, handler, html){
26161        
26162             var  event = toggle ? 'toggle' : 'click';
26163        
26164             var a = {
26165                 size : 'sm',
26166                 xtype: 'Button',
26167                 xns: Roo.bootstrap,
26168                 //glyphicon : id,
26169                 fa: id,
26170                 cmd : id || cmd,
26171                 enableToggle:toggle !== false,
26172                 html : html || '',
26173                 pressed : toggle ? false : null,
26174                 listeners : {}
26175             };
26176             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26177                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26178             };
26179             children.push(a);
26180             return a;
26181        }
26182        
26183     //    var cb_box = function...
26184         
26185         var style = {
26186                 xtype: 'Button',
26187                 size : 'sm',
26188                 xns: Roo.bootstrap,
26189                 fa : 'font',
26190                 //html : 'submit'
26191                 menu : {
26192                     xtype: 'Menu',
26193                     xns: Roo.bootstrap,
26194                     items:  []
26195                 }
26196         };
26197         Roo.each(this.formats, function(f) {
26198             style.menu.items.push({
26199                 xtype :'MenuItem',
26200                 xns: Roo.bootstrap,
26201                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26202                 tagname : f,
26203                 listeners : {
26204                     click : function()
26205                     {
26206                         editorcore.insertTag(this.tagname);
26207                         editor.focus();
26208                     }
26209                 }
26210                 
26211             });
26212         });
26213         children.push(style);   
26214         
26215         btn('bold',false,true);
26216         btn('italic',false,true);
26217         btn('align-left', 'justifyleft',true);
26218         btn('align-center', 'justifycenter',true);
26219         btn('align-right' , 'justifyright',true);
26220         btn('link', false, false, function(btn) {
26221             //Roo.log("create link?");
26222             var url = prompt(this.createLinkText, this.defaultLinkValue);
26223             if(url && url != 'http:/'+'/'){
26224                 this.editorcore.relayCmd('createlink', url);
26225             }
26226         }),
26227         btn('list','insertunorderedlist',true);
26228         btn('pencil', false,true, function(btn){
26229                 Roo.log(this);
26230                 this.toggleSourceEdit(btn.pressed);
26231         });
26232         
26233         if (this.editor.btns.length > 0) {
26234             for (var i = 0; i<this.editor.btns.length; i++) {
26235                 children.push(this.editor.btns[i]);
26236             }
26237         }
26238         
26239         /*
26240         var cog = {
26241                 xtype: 'Button',
26242                 size : 'sm',
26243                 xns: Roo.bootstrap,
26244                 glyphicon : 'cog',
26245                 //html : 'submit'
26246                 menu : {
26247                     xtype: 'Menu',
26248                     xns: Roo.bootstrap,
26249                     items:  []
26250                 }
26251         };
26252         
26253         cog.menu.items.push({
26254             xtype :'MenuItem',
26255             xns: Roo.bootstrap,
26256             html : Clean styles,
26257             tagname : f,
26258             listeners : {
26259                 click : function()
26260                 {
26261                     editorcore.insertTag(this.tagname);
26262                     editor.focus();
26263                 }
26264             }
26265             
26266         });
26267        */
26268         
26269          
26270        this.xtype = 'NavSimplebar';
26271         
26272         for(var i=0;i< children.length;i++) {
26273             
26274             this.buttons.add(this.addxtypeChild(children[i]));
26275             
26276         }
26277         
26278         editor.on('editorevent', this.updateToolbar, this);
26279     },
26280     onBtnClick : function(id)
26281     {
26282        this.editorcore.relayCmd(id);
26283        this.editorcore.focus();
26284     },
26285     
26286     /**
26287      * Protected method that will not generally be called directly. It triggers
26288      * a toolbar update by reading the markup state of the current selection in the editor.
26289      */
26290     updateToolbar: function(){
26291
26292         if(!this.editorcore.activated){
26293             this.editor.onFirstFocus(); // is this neeed?
26294             return;
26295         }
26296
26297         var btns = this.buttons; 
26298         var doc = this.editorcore.doc;
26299         btns.get('bold').setActive(doc.queryCommandState('bold'));
26300         btns.get('italic').setActive(doc.queryCommandState('italic'));
26301         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26302         
26303         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26304         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26305         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26306         
26307         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26308         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26309          /*
26310         
26311         var ans = this.editorcore.getAllAncestors();
26312         if (this.formatCombo) {
26313             
26314             
26315             var store = this.formatCombo.store;
26316             this.formatCombo.setValue("");
26317             for (var i =0; i < ans.length;i++) {
26318                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26319                     // select it..
26320                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26321                     break;
26322                 }
26323             }
26324         }
26325         
26326         
26327         
26328         // hides menus... - so this cant be on a menu...
26329         Roo.bootstrap.MenuMgr.hideAll();
26330         */
26331         Roo.bootstrap.MenuMgr.hideAll();
26332         //this.editorsyncValue();
26333     },
26334     onFirstFocus: function() {
26335         this.buttons.each(function(item){
26336            item.enable();
26337         });
26338     },
26339     toggleSourceEdit : function(sourceEditMode){
26340         
26341           
26342         if(sourceEditMode){
26343             Roo.log("disabling buttons");
26344            this.buttons.each( function(item){
26345                 if(item.cmd != 'pencil'){
26346                     item.disable();
26347                 }
26348             });
26349           
26350         }else{
26351             Roo.log("enabling buttons");
26352             if(this.editorcore.initialized){
26353                 this.buttons.each( function(item){
26354                     item.enable();
26355                 });
26356             }
26357             
26358         }
26359         Roo.log("calling toggole on editor");
26360         // tell the editor that it's been pressed..
26361         this.editor.toggleSourceEdit(sourceEditMode);
26362        
26363     }
26364 });
26365
26366
26367
26368
26369  
26370 /*
26371  * - LGPL
26372  */
26373
26374 /**
26375  * @class Roo.bootstrap.Markdown
26376  * @extends Roo.bootstrap.TextArea
26377  * Bootstrap Showdown editable area
26378  * @cfg {string} content
26379  * 
26380  * @constructor
26381  * Create a new Showdown
26382  */
26383
26384 Roo.bootstrap.Markdown = function(config){
26385     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26386    
26387 };
26388
26389 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26390     
26391     editing :false,
26392     
26393     initEvents : function()
26394     {
26395         
26396         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26397         this.markdownEl = this.el.createChild({
26398             cls : 'roo-markdown-area'
26399         });
26400         this.inputEl().addClass('d-none');
26401         if (this.getValue() == '') {
26402             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26403             
26404         } else {
26405             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26406         }
26407         this.markdownEl.on('click', this.toggleTextEdit, this);
26408         this.on('blur', this.toggleTextEdit, this);
26409         this.on('specialkey', this.resizeTextArea, this);
26410     },
26411     
26412     toggleTextEdit : function()
26413     {
26414         var sh = this.markdownEl.getHeight();
26415         this.inputEl().addClass('d-none');
26416         this.markdownEl.addClass('d-none');
26417         if (!this.editing) {
26418             // show editor?
26419             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26420             this.inputEl().removeClass('d-none');
26421             this.inputEl().focus();
26422             this.editing = true;
26423             return;
26424         }
26425         // show showdown...
26426         this.updateMarkdown();
26427         this.markdownEl.removeClass('d-none');
26428         this.editing = false;
26429         return;
26430     },
26431     updateMarkdown : function()
26432     {
26433         if (this.getValue() == '') {
26434             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26435             return;
26436         }
26437  
26438         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26439     },
26440     
26441     resizeTextArea: function () {
26442         
26443         var sh = 100;
26444         Roo.log([sh, this.getValue().split("\n").length * 30]);
26445         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26446     },
26447     setValue : function(val)
26448     {
26449         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26450         if (!this.editing) {
26451             this.updateMarkdown();
26452         }
26453         
26454     },
26455     focus : function()
26456     {
26457         if (!this.editing) {
26458             this.toggleTextEdit();
26459         }
26460         
26461     }
26462
26463
26464 });
26465 /**
26466  * @class Roo.bootstrap.Table.AbstractSelectionModel
26467  * @extends Roo.util.Observable
26468  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26469  * implemented by descendant classes.  This class should not be directly instantiated.
26470  * @constructor
26471  */
26472 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26473     this.locked = false;
26474     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26475 };
26476
26477
26478 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26479     /** @ignore Called by the grid automatically. Do not call directly. */
26480     init : function(grid){
26481         this.grid = grid;
26482         this.initEvents();
26483     },
26484
26485     /**
26486      * Locks the selections.
26487      */
26488     lock : function(){
26489         this.locked = true;
26490     },
26491
26492     /**
26493      * Unlocks the selections.
26494      */
26495     unlock : function(){
26496         this.locked = false;
26497     },
26498
26499     /**
26500      * Returns true if the selections are locked.
26501      * @return {Boolean}
26502      */
26503     isLocked : function(){
26504         return this.locked;
26505     },
26506     
26507     
26508     initEvents : function ()
26509     {
26510         
26511     }
26512 });
26513 /**
26514  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26515  * @class Roo.bootstrap.Table.RowSelectionModel
26516  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26517  * It supports multiple selections and keyboard selection/navigation. 
26518  * @constructor
26519  * @param {Object} config
26520  */
26521
26522 Roo.bootstrap.Table.RowSelectionModel = function(config){
26523     Roo.apply(this, config);
26524     this.selections = new Roo.util.MixedCollection(false, function(o){
26525         return o.id;
26526     });
26527
26528     this.last = false;
26529     this.lastActive = false;
26530
26531     this.addEvents({
26532         /**
26533              * @event selectionchange
26534              * Fires when the selection changes
26535              * @param {SelectionModel} this
26536              */
26537             "selectionchange" : true,
26538         /**
26539              * @event afterselectionchange
26540              * Fires after the selection changes (eg. by key press or clicking)
26541              * @param {SelectionModel} this
26542              */
26543             "afterselectionchange" : true,
26544         /**
26545              * @event beforerowselect
26546              * Fires when a row is selected being selected, return false to cancel.
26547              * @param {SelectionModel} this
26548              * @param {Number} rowIndex The selected index
26549              * @param {Boolean} keepExisting False if other selections will be cleared
26550              */
26551             "beforerowselect" : true,
26552         /**
26553              * @event rowselect
26554              * Fires when a row is selected.
26555              * @param {SelectionModel} this
26556              * @param {Number} rowIndex The selected index
26557              * @param {Roo.data.Record} r The record
26558              */
26559             "rowselect" : true,
26560         /**
26561              * @event rowdeselect
26562              * Fires when a row is deselected.
26563              * @param {SelectionModel} this
26564              * @param {Number} rowIndex The selected index
26565              */
26566         "rowdeselect" : true
26567     });
26568     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26569     this.locked = false;
26570  };
26571
26572 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26573     /**
26574      * @cfg {Boolean} singleSelect
26575      * True to allow selection of only one row at a time (defaults to false)
26576      */
26577     singleSelect : false,
26578
26579     // private
26580     initEvents : function()
26581     {
26582
26583         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26584         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26585         //}else{ // allow click to work like normal
26586          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26587         //}
26588         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26589         this.grid.on("rowclick", this.handleMouseDown, this);
26590         
26591         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26592             "up" : function(e){
26593                 if(!e.shiftKey){
26594                     this.selectPrevious(e.shiftKey);
26595                 }else if(this.last !== false && this.lastActive !== false){
26596                     var last = this.last;
26597                     this.selectRange(this.last,  this.lastActive-1);
26598                     this.grid.getView().focusRow(this.lastActive);
26599                     if(last !== false){
26600                         this.last = last;
26601                     }
26602                 }else{
26603                     this.selectFirstRow();
26604                 }
26605                 this.fireEvent("afterselectionchange", this);
26606             },
26607             "down" : function(e){
26608                 if(!e.shiftKey){
26609                     this.selectNext(e.shiftKey);
26610                 }else if(this.last !== false && this.lastActive !== false){
26611                     var last = this.last;
26612                     this.selectRange(this.last,  this.lastActive+1);
26613                     this.grid.getView().focusRow(this.lastActive);
26614                     if(last !== false){
26615                         this.last = last;
26616                     }
26617                 }else{
26618                     this.selectFirstRow();
26619                 }
26620                 this.fireEvent("afterselectionchange", this);
26621             },
26622             scope: this
26623         });
26624         this.grid.store.on('load', function(){
26625             this.selections.clear();
26626         },this);
26627         /*
26628         var view = this.grid.view;
26629         view.on("refresh", this.onRefresh, this);
26630         view.on("rowupdated", this.onRowUpdated, this);
26631         view.on("rowremoved", this.onRemove, this);
26632         */
26633     },
26634
26635     // private
26636     onRefresh : function()
26637     {
26638         var ds = this.grid.store, i, v = this.grid.view;
26639         var s = this.selections;
26640         s.each(function(r){
26641             if((i = ds.indexOfId(r.id)) != -1){
26642                 v.onRowSelect(i);
26643             }else{
26644                 s.remove(r);
26645             }
26646         });
26647     },
26648
26649     // private
26650     onRemove : function(v, index, r){
26651         this.selections.remove(r);
26652     },
26653
26654     // private
26655     onRowUpdated : function(v, index, r){
26656         if(this.isSelected(r)){
26657             v.onRowSelect(index);
26658         }
26659     },
26660
26661     /**
26662      * Select records.
26663      * @param {Array} records The records to select
26664      * @param {Boolean} keepExisting (optional) True to keep existing selections
26665      */
26666     selectRecords : function(records, keepExisting)
26667     {
26668         if(!keepExisting){
26669             this.clearSelections();
26670         }
26671             var ds = this.grid.store;
26672         for(var i = 0, len = records.length; i < len; i++){
26673             this.selectRow(ds.indexOf(records[i]), true);
26674         }
26675     },
26676
26677     /**
26678      * Gets the number of selected rows.
26679      * @return {Number}
26680      */
26681     getCount : function(){
26682         return this.selections.length;
26683     },
26684
26685     /**
26686      * Selects the first row in the grid.
26687      */
26688     selectFirstRow : function(){
26689         this.selectRow(0);
26690     },
26691
26692     /**
26693      * Select the last row.
26694      * @param {Boolean} keepExisting (optional) True to keep existing selections
26695      */
26696     selectLastRow : function(keepExisting){
26697         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26698         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26699     },
26700
26701     /**
26702      * Selects the row immediately following the last selected row.
26703      * @param {Boolean} keepExisting (optional) True to keep existing selections
26704      */
26705     selectNext : function(keepExisting)
26706     {
26707             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26708             this.selectRow(this.last+1, keepExisting);
26709             this.grid.getView().focusRow(this.last);
26710         }
26711     },
26712
26713     /**
26714      * Selects the row that precedes the last selected row.
26715      * @param {Boolean} keepExisting (optional) True to keep existing selections
26716      */
26717     selectPrevious : function(keepExisting){
26718         if(this.last){
26719             this.selectRow(this.last-1, keepExisting);
26720             this.grid.getView().focusRow(this.last);
26721         }
26722     },
26723
26724     /**
26725      * Returns the selected records
26726      * @return {Array} Array of selected records
26727      */
26728     getSelections : function(){
26729         return [].concat(this.selections.items);
26730     },
26731
26732     /**
26733      * Returns the first selected record.
26734      * @return {Record}
26735      */
26736     getSelected : function(){
26737         return this.selections.itemAt(0);
26738     },
26739
26740
26741     /**
26742      * Clears all selections.
26743      */
26744     clearSelections : function(fast)
26745     {
26746         if(this.locked) {
26747             return;
26748         }
26749         if(fast !== true){
26750                 var ds = this.grid.store;
26751             var s = this.selections;
26752             s.each(function(r){
26753                 this.deselectRow(ds.indexOfId(r.id));
26754             }, this);
26755             s.clear();
26756         }else{
26757             this.selections.clear();
26758         }
26759         this.last = false;
26760     },
26761
26762
26763     /**
26764      * Selects all rows.
26765      */
26766     selectAll : function(){
26767         if(this.locked) {
26768             return;
26769         }
26770         this.selections.clear();
26771         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26772             this.selectRow(i, true);
26773         }
26774     },
26775
26776     /**
26777      * Returns True if there is a selection.
26778      * @return {Boolean}
26779      */
26780     hasSelection : function(){
26781         return this.selections.length > 0;
26782     },
26783
26784     /**
26785      * Returns True if the specified row is selected.
26786      * @param {Number/Record} record The record or index of the record to check
26787      * @return {Boolean}
26788      */
26789     isSelected : function(index){
26790             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26791         return (r && this.selections.key(r.id) ? true : false);
26792     },
26793
26794     /**
26795      * Returns True if the specified record id is selected.
26796      * @param {String} id The id of record to check
26797      * @return {Boolean}
26798      */
26799     isIdSelected : function(id){
26800         return (this.selections.key(id) ? true : false);
26801     },
26802
26803
26804     // private
26805     handleMouseDBClick : function(e, t){
26806         
26807     },
26808     // private
26809     handleMouseDown : function(e, t)
26810     {
26811             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26812         if(this.isLocked() || rowIndex < 0 ){
26813             return;
26814         };
26815         if(e.shiftKey && this.last !== false){
26816             var last = this.last;
26817             this.selectRange(last, rowIndex, e.ctrlKey);
26818             this.last = last; // reset the last
26819             t.focus();
26820     
26821         }else{
26822             var isSelected = this.isSelected(rowIndex);
26823             //Roo.log("select row:" + rowIndex);
26824             if(isSelected){
26825                 this.deselectRow(rowIndex);
26826             } else {
26827                         this.selectRow(rowIndex, true);
26828             }
26829     
26830             /*
26831                 if(e.button !== 0 && isSelected){
26832                 alert('rowIndex 2: ' + rowIndex);
26833                     view.focusRow(rowIndex);
26834                 }else if(e.ctrlKey && isSelected){
26835                     this.deselectRow(rowIndex);
26836                 }else if(!isSelected){
26837                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26838                     view.focusRow(rowIndex);
26839                 }
26840             */
26841         }
26842         this.fireEvent("afterselectionchange", this);
26843     },
26844     // private
26845     handleDragableRowClick :  function(grid, rowIndex, e) 
26846     {
26847         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26848             this.selectRow(rowIndex, false);
26849             grid.view.focusRow(rowIndex);
26850              this.fireEvent("afterselectionchange", this);
26851         }
26852     },
26853     
26854     /**
26855      * Selects multiple rows.
26856      * @param {Array} rows Array of the indexes of the row to select
26857      * @param {Boolean} keepExisting (optional) True to keep existing selections
26858      */
26859     selectRows : function(rows, keepExisting){
26860         if(!keepExisting){
26861             this.clearSelections();
26862         }
26863         for(var i = 0, len = rows.length; i < len; i++){
26864             this.selectRow(rows[i], true);
26865         }
26866     },
26867
26868     /**
26869      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26870      * @param {Number} startRow The index of the first row in the range
26871      * @param {Number} endRow The index of the last row in the range
26872      * @param {Boolean} keepExisting (optional) True to retain existing selections
26873      */
26874     selectRange : function(startRow, endRow, keepExisting){
26875         if(this.locked) {
26876             return;
26877         }
26878         if(!keepExisting){
26879             this.clearSelections();
26880         }
26881         if(startRow <= endRow){
26882             for(var i = startRow; i <= endRow; i++){
26883                 this.selectRow(i, true);
26884             }
26885         }else{
26886             for(var i = startRow; i >= endRow; i--){
26887                 this.selectRow(i, true);
26888             }
26889         }
26890     },
26891
26892     /**
26893      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26894      * @param {Number} startRow The index of the first row in the range
26895      * @param {Number} endRow The index of the last row in the range
26896      */
26897     deselectRange : function(startRow, endRow, preventViewNotify){
26898         if(this.locked) {
26899             return;
26900         }
26901         for(var i = startRow; i <= endRow; i++){
26902             this.deselectRow(i, preventViewNotify);
26903         }
26904     },
26905
26906     /**
26907      * Selects a row.
26908      * @param {Number} row The index of the row to select
26909      * @param {Boolean} keepExisting (optional) True to keep existing selections
26910      */
26911     selectRow : function(index, keepExisting, preventViewNotify)
26912     {
26913             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26914             return;
26915         }
26916         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26917             if(!keepExisting || this.singleSelect){
26918                 this.clearSelections();
26919             }
26920             
26921             var r = this.grid.store.getAt(index);
26922             //console.log('selectRow - record id :' + r.id);
26923             
26924             this.selections.add(r);
26925             this.last = this.lastActive = index;
26926             if(!preventViewNotify){
26927                 var proxy = new Roo.Element(
26928                                 this.grid.getRowDom(index)
26929                 );
26930                 proxy.addClass('bg-info info');
26931             }
26932             this.fireEvent("rowselect", this, index, r);
26933             this.fireEvent("selectionchange", this);
26934         }
26935     },
26936
26937     /**
26938      * Deselects a row.
26939      * @param {Number} row The index of the row to deselect
26940      */
26941     deselectRow : function(index, preventViewNotify)
26942     {
26943         if(this.locked) {
26944             return;
26945         }
26946         if(this.last == index){
26947             this.last = false;
26948         }
26949         if(this.lastActive == index){
26950             this.lastActive = false;
26951         }
26952         
26953         var r = this.grid.store.getAt(index);
26954         if (!r) {
26955             return;
26956         }
26957         
26958         this.selections.remove(r);
26959         //.console.log('deselectRow - record id :' + r.id);
26960         if(!preventViewNotify){
26961         
26962             var proxy = new Roo.Element(
26963                 this.grid.getRowDom(index)
26964             );
26965             proxy.removeClass('bg-info info');
26966         }
26967         this.fireEvent("rowdeselect", this, index);
26968         this.fireEvent("selectionchange", this);
26969     },
26970
26971     // private
26972     restoreLast : function(){
26973         if(this._last){
26974             this.last = this._last;
26975         }
26976     },
26977
26978     // private
26979     acceptsNav : function(row, col, cm){
26980         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26981     },
26982
26983     // private
26984     onEditorKey : function(field, e){
26985         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26986         if(k == e.TAB){
26987             e.stopEvent();
26988             ed.completeEdit();
26989             if(e.shiftKey){
26990                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26991             }else{
26992                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26993             }
26994         }else if(k == e.ENTER && !e.ctrlKey){
26995             e.stopEvent();
26996             ed.completeEdit();
26997             if(e.shiftKey){
26998                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26999             }else{
27000                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27001             }
27002         }else if(k == e.ESC){
27003             ed.cancelEdit();
27004         }
27005         if(newCell){
27006             g.startEditing(newCell[0], newCell[1]);
27007         }
27008     }
27009 });
27010 /*
27011  * Based on:
27012  * Ext JS Library 1.1.1
27013  * Copyright(c) 2006-2007, Ext JS, LLC.
27014  *
27015  * Originally Released Under LGPL - original licence link has changed is not relivant.
27016  *
27017  * Fork - LGPL
27018  * <script type="text/javascript">
27019  */
27020  
27021 /**
27022  * @class Roo.bootstrap.PagingToolbar
27023  * @extends Roo.bootstrap.NavSimplebar
27024  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27025  * @constructor
27026  * Create a new PagingToolbar
27027  * @param {Object} config The config object
27028  * @param {Roo.data.Store} store
27029  */
27030 Roo.bootstrap.PagingToolbar = function(config)
27031 {
27032     // old args format still supported... - xtype is prefered..
27033         // created from xtype...
27034     
27035     this.ds = config.dataSource;
27036     
27037     if (config.store && !this.ds) {
27038         this.store= Roo.factory(config.store, Roo.data);
27039         this.ds = this.store;
27040         this.ds.xmodule = this.xmodule || false;
27041     }
27042     
27043     this.toolbarItems = [];
27044     if (config.items) {
27045         this.toolbarItems = config.items;
27046     }
27047     
27048     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27049     
27050     this.cursor = 0;
27051     
27052     if (this.ds) { 
27053         this.bind(this.ds);
27054     }
27055     
27056     if (Roo.bootstrap.version == 4) {
27057         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27058     } else {
27059         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27060     }
27061     
27062 };
27063
27064 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27065     /**
27066      * @cfg {Roo.data.Store} dataSource
27067      * The underlying data store providing the paged data
27068      */
27069     /**
27070      * @cfg {String/HTMLElement/Element} container
27071      * container The id or element that will contain the toolbar
27072      */
27073     /**
27074      * @cfg {Boolean} displayInfo
27075      * True to display the displayMsg (defaults to false)
27076      */
27077     /**
27078      * @cfg {Number} pageSize
27079      * The number of records to display per page (defaults to 20)
27080      */
27081     pageSize: 20,
27082     /**
27083      * @cfg {String} displayMsg
27084      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27085      */
27086     displayMsg : 'Displaying {0} - {1} of {2}',
27087     /**
27088      * @cfg {String} emptyMsg
27089      * The message to display when no records are found (defaults to "No data to display")
27090      */
27091     emptyMsg : 'No data to display',
27092     /**
27093      * Customizable piece of the default paging text (defaults to "Page")
27094      * @type String
27095      */
27096     beforePageText : "Page",
27097     /**
27098      * Customizable piece of the default paging text (defaults to "of %0")
27099      * @type String
27100      */
27101     afterPageText : "of {0}",
27102     /**
27103      * Customizable piece of the default paging text (defaults to "First Page")
27104      * @type String
27105      */
27106     firstText : "First Page",
27107     /**
27108      * Customizable piece of the default paging text (defaults to "Previous Page")
27109      * @type String
27110      */
27111     prevText : "Previous Page",
27112     /**
27113      * Customizable piece of the default paging text (defaults to "Next Page")
27114      * @type String
27115      */
27116     nextText : "Next Page",
27117     /**
27118      * Customizable piece of the default paging text (defaults to "Last Page")
27119      * @type String
27120      */
27121     lastText : "Last Page",
27122     /**
27123      * Customizable piece of the default paging text (defaults to "Refresh")
27124      * @type String
27125      */
27126     refreshText : "Refresh",
27127
27128     buttons : false,
27129     // private
27130     onRender : function(ct, position) 
27131     {
27132         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27133         this.navgroup.parentId = this.id;
27134         this.navgroup.onRender(this.el, null);
27135         // add the buttons to the navgroup
27136         
27137         if(this.displayInfo){
27138             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27139             this.displayEl = this.el.select('.x-paging-info', true).first();
27140 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27141 //            this.displayEl = navel.el.select('span',true).first();
27142         }
27143         
27144         var _this = this;
27145         
27146         if(this.buttons){
27147             Roo.each(_this.buttons, function(e){ // this might need to use render????
27148                Roo.factory(e).render(_this.el);
27149             });
27150         }
27151             
27152         Roo.each(_this.toolbarItems, function(e) {
27153             _this.navgroup.addItem(e);
27154         });
27155         
27156         
27157         this.first = this.navgroup.addItem({
27158             tooltip: this.firstText,
27159             cls: "prev btn-outline-secondary",
27160             html : ' <i class="fa fa-step-backward"></i>',
27161             disabled: true,
27162             preventDefault: true,
27163             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27164         });
27165         
27166         this.prev =  this.navgroup.addItem({
27167             tooltip: this.prevText,
27168             cls: "prev btn-outline-secondary",
27169             html : ' <i class="fa fa-backward"></i>',
27170             disabled: true,
27171             preventDefault: true,
27172             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27173         });
27174     //this.addSeparator();
27175         
27176         
27177         var field = this.navgroup.addItem( {
27178             tagtype : 'span',
27179             cls : 'x-paging-position  btn-outline-secondary',
27180              disabled: true,
27181             html : this.beforePageText  +
27182                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27183                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27184          } ); //?? escaped?
27185         
27186         this.field = field.el.select('input', true).first();
27187         this.field.on("keydown", this.onPagingKeydown, this);
27188         this.field.on("focus", function(){this.dom.select();});
27189     
27190     
27191         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27192         //this.field.setHeight(18);
27193         //this.addSeparator();
27194         this.next = this.navgroup.addItem({
27195             tooltip: this.nextText,
27196             cls: "next btn-outline-secondary",
27197             html : ' <i class="fa fa-forward"></i>',
27198             disabled: true,
27199             preventDefault: true,
27200             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27201         });
27202         this.last = this.navgroup.addItem({
27203             tooltip: this.lastText,
27204             html : ' <i class="fa fa-step-forward"></i>',
27205             cls: "next btn-outline-secondary",
27206             disabled: true,
27207             preventDefault: true,
27208             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27209         });
27210     //this.addSeparator();
27211         this.loading = this.navgroup.addItem({
27212             tooltip: this.refreshText,
27213             cls: "btn-outline-secondary",
27214             html : ' <i class="fa fa-refresh"></i>',
27215             preventDefault: true,
27216             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27217         });
27218         
27219     },
27220
27221     // private
27222     updateInfo : function(){
27223         if(this.displayEl){
27224             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27225             var msg = count == 0 ?
27226                 this.emptyMsg :
27227                 String.format(
27228                     this.displayMsg,
27229                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27230                 );
27231             this.displayEl.update(msg);
27232         }
27233     },
27234
27235     // private
27236     onLoad : function(ds, r, o)
27237     {
27238         this.cursor = o.params.start ? o.params.start : 0;
27239         
27240         var d = this.getPageData(),
27241             ap = d.activePage,
27242             ps = d.pages;
27243         
27244         
27245         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27246         this.field.dom.value = ap;
27247         this.first.setDisabled(ap == 1);
27248         this.prev.setDisabled(ap == 1);
27249         this.next.setDisabled(ap == ps);
27250         this.last.setDisabled(ap == ps);
27251         this.loading.enable();
27252         this.updateInfo();
27253     },
27254
27255     // private
27256     getPageData : function(){
27257         var total = this.ds.getTotalCount();
27258         return {
27259             total : total,
27260             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27261             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27262         };
27263     },
27264
27265     // private
27266     onLoadError : function(){
27267         this.loading.enable();
27268     },
27269
27270     // private
27271     onPagingKeydown : function(e){
27272         var k = e.getKey();
27273         var d = this.getPageData();
27274         if(k == e.RETURN){
27275             var v = this.field.dom.value, pageNum;
27276             if(!v || isNaN(pageNum = parseInt(v, 10))){
27277                 this.field.dom.value = d.activePage;
27278                 return;
27279             }
27280             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27281             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27282             e.stopEvent();
27283         }
27284         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))
27285         {
27286           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27287           this.field.dom.value = pageNum;
27288           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27289           e.stopEvent();
27290         }
27291         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27292         {
27293           var v = this.field.dom.value, pageNum; 
27294           var increment = (e.shiftKey) ? 10 : 1;
27295           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27296                 increment *= -1;
27297           }
27298           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27299             this.field.dom.value = d.activePage;
27300             return;
27301           }
27302           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27303           {
27304             this.field.dom.value = parseInt(v, 10) + increment;
27305             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27306             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27307           }
27308           e.stopEvent();
27309         }
27310     },
27311
27312     // private
27313     beforeLoad : function(){
27314         if(this.loading){
27315             this.loading.disable();
27316         }
27317     },
27318
27319     // private
27320     onClick : function(which){
27321         
27322         var ds = this.ds;
27323         if (!ds) {
27324             return;
27325         }
27326         
27327         switch(which){
27328             case "first":
27329                 ds.load({params:{start: 0, limit: this.pageSize}});
27330             break;
27331             case "prev":
27332                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27333             break;
27334             case "next":
27335                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27336             break;
27337             case "last":
27338                 var total = ds.getTotalCount();
27339                 var extra = total % this.pageSize;
27340                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27341                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27342             break;
27343             case "refresh":
27344                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27345             break;
27346         }
27347     },
27348
27349     /**
27350      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27351      * @param {Roo.data.Store} store The data store to unbind
27352      */
27353     unbind : function(ds){
27354         ds.un("beforeload", this.beforeLoad, this);
27355         ds.un("load", this.onLoad, this);
27356         ds.un("loadexception", this.onLoadError, this);
27357         ds.un("remove", this.updateInfo, this);
27358         ds.un("add", this.updateInfo, this);
27359         this.ds = undefined;
27360     },
27361
27362     /**
27363      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27364      * @param {Roo.data.Store} store The data store to bind
27365      */
27366     bind : function(ds){
27367         ds.on("beforeload", this.beforeLoad, this);
27368         ds.on("load", this.onLoad, this);
27369         ds.on("loadexception", this.onLoadError, this);
27370         ds.on("remove", this.updateInfo, this);
27371         ds.on("add", this.updateInfo, this);
27372         this.ds = ds;
27373     }
27374 });/*
27375  * - LGPL
27376  *
27377  * element
27378  * 
27379  */
27380
27381 /**
27382  * @class Roo.bootstrap.MessageBar
27383  * @extends Roo.bootstrap.Component
27384  * Bootstrap MessageBar class
27385  * @cfg {String} html contents of the MessageBar
27386  * @cfg {String} weight (info | success | warning | danger) default info
27387  * @cfg {String} beforeClass insert the bar before the given class
27388  * @cfg {Boolean} closable (true | false) default false
27389  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27390  * 
27391  * @constructor
27392  * Create a new Element
27393  * @param {Object} config The config object
27394  */
27395
27396 Roo.bootstrap.MessageBar = function(config){
27397     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27398 };
27399
27400 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27401     
27402     html: '',
27403     weight: 'info',
27404     closable: false,
27405     fixed: false,
27406     beforeClass: 'bootstrap-sticky-wrap',
27407     
27408     getAutoCreate : function(){
27409         
27410         var cfg = {
27411             tag: 'div',
27412             cls: 'alert alert-dismissable alert-' + this.weight,
27413             cn: [
27414                 {
27415                     tag: 'span',
27416                     cls: 'message',
27417                     html: this.html || ''
27418                 }
27419             ]
27420         };
27421         
27422         if(this.fixed){
27423             cfg.cls += ' alert-messages-fixed';
27424         }
27425         
27426         if(this.closable){
27427             cfg.cn.push({
27428                 tag: 'button',
27429                 cls: 'close',
27430                 html: 'x'
27431             });
27432         }
27433         
27434         return cfg;
27435     },
27436     
27437     onRender : function(ct, position)
27438     {
27439         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27440         
27441         if(!this.el){
27442             var cfg = Roo.apply({},  this.getAutoCreate());
27443             cfg.id = Roo.id();
27444             
27445             if (this.cls) {
27446                 cfg.cls += ' ' + this.cls;
27447             }
27448             if (this.style) {
27449                 cfg.style = this.style;
27450             }
27451             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27452             
27453             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27454         }
27455         
27456         this.el.select('>button.close').on('click', this.hide, this);
27457         
27458     },
27459     
27460     show : function()
27461     {
27462         if (!this.rendered) {
27463             this.render();
27464         }
27465         
27466         this.el.show();
27467         
27468         this.fireEvent('show', this);
27469         
27470     },
27471     
27472     hide : function()
27473     {
27474         if (!this.rendered) {
27475             this.render();
27476         }
27477         
27478         this.el.hide();
27479         
27480         this.fireEvent('hide', this);
27481     },
27482     
27483     update : function()
27484     {
27485 //        var e = this.el.dom.firstChild;
27486 //        
27487 //        if(this.closable){
27488 //            e = e.nextSibling;
27489 //        }
27490 //        
27491 //        e.data = this.html || '';
27492
27493         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27494     }
27495    
27496 });
27497
27498  
27499
27500      /*
27501  * - LGPL
27502  *
27503  * Graph
27504  * 
27505  */
27506
27507
27508 /**
27509  * @class Roo.bootstrap.Graph
27510  * @extends Roo.bootstrap.Component
27511  * Bootstrap Graph class
27512 > Prameters
27513  -sm {number} sm 4
27514  -md {number} md 5
27515  @cfg {String} graphtype  bar | vbar | pie
27516  @cfg {number} g_x coodinator | centre x (pie)
27517  @cfg {number} g_y coodinator | centre y (pie)
27518  @cfg {number} g_r radius (pie)
27519  @cfg {number} g_height height of the chart (respected by all elements in the set)
27520  @cfg {number} g_width width of the chart (respected by all elements in the set)
27521  @cfg {Object} title The title of the chart
27522     
27523  -{Array}  values
27524  -opts (object) options for the chart 
27525      o {
27526      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27527      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27528      o vgutter (number)
27529      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.
27530      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27531      o to
27532      o stretch (boolean)
27533      o }
27534  -opts (object) options for the pie
27535      o{
27536      o cut
27537      o startAngle (number)
27538      o endAngle (number)
27539      } 
27540  *
27541  * @constructor
27542  * Create a new Input
27543  * @param {Object} config The config object
27544  */
27545
27546 Roo.bootstrap.Graph = function(config){
27547     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27548     
27549     this.addEvents({
27550         // img events
27551         /**
27552          * @event click
27553          * The img click event for the img.
27554          * @param {Roo.EventObject} e
27555          */
27556         "click" : true
27557     });
27558 };
27559
27560 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27561     
27562     sm: 4,
27563     md: 5,
27564     graphtype: 'bar',
27565     g_height: 250,
27566     g_width: 400,
27567     g_x: 50,
27568     g_y: 50,
27569     g_r: 30,
27570     opts:{
27571         //g_colors: this.colors,
27572         g_type: 'soft',
27573         g_gutter: '20%'
27574
27575     },
27576     title : false,
27577
27578     getAutoCreate : function(){
27579         
27580         var cfg = {
27581             tag: 'div',
27582             html : null
27583         };
27584         
27585         
27586         return  cfg;
27587     },
27588
27589     onRender : function(ct,position){
27590         
27591         
27592         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27593         
27594         if (typeof(Raphael) == 'undefined') {
27595             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27596             return;
27597         }
27598         
27599         this.raphael = Raphael(this.el.dom);
27600         
27601                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27602                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27603                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27604                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27605                 /*
27606                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27607                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27608                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27609                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27610                 
27611                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27612                 r.barchart(330, 10, 300, 220, data1);
27613                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27614                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27615                 */
27616                 
27617                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27618                 // r.barchart(30, 30, 560, 250,  xdata, {
27619                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27620                 //     axis : "0 0 1 1",
27621                 //     axisxlabels :  xdata
27622                 //     //yvalues : cols,
27623                    
27624                 // });
27625 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27626 //        
27627 //        this.load(null,xdata,{
27628 //                axis : "0 0 1 1",
27629 //                axisxlabels :  xdata
27630 //                });
27631
27632     },
27633
27634     load : function(graphtype,xdata,opts)
27635     {
27636         this.raphael.clear();
27637         if(!graphtype) {
27638             graphtype = this.graphtype;
27639         }
27640         if(!opts){
27641             opts = this.opts;
27642         }
27643         var r = this.raphael,
27644             fin = function () {
27645                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27646             },
27647             fout = function () {
27648                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27649             },
27650             pfin = function() {
27651                 this.sector.stop();
27652                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27653
27654                 if (this.label) {
27655                     this.label[0].stop();
27656                     this.label[0].attr({ r: 7.5 });
27657                     this.label[1].attr({ "font-weight": 800 });
27658                 }
27659             },
27660             pfout = function() {
27661                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27662
27663                 if (this.label) {
27664                     this.label[0].animate({ r: 5 }, 500, "bounce");
27665                     this.label[1].attr({ "font-weight": 400 });
27666                 }
27667             };
27668
27669         switch(graphtype){
27670             case 'bar':
27671                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27672                 break;
27673             case 'hbar':
27674                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27675                 break;
27676             case 'pie':
27677 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27678 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27679 //            
27680                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27681                 
27682                 break;
27683
27684         }
27685         
27686         if(this.title){
27687             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27688         }
27689         
27690     },
27691     
27692     setTitle: function(o)
27693     {
27694         this.title = o;
27695     },
27696     
27697     initEvents: function() {
27698         
27699         if(!this.href){
27700             this.el.on('click', this.onClick, this);
27701         }
27702     },
27703     
27704     onClick : function(e)
27705     {
27706         Roo.log('img onclick');
27707         this.fireEvent('click', this, e);
27708     }
27709    
27710 });
27711
27712  
27713 /*
27714  * - LGPL
27715  *
27716  * numberBox
27717  * 
27718  */
27719 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27720
27721 /**
27722  * @class Roo.bootstrap.dash.NumberBox
27723  * @extends Roo.bootstrap.Component
27724  * Bootstrap NumberBox class
27725  * @cfg {String} headline Box headline
27726  * @cfg {String} content Box content
27727  * @cfg {String} icon Box icon
27728  * @cfg {String} footer Footer text
27729  * @cfg {String} fhref Footer href
27730  * 
27731  * @constructor
27732  * Create a new NumberBox
27733  * @param {Object} config The config object
27734  */
27735
27736
27737 Roo.bootstrap.dash.NumberBox = function(config){
27738     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27739     
27740 };
27741
27742 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27743     
27744     headline : '',
27745     content : '',
27746     icon : '',
27747     footer : '',
27748     fhref : '',
27749     ficon : '',
27750     
27751     getAutoCreate : function(){
27752         
27753         var cfg = {
27754             tag : 'div',
27755             cls : 'small-box ',
27756             cn : [
27757                 {
27758                     tag : 'div',
27759                     cls : 'inner',
27760                     cn :[
27761                         {
27762                             tag : 'h3',
27763                             cls : 'roo-headline',
27764                             html : this.headline
27765                         },
27766                         {
27767                             tag : 'p',
27768                             cls : 'roo-content',
27769                             html : this.content
27770                         }
27771                     ]
27772                 }
27773             ]
27774         };
27775         
27776         if(this.icon){
27777             cfg.cn.push({
27778                 tag : 'div',
27779                 cls : 'icon',
27780                 cn :[
27781                     {
27782                         tag : 'i',
27783                         cls : 'ion ' + this.icon
27784                     }
27785                 ]
27786             });
27787         }
27788         
27789         if(this.footer){
27790             var footer = {
27791                 tag : 'a',
27792                 cls : 'small-box-footer',
27793                 href : this.fhref || '#',
27794                 html : this.footer
27795             };
27796             
27797             cfg.cn.push(footer);
27798             
27799         }
27800         
27801         return  cfg;
27802     },
27803
27804     onRender : function(ct,position){
27805         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27806
27807
27808        
27809                 
27810     },
27811
27812     setHeadline: function (value)
27813     {
27814         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27815     },
27816     
27817     setFooter: function (value, href)
27818     {
27819         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27820         
27821         if(href){
27822             this.el.select('a.small-box-footer',true).first().attr('href', href);
27823         }
27824         
27825     },
27826
27827     setContent: function (value)
27828     {
27829         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27830     },
27831
27832     initEvents: function() 
27833     {   
27834         
27835     }
27836     
27837 });
27838
27839  
27840 /*
27841  * - LGPL
27842  *
27843  * TabBox
27844  * 
27845  */
27846 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27847
27848 /**
27849  * @class Roo.bootstrap.dash.TabBox
27850  * @extends Roo.bootstrap.Component
27851  * Bootstrap TabBox class
27852  * @cfg {String} title Title of the TabBox
27853  * @cfg {String} icon Icon of the TabBox
27854  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27855  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27856  * 
27857  * @constructor
27858  * Create a new TabBox
27859  * @param {Object} config The config object
27860  */
27861
27862
27863 Roo.bootstrap.dash.TabBox = function(config){
27864     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27865     this.addEvents({
27866         // raw events
27867         /**
27868          * @event addpane
27869          * When a pane is added
27870          * @param {Roo.bootstrap.dash.TabPane} pane
27871          */
27872         "addpane" : true,
27873         /**
27874          * @event activatepane
27875          * When a pane is activated
27876          * @param {Roo.bootstrap.dash.TabPane} pane
27877          */
27878         "activatepane" : true
27879         
27880          
27881     });
27882     
27883     this.panes = [];
27884 };
27885
27886 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27887
27888     title : '',
27889     icon : false,
27890     showtabs : true,
27891     tabScrollable : false,
27892     
27893     getChildContainer : function()
27894     {
27895         return this.el.select('.tab-content', true).first();
27896     },
27897     
27898     getAutoCreate : function(){
27899         
27900         var header = {
27901             tag: 'li',
27902             cls: 'pull-left header',
27903             html: this.title,
27904             cn : []
27905         };
27906         
27907         if(this.icon){
27908             header.cn.push({
27909                 tag: 'i',
27910                 cls: 'fa ' + this.icon
27911             });
27912         }
27913         
27914         var h = {
27915             tag: 'ul',
27916             cls: 'nav nav-tabs pull-right',
27917             cn: [
27918                 header
27919             ]
27920         };
27921         
27922         if(this.tabScrollable){
27923             h = {
27924                 tag: 'div',
27925                 cls: 'tab-header',
27926                 cn: [
27927                     {
27928                         tag: 'ul',
27929                         cls: 'nav nav-tabs pull-right',
27930                         cn: [
27931                             header
27932                         ]
27933                     }
27934                 ]
27935             };
27936         }
27937         
27938         var cfg = {
27939             tag: 'div',
27940             cls: 'nav-tabs-custom',
27941             cn: [
27942                 h,
27943                 {
27944                     tag: 'div',
27945                     cls: 'tab-content no-padding',
27946                     cn: []
27947                 }
27948             ]
27949         };
27950
27951         return  cfg;
27952     },
27953     initEvents : function()
27954     {
27955         //Roo.log('add add pane handler');
27956         this.on('addpane', this.onAddPane, this);
27957     },
27958      /**
27959      * Updates the box title
27960      * @param {String} html to set the title to.
27961      */
27962     setTitle : function(value)
27963     {
27964         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27965     },
27966     onAddPane : function(pane)
27967     {
27968         this.panes.push(pane);
27969         //Roo.log('addpane');
27970         //Roo.log(pane);
27971         // tabs are rendere left to right..
27972         if(!this.showtabs){
27973             return;
27974         }
27975         
27976         var ctr = this.el.select('.nav-tabs', true).first();
27977          
27978          
27979         var existing = ctr.select('.nav-tab',true);
27980         var qty = existing.getCount();;
27981         
27982         
27983         var tab = ctr.createChild({
27984             tag : 'li',
27985             cls : 'nav-tab' + (qty ? '' : ' active'),
27986             cn : [
27987                 {
27988                     tag : 'a',
27989                     href:'#',
27990                     html : pane.title
27991                 }
27992             ]
27993         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27994         pane.tab = tab;
27995         
27996         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27997         if (!qty) {
27998             pane.el.addClass('active');
27999         }
28000         
28001                 
28002     },
28003     onTabClick : function(ev,un,ob,pane)
28004     {
28005         //Roo.log('tab - prev default');
28006         ev.preventDefault();
28007         
28008         
28009         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28010         pane.tab.addClass('active');
28011         //Roo.log(pane.title);
28012         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28013         // technically we should have a deactivate event.. but maybe add later.
28014         // and it should not de-activate the selected tab...
28015         this.fireEvent('activatepane', pane);
28016         pane.el.addClass('active');
28017         pane.fireEvent('activate');
28018         
28019         
28020     },
28021     
28022     getActivePane : function()
28023     {
28024         var r = false;
28025         Roo.each(this.panes, function(p) {
28026             if(p.el.hasClass('active')){
28027                 r = p;
28028                 return false;
28029             }
28030             
28031             return;
28032         });
28033         
28034         return r;
28035     }
28036     
28037     
28038 });
28039
28040  
28041 /*
28042  * - LGPL
28043  *
28044  * Tab pane
28045  * 
28046  */
28047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28048 /**
28049  * @class Roo.bootstrap.TabPane
28050  * @extends Roo.bootstrap.Component
28051  * Bootstrap TabPane class
28052  * @cfg {Boolean} active (false | true) Default false
28053  * @cfg {String} title title of panel
28054
28055  * 
28056  * @constructor
28057  * Create a new TabPane
28058  * @param {Object} config The config object
28059  */
28060
28061 Roo.bootstrap.dash.TabPane = function(config){
28062     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28063     
28064     this.addEvents({
28065         // raw events
28066         /**
28067          * @event activate
28068          * When a pane is activated
28069          * @param {Roo.bootstrap.dash.TabPane} pane
28070          */
28071         "activate" : true
28072          
28073     });
28074 };
28075
28076 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28077     
28078     active : false,
28079     title : '',
28080     
28081     // the tabBox that this is attached to.
28082     tab : false,
28083      
28084     getAutoCreate : function() 
28085     {
28086         var cfg = {
28087             tag: 'div',
28088             cls: 'tab-pane'
28089         };
28090         
28091         if(this.active){
28092             cfg.cls += ' active';
28093         }
28094         
28095         return cfg;
28096     },
28097     initEvents  : function()
28098     {
28099         //Roo.log('trigger add pane handler');
28100         this.parent().fireEvent('addpane', this)
28101     },
28102     
28103      /**
28104      * Updates the tab title 
28105      * @param {String} html to set the title to.
28106      */
28107     setTitle: function(str)
28108     {
28109         if (!this.tab) {
28110             return;
28111         }
28112         this.title = str;
28113         this.tab.select('a', true).first().dom.innerHTML = str;
28114         
28115     }
28116     
28117     
28118     
28119 });
28120
28121  
28122
28123
28124  /*
28125  * - LGPL
28126  *
28127  * menu
28128  * 
28129  */
28130 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28131
28132 /**
28133  * @class Roo.bootstrap.menu.Menu
28134  * @extends Roo.bootstrap.Component
28135  * Bootstrap Menu class - container for Menu
28136  * @cfg {String} html Text of the menu
28137  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28138  * @cfg {String} icon Font awesome icon
28139  * @cfg {String} pos Menu align to (top | bottom) default bottom
28140  * 
28141  * 
28142  * @constructor
28143  * Create a new Menu
28144  * @param {Object} config The config object
28145  */
28146
28147
28148 Roo.bootstrap.menu.Menu = function(config){
28149     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28150     
28151     this.addEvents({
28152         /**
28153          * @event beforeshow
28154          * Fires before this menu is displayed
28155          * @param {Roo.bootstrap.menu.Menu} this
28156          */
28157         beforeshow : true,
28158         /**
28159          * @event beforehide
28160          * Fires before this menu is hidden
28161          * @param {Roo.bootstrap.menu.Menu} this
28162          */
28163         beforehide : true,
28164         /**
28165          * @event show
28166          * Fires after this menu is displayed
28167          * @param {Roo.bootstrap.menu.Menu} this
28168          */
28169         show : true,
28170         /**
28171          * @event hide
28172          * Fires after this menu is hidden
28173          * @param {Roo.bootstrap.menu.Menu} this
28174          */
28175         hide : true,
28176         /**
28177          * @event click
28178          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28179          * @param {Roo.bootstrap.menu.Menu} this
28180          * @param {Roo.EventObject} e
28181          */
28182         click : true
28183     });
28184     
28185 };
28186
28187 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28188     
28189     submenu : false,
28190     html : '',
28191     weight : 'default',
28192     icon : false,
28193     pos : 'bottom',
28194     
28195     
28196     getChildContainer : function() {
28197         if(this.isSubMenu){
28198             return this.el;
28199         }
28200         
28201         return this.el.select('ul.dropdown-menu', true).first();  
28202     },
28203     
28204     getAutoCreate : function()
28205     {
28206         var text = [
28207             {
28208                 tag : 'span',
28209                 cls : 'roo-menu-text',
28210                 html : this.html
28211             }
28212         ];
28213         
28214         if(this.icon){
28215             text.unshift({
28216                 tag : 'i',
28217                 cls : 'fa ' + this.icon
28218             })
28219         }
28220         
28221         
28222         var cfg = {
28223             tag : 'div',
28224             cls : 'btn-group',
28225             cn : [
28226                 {
28227                     tag : 'button',
28228                     cls : 'dropdown-button btn btn-' + this.weight,
28229                     cn : text
28230                 },
28231                 {
28232                     tag : 'button',
28233                     cls : 'dropdown-toggle btn btn-' + this.weight,
28234                     cn : [
28235                         {
28236                             tag : 'span',
28237                             cls : 'caret'
28238                         }
28239                     ]
28240                 },
28241                 {
28242                     tag : 'ul',
28243                     cls : 'dropdown-menu'
28244                 }
28245             ]
28246             
28247         };
28248         
28249         if(this.pos == 'top'){
28250             cfg.cls += ' dropup';
28251         }
28252         
28253         if(this.isSubMenu){
28254             cfg = {
28255                 tag : 'ul',
28256                 cls : 'dropdown-menu'
28257             }
28258         }
28259         
28260         return cfg;
28261     },
28262     
28263     onRender : function(ct, position)
28264     {
28265         this.isSubMenu = ct.hasClass('dropdown-submenu');
28266         
28267         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28268     },
28269     
28270     initEvents : function() 
28271     {
28272         if(this.isSubMenu){
28273             return;
28274         }
28275         
28276         this.hidden = true;
28277         
28278         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28279         this.triggerEl.on('click', this.onTriggerPress, this);
28280         
28281         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28282         this.buttonEl.on('click', this.onClick, this);
28283         
28284     },
28285     
28286     list : function()
28287     {
28288         if(this.isSubMenu){
28289             return this.el;
28290         }
28291         
28292         return this.el.select('ul.dropdown-menu', true).first();
28293     },
28294     
28295     onClick : function(e)
28296     {
28297         this.fireEvent("click", this, e);
28298     },
28299     
28300     onTriggerPress  : function(e)
28301     {   
28302         if (this.isVisible()) {
28303             this.hide();
28304         } else {
28305             this.show();
28306         }
28307     },
28308     
28309     isVisible : function(){
28310         return !this.hidden;
28311     },
28312     
28313     show : function()
28314     {
28315         this.fireEvent("beforeshow", this);
28316         
28317         this.hidden = false;
28318         this.el.addClass('open');
28319         
28320         Roo.get(document).on("mouseup", this.onMouseUp, this);
28321         
28322         this.fireEvent("show", this);
28323         
28324         
28325     },
28326     
28327     hide : function()
28328     {
28329         this.fireEvent("beforehide", this);
28330         
28331         this.hidden = true;
28332         this.el.removeClass('open');
28333         
28334         Roo.get(document).un("mouseup", this.onMouseUp);
28335         
28336         this.fireEvent("hide", this);
28337     },
28338     
28339     onMouseUp : function()
28340     {
28341         this.hide();
28342     }
28343     
28344 });
28345
28346  
28347  /*
28348  * - LGPL
28349  *
28350  * menu item
28351  * 
28352  */
28353 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28354
28355 /**
28356  * @class Roo.bootstrap.menu.Item
28357  * @extends Roo.bootstrap.Component
28358  * Bootstrap MenuItem class
28359  * @cfg {Boolean} submenu (true | false) default false
28360  * @cfg {String} html text of the item
28361  * @cfg {String} href the link
28362  * @cfg {Boolean} disable (true | false) default false
28363  * @cfg {Boolean} preventDefault (true | false) default true
28364  * @cfg {String} icon Font awesome icon
28365  * @cfg {String} pos Submenu align to (left | right) default right 
28366  * 
28367  * 
28368  * @constructor
28369  * Create a new Item
28370  * @param {Object} config The config object
28371  */
28372
28373
28374 Roo.bootstrap.menu.Item = function(config){
28375     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28376     this.addEvents({
28377         /**
28378          * @event mouseover
28379          * Fires when the mouse is hovering over this menu
28380          * @param {Roo.bootstrap.menu.Item} this
28381          * @param {Roo.EventObject} e
28382          */
28383         mouseover : true,
28384         /**
28385          * @event mouseout
28386          * Fires when the mouse exits this menu
28387          * @param {Roo.bootstrap.menu.Item} this
28388          * @param {Roo.EventObject} e
28389          */
28390         mouseout : true,
28391         // raw events
28392         /**
28393          * @event click
28394          * The raw click event for the entire grid.
28395          * @param {Roo.EventObject} e
28396          */
28397         click : true
28398     });
28399 };
28400
28401 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28402     
28403     submenu : false,
28404     href : '',
28405     html : '',
28406     preventDefault: true,
28407     disable : false,
28408     icon : false,
28409     pos : 'right',
28410     
28411     getAutoCreate : function()
28412     {
28413         var text = [
28414             {
28415                 tag : 'span',
28416                 cls : 'roo-menu-item-text',
28417                 html : this.html
28418             }
28419         ];
28420         
28421         if(this.icon){
28422             text.unshift({
28423                 tag : 'i',
28424                 cls : 'fa ' + this.icon
28425             })
28426         }
28427         
28428         var cfg = {
28429             tag : 'li',
28430             cn : [
28431                 {
28432                     tag : 'a',
28433                     href : this.href || '#',
28434                     cn : text
28435                 }
28436             ]
28437         };
28438         
28439         if(this.disable){
28440             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28441         }
28442         
28443         if(this.submenu){
28444             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28445             
28446             if(this.pos == 'left'){
28447                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28448             }
28449         }
28450         
28451         return cfg;
28452     },
28453     
28454     initEvents : function() 
28455     {
28456         this.el.on('mouseover', this.onMouseOver, this);
28457         this.el.on('mouseout', this.onMouseOut, this);
28458         
28459         this.el.select('a', true).first().on('click', this.onClick, this);
28460         
28461     },
28462     
28463     onClick : function(e)
28464     {
28465         if(this.preventDefault){
28466             e.preventDefault();
28467         }
28468         
28469         this.fireEvent("click", this, e);
28470     },
28471     
28472     onMouseOver : function(e)
28473     {
28474         if(this.submenu && this.pos == 'left'){
28475             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28476         }
28477         
28478         this.fireEvent("mouseover", this, e);
28479     },
28480     
28481     onMouseOut : function(e)
28482     {
28483         this.fireEvent("mouseout", this, e);
28484     }
28485 });
28486
28487  
28488
28489  /*
28490  * - LGPL
28491  *
28492  * menu separator
28493  * 
28494  */
28495 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28496
28497 /**
28498  * @class Roo.bootstrap.menu.Separator
28499  * @extends Roo.bootstrap.Component
28500  * Bootstrap Separator class
28501  * 
28502  * @constructor
28503  * Create a new Separator
28504  * @param {Object} config The config object
28505  */
28506
28507
28508 Roo.bootstrap.menu.Separator = function(config){
28509     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28510 };
28511
28512 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28513     
28514     getAutoCreate : function(){
28515         var cfg = {
28516             tag : 'li',
28517             cls: 'divider'
28518         };
28519         
28520         return cfg;
28521     }
28522    
28523 });
28524
28525  
28526
28527  /*
28528  * - LGPL
28529  *
28530  * Tooltip
28531  * 
28532  */
28533
28534 /**
28535  * @class Roo.bootstrap.Tooltip
28536  * Bootstrap Tooltip class
28537  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28538  * to determine which dom element triggers the tooltip.
28539  * 
28540  * It needs to add support for additional attributes like tooltip-position
28541  * 
28542  * @constructor
28543  * Create a new Toolti
28544  * @param {Object} config The config object
28545  */
28546
28547 Roo.bootstrap.Tooltip = function(config){
28548     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28549     
28550     this.alignment = Roo.bootstrap.Tooltip.alignment;
28551     
28552     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28553         this.alignment = config.alignment;
28554     }
28555     
28556 };
28557
28558 Roo.apply(Roo.bootstrap.Tooltip, {
28559     /**
28560      * @function init initialize tooltip monitoring.
28561      * @static
28562      */
28563     currentEl : false,
28564     currentTip : false,
28565     currentRegion : false,
28566     
28567     //  init : delay?
28568     
28569     init : function()
28570     {
28571         Roo.get(document).on('mouseover', this.enter ,this);
28572         Roo.get(document).on('mouseout', this.leave, this);
28573          
28574         
28575         this.currentTip = new Roo.bootstrap.Tooltip();
28576     },
28577     
28578     enter : function(ev)
28579     {
28580         var dom = ev.getTarget();
28581         
28582         //Roo.log(['enter',dom]);
28583         var el = Roo.fly(dom);
28584         if (this.currentEl) {
28585             //Roo.log(dom);
28586             //Roo.log(this.currentEl);
28587             //Roo.log(this.currentEl.contains(dom));
28588             if (this.currentEl == el) {
28589                 return;
28590             }
28591             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28592                 return;
28593             }
28594
28595         }
28596         
28597         if (this.currentTip.el) {
28598             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28599         }    
28600         //Roo.log(ev);
28601         
28602         if(!el || el.dom == document){
28603             return;
28604         }
28605         
28606         var bindEl = el;
28607         
28608         // you can not look for children, as if el is the body.. then everythign is the child..
28609         if (!el.attr('tooltip')) { //
28610             if (!el.select("[tooltip]").elements.length) {
28611                 return;
28612             }
28613             // is the mouse over this child...?
28614             bindEl = el.select("[tooltip]").first();
28615             var xy = ev.getXY();
28616             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28617                 //Roo.log("not in region.");
28618                 return;
28619             }
28620             //Roo.log("child element over..");
28621             
28622         }
28623         this.currentEl = bindEl;
28624         this.currentTip.bind(bindEl);
28625         this.currentRegion = Roo.lib.Region.getRegion(dom);
28626         this.currentTip.enter();
28627         
28628     },
28629     leave : function(ev)
28630     {
28631         var dom = ev.getTarget();
28632         //Roo.log(['leave',dom]);
28633         if (!this.currentEl) {
28634             return;
28635         }
28636         
28637         
28638         if (dom != this.currentEl.dom) {
28639             return;
28640         }
28641         var xy = ev.getXY();
28642         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28643             return;
28644         }
28645         // only activate leave if mouse cursor is outside... bounding box..
28646         
28647         
28648         
28649         
28650         if (this.currentTip) {
28651             this.currentTip.leave();
28652         }
28653         //Roo.log('clear currentEl');
28654         this.currentEl = false;
28655         
28656         
28657     },
28658     alignment : {
28659         'left' : ['r-l', [-2,0], 'right'],
28660         'right' : ['l-r', [2,0], 'left'],
28661         'bottom' : ['t-b', [0,2], 'top'],
28662         'top' : [ 'b-t', [0,-2], 'bottom']
28663     }
28664     
28665 });
28666
28667
28668 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28669     
28670     
28671     bindEl : false,
28672     
28673     delay : null, // can be { show : 300 , hide: 500}
28674     
28675     timeout : null,
28676     
28677     hoverState : null, //???
28678     
28679     placement : 'bottom', 
28680     
28681     alignment : false,
28682     
28683     getAutoCreate : function(){
28684     
28685         var cfg = {
28686            cls : 'tooltip',   
28687            role : 'tooltip',
28688            cn : [
28689                 {
28690                     cls : 'tooltip-arrow arrow'
28691                 },
28692                 {
28693                     cls : 'tooltip-inner'
28694                 }
28695            ]
28696         };
28697         
28698         return cfg;
28699     },
28700     bind : function(el)
28701     {
28702         this.bindEl = el;
28703     },
28704     
28705     initEvents : function()
28706     {
28707         this.arrowEl = this.el.select('.arrow', true).first();
28708         this.innerEl = this.el.select('.tooltip-inner', true).first();
28709     },
28710     
28711     enter : function () {
28712        
28713         if (this.timeout != null) {
28714             clearTimeout(this.timeout);
28715         }
28716         
28717         this.hoverState = 'in';
28718          //Roo.log("enter - show");
28719         if (!this.delay || !this.delay.show) {
28720             this.show();
28721             return;
28722         }
28723         var _t = this;
28724         this.timeout = setTimeout(function () {
28725             if (_t.hoverState == 'in') {
28726                 _t.show();
28727             }
28728         }, this.delay.show);
28729     },
28730     leave : function()
28731     {
28732         clearTimeout(this.timeout);
28733     
28734         this.hoverState = 'out';
28735          if (!this.delay || !this.delay.hide) {
28736             this.hide();
28737             return;
28738         }
28739        
28740         var _t = this;
28741         this.timeout = setTimeout(function () {
28742             //Roo.log("leave - timeout");
28743             
28744             if (_t.hoverState == 'out') {
28745                 _t.hide();
28746                 Roo.bootstrap.Tooltip.currentEl = false;
28747             }
28748         }, delay);
28749     },
28750     
28751     show : function (msg)
28752     {
28753         if (!this.el) {
28754             this.render(document.body);
28755         }
28756         // set content.
28757         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28758         
28759         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28760         
28761         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28762         
28763         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28764                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28765         
28766         var placement = typeof this.placement == 'function' ?
28767             this.placement.call(this, this.el, on_el) :
28768             this.placement;
28769             
28770         var autoToken = /\s?auto?\s?/i;
28771         var autoPlace = autoToken.test(placement);
28772         if (autoPlace) {
28773             placement = placement.replace(autoToken, '') || 'top';
28774         }
28775         
28776         //this.el.detach()
28777         //this.el.setXY([0,0]);
28778         this.el.show();
28779         //this.el.dom.style.display='block';
28780         
28781         //this.el.appendTo(on_el);
28782         
28783         var p = this.getPosition();
28784         var box = this.el.getBox();
28785         
28786         if (autoPlace) {
28787             // fixme..
28788         }
28789         
28790         var align = this.alignment[placement];
28791         
28792         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28793         
28794         if(placement == 'top' || placement == 'bottom'){
28795             if(xy[0] < 0){
28796                 placement = 'right';
28797             }
28798             
28799             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28800                 placement = 'left';
28801             }
28802             
28803             var scroll = Roo.select('body', true).first().getScroll();
28804             
28805             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28806                 placement = 'top';
28807             }
28808             
28809             align = this.alignment[placement];
28810             
28811             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28812             
28813         }
28814         
28815         this.el.alignTo(this.bindEl, align[0],align[1]);
28816         //var arrow = this.el.select('.arrow',true).first();
28817         //arrow.set(align[2], 
28818         
28819         this.el.addClass(placement);
28820         this.el.addClass("bs-tooltip-"+ placement);
28821         
28822         this.el.addClass('in fade show');
28823         
28824         this.hoverState = null;
28825         
28826         if (this.el.hasClass('fade')) {
28827             // fade it?
28828         }
28829         
28830         
28831         
28832         
28833         
28834     },
28835     hide : function()
28836     {
28837          
28838         if (!this.el) {
28839             return;
28840         }
28841         //this.el.setXY([0,0]);
28842         this.el.removeClass(['show', 'in']);
28843         //this.el.hide();
28844         
28845     }
28846     
28847 });
28848  
28849
28850  /*
28851  * - LGPL
28852  *
28853  * Location Picker
28854  * 
28855  */
28856
28857 /**
28858  * @class Roo.bootstrap.LocationPicker
28859  * @extends Roo.bootstrap.Component
28860  * Bootstrap LocationPicker class
28861  * @cfg {Number} latitude Position when init default 0
28862  * @cfg {Number} longitude Position when init default 0
28863  * @cfg {Number} zoom default 15
28864  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28865  * @cfg {Boolean} mapTypeControl default false
28866  * @cfg {Boolean} disableDoubleClickZoom default false
28867  * @cfg {Boolean} scrollwheel default true
28868  * @cfg {Boolean} streetViewControl default false
28869  * @cfg {Number} radius default 0
28870  * @cfg {String} locationName
28871  * @cfg {Boolean} draggable default true
28872  * @cfg {Boolean} enableAutocomplete default false
28873  * @cfg {Boolean} enableReverseGeocode default true
28874  * @cfg {String} markerTitle
28875  * 
28876  * @constructor
28877  * Create a new LocationPicker
28878  * @param {Object} config The config object
28879  */
28880
28881
28882 Roo.bootstrap.LocationPicker = function(config){
28883     
28884     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28885     
28886     this.addEvents({
28887         /**
28888          * @event initial
28889          * Fires when the picker initialized.
28890          * @param {Roo.bootstrap.LocationPicker} this
28891          * @param {Google Location} location
28892          */
28893         initial : true,
28894         /**
28895          * @event positionchanged
28896          * Fires when the picker position changed.
28897          * @param {Roo.bootstrap.LocationPicker} this
28898          * @param {Google Location} location
28899          */
28900         positionchanged : true,
28901         /**
28902          * @event resize
28903          * Fires when the map resize.
28904          * @param {Roo.bootstrap.LocationPicker} this
28905          */
28906         resize : true,
28907         /**
28908          * @event show
28909          * Fires when the map show.
28910          * @param {Roo.bootstrap.LocationPicker} this
28911          */
28912         show : true,
28913         /**
28914          * @event hide
28915          * Fires when the map hide.
28916          * @param {Roo.bootstrap.LocationPicker} this
28917          */
28918         hide : true,
28919         /**
28920          * @event mapClick
28921          * Fires when click the map.
28922          * @param {Roo.bootstrap.LocationPicker} this
28923          * @param {Map event} e
28924          */
28925         mapClick : true,
28926         /**
28927          * @event mapRightClick
28928          * Fires when right click the map.
28929          * @param {Roo.bootstrap.LocationPicker} this
28930          * @param {Map event} e
28931          */
28932         mapRightClick : true,
28933         /**
28934          * @event markerClick
28935          * Fires when click the marker.
28936          * @param {Roo.bootstrap.LocationPicker} this
28937          * @param {Map event} e
28938          */
28939         markerClick : true,
28940         /**
28941          * @event markerRightClick
28942          * Fires when right click the marker.
28943          * @param {Roo.bootstrap.LocationPicker} this
28944          * @param {Map event} e
28945          */
28946         markerRightClick : true,
28947         /**
28948          * @event OverlayViewDraw
28949          * Fires when OverlayView Draw
28950          * @param {Roo.bootstrap.LocationPicker} this
28951          */
28952         OverlayViewDraw : true,
28953         /**
28954          * @event OverlayViewOnAdd
28955          * Fires when OverlayView Draw
28956          * @param {Roo.bootstrap.LocationPicker} this
28957          */
28958         OverlayViewOnAdd : true,
28959         /**
28960          * @event OverlayViewOnRemove
28961          * Fires when OverlayView Draw
28962          * @param {Roo.bootstrap.LocationPicker} this
28963          */
28964         OverlayViewOnRemove : true,
28965         /**
28966          * @event OverlayViewShow
28967          * Fires when OverlayView Draw
28968          * @param {Roo.bootstrap.LocationPicker} this
28969          * @param {Pixel} cpx
28970          */
28971         OverlayViewShow : true,
28972         /**
28973          * @event OverlayViewHide
28974          * Fires when OverlayView Draw
28975          * @param {Roo.bootstrap.LocationPicker} this
28976          */
28977         OverlayViewHide : true,
28978         /**
28979          * @event loadexception
28980          * Fires when load google lib failed.
28981          * @param {Roo.bootstrap.LocationPicker} this
28982          */
28983         loadexception : true
28984     });
28985         
28986 };
28987
28988 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28989     
28990     gMapContext: false,
28991     
28992     latitude: 0,
28993     longitude: 0,
28994     zoom: 15,
28995     mapTypeId: false,
28996     mapTypeControl: false,
28997     disableDoubleClickZoom: false,
28998     scrollwheel: true,
28999     streetViewControl: false,
29000     radius: 0,
29001     locationName: '',
29002     draggable: true,
29003     enableAutocomplete: false,
29004     enableReverseGeocode: true,
29005     markerTitle: '',
29006     
29007     getAutoCreate: function()
29008     {
29009
29010         var cfg = {
29011             tag: 'div',
29012             cls: 'roo-location-picker'
29013         };
29014         
29015         return cfg
29016     },
29017     
29018     initEvents: function(ct, position)
29019     {       
29020         if(!this.el.getWidth() || this.isApplied()){
29021             return;
29022         }
29023         
29024         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29025         
29026         this.initial();
29027     },
29028     
29029     initial: function()
29030     {
29031         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29032             this.fireEvent('loadexception', this);
29033             return;
29034         }
29035         
29036         if(!this.mapTypeId){
29037             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29038         }
29039         
29040         this.gMapContext = this.GMapContext();
29041         
29042         this.initOverlayView();
29043         
29044         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29045         
29046         var _this = this;
29047                 
29048         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29049             _this.setPosition(_this.gMapContext.marker.position);
29050         });
29051         
29052         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29053             _this.fireEvent('mapClick', this, event);
29054             
29055         });
29056
29057         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29058             _this.fireEvent('mapRightClick', this, event);
29059             
29060         });
29061         
29062         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29063             _this.fireEvent('markerClick', this, event);
29064             
29065         });
29066
29067         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29068             _this.fireEvent('markerRightClick', this, event);
29069             
29070         });
29071         
29072         this.setPosition(this.gMapContext.location);
29073         
29074         this.fireEvent('initial', this, this.gMapContext.location);
29075     },
29076     
29077     initOverlayView: function()
29078     {
29079         var _this = this;
29080         
29081         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29082             
29083             draw: function()
29084             {
29085                 _this.fireEvent('OverlayViewDraw', _this);
29086             },
29087             
29088             onAdd: function()
29089             {
29090                 _this.fireEvent('OverlayViewOnAdd', _this);
29091             },
29092             
29093             onRemove: function()
29094             {
29095                 _this.fireEvent('OverlayViewOnRemove', _this);
29096             },
29097             
29098             show: function(cpx)
29099             {
29100                 _this.fireEvent('OverlayViewShow', _this, cpx);
29101             },
29102             
29103             hide: function()
29104             {
29105                 _this.fireEvent('OverlayViewHide', _this);
29106             }
29107             
29108         });
29109     },
29110     
29111     fromLatLngToContainerPixel: function(event)
29112     {
29113         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29114     },
29115     
29116     isApplied: function() 
29117     {
29118         return this.getGmapContext() == false ? false : true;
29119     },
29120     
29121     getGmapContext: function() 
29122     {
29123         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29124     },
29125     
29126     GMapContext: function() 
29127     {
29128         var position = new google.maps.LatLng(this.latitude, this.longitude);
29129         
29130         var _map = new google.maps.Map(this.el.dom, {
29131             center: position,
29132             zoom: this.zoom,
29133             mapTypeId: this.mapTypeId,
29134             mapTypeControl: this.mapTypeControl,
29135             disableDoubleClickZoom: this.disableDoubleClickZoom,
29136             scrollwheel: this.scrollwheel,
29137             streetViewControl: this.streetViewControl,
29138             locationName: this.locationName,
29139             draggable: this.draggable,
29140             enableAutocomplete: this.enableAutocomplete,
29141             enableReverseGeocode: this.enableReverseGeocode
29142         });
29143         
29144         var _marker = new google.maps.Marker({
29145             position: position,
29146             map: _map,
29147             title: this.markerTitle,
29148             draggable: this.draggable
29149         });
29150         
29151         return {
29152             map: _map,
29153             marker: _marker,
29154             circle: null,
29155             location: position,
29156             radius: this.radius,
29157             locationName: this.locationName,
29158             addressComponents: {
29159                 formatted_address: null,
29160                 addressLine1: null,
29161                 addressLine2: null,
29162                 streetName: null,
29163                 streetNumber: null,
29164                 city: null,
29165                 district: null,
29166                 state: null,
29167                 stateOrProvince: null
29168             },
29169             settings: this,
29170             domContainer: this.el.dom,
29171             geodecoder: new google.maps.Geocoder()
29172         };
29173     },
29174     
29175     drawCircle: function(center, radius, options) 
29176     {
29177         if (this.gMapContext.circle != null) {
29178             this.gMapContext.circle.setMap(null);
29179         }
29180         if (radius > 0) {
29181             radius *= 1;
29182             options = Roo.apply({}, options, {
29183                 strokeColor: "#0000FF",
29184                 strokeOpacity: .35,
29185                 strokeWeight: 2,
29186                 fillColor: "#0000FF",
29187                 fillOpacity: .2
29188             });
29189             
29190             options.map = this.gMapContext.map;
29191             options.radius = radius;
29192             options.center = center;
29193             this.gMapContext.circle = new google.maps.Circle(options);
29194             return this.gMapContext.circle;
29195         }
29196         
29197         return null;
29198     },
29199     
29200     setPosition: function(location) 
29201     {
29202         this.gMapContext.location = location;
29203         this.gMapContext.marker.setPosition(location);
29204         this.gMapContext.map.panTo(location);
29205         this.drawCircle(location, this.gMapContext.radius, {});
29206         
29207         var _this = this;
29208         
29209         if (this.gMapContext.settings.enableReverseGeocode) {
29210             this.gMapContext.geodecoder.geocode({
29211                 latLng: this.gMapContext.location
29212             }, function(results, status) {
29213                 
29214                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29215                     _this.gMapContext.locationName = results[0].formatted_address;
29216                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29217                     
29218                     _this.fireEvent('positionchanged', this, location);
29219                 }
29220             });
29221             
29222             return;
29223         }
29224         
29225         this.fireEvent('positionchanged', this, location);
29226     },
29227     
29228     resize: function()
29229     {
29230         google.maps.event.trigger(this.gMapContext.map, "resize");
29231         
29232         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29233         
29234         this.fireEvent('resize', this);
29235     },
29236     
29237     setPositionByLatLng: function(latitude, longitude)
29238     {
29239         this.setPosition(new google.maps.LatLng(latitude, longitude));
29240     },
29241     
29242     getCurrentPosition: function() 
29243     {
29244         return {
29245             latitude: this.gMapContext.location.lat(),
29246             longitude: this.gMapContext.location.lng()
29247         };
29248     },
29249     
29250     getAddressName: function() 
29251     {
29252         return this.gMapContext.locationName;
29253     },
29254     
29255     getAddressComponents: function() 
29256     {
29257         return this.gMapContext.addressComponents;
29258     },
29259     
29260     address_component_from_google_geocode: function(address_components) 
29261     {
29262         var result = {};
29263         
29264         for (var i = 0; i < address_components.length; i++) {
29265             var component = address_components[i];
29266             if (component.types.indexOf("postal_code") >= 0) {
29267                 result.postalCode = component.short_name;
29268             } else if (component.types.indexOf("street_number") >= 0) {
29269                 result.streetNumber = component.short_name;
29270             } else if (component.types.indexOf("route") >= 0) {
29271                 result.streetName = component.short_name;
29272             } else if (component.types.indexOf("neighborhood") >= 0) {
29273                 result.city = component.short_name;
29274             } else if (component.types.indexOf("locality") >= 0) {
29275                 result.city = component.short_name;
29276             } else if (component.types.indexOf("sublocality") >= 0) {
29277                 result.district = component.short_name;
29278             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29279                 result.stateOrProvince = component.short_name;
29280             } else if (component.types.indexOf("country") >= 0) {
29281                 result.country = component.short_name;
29282             }
29283         }
29284         
29285         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29286         result.addressLine2 = "";
29287         return result;
29288     },
29289     
29290     setZoomLevel: function(zoom)
29291     {
29292         this.gMapContext.map.setZoom(zoom);
29293     },
29294     
29295     show: function()
29296     {
29297         if(!this.el){
29298             return;
29299         }
29300         
29301         this.el.show();
29302         
29303         this.resize();
29304         
29305         this.fireEvent('show', this);
29306     },
29307     
29308     hide: function()
29309     {
29310         if(!this.el){
29311             return;
29312         }
29313         
29314         this.el.hide();
29315         
29316         this.fireEvent('hide', this);
29317     }
29318     
29319 });
29320
29321 Roo.apply(Roo.bootstrap.LocationPicker, {
29322     
29323     OverlayView : function(map, options)
29324     {
29325         options = options || {};
29326         
29327         this.setMap(map);
29328     }
29329     
29330     
29331 });/**
29332  * @class Roo.bootstrap.Alert
29333  * @extends Roo.bootstrap.Component
29334  * Bootstrap Alert class - shows an alert area box
29335  * eg
29336  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29337   Enter a valid email address
29338 </div>
29339  * @licence LGPL
29340  * @cfg {String} title The title of alert
29341  * @cfg {String} html The content of alert
29342  * @cfg {String} weight (  success | info | warning | danger )
29343  * @cfg {String} faicon font-awesomeicon
29344  * 
29345  * @constructor
29346  * Create a new alert
29347  * @param {Object} config The config object
29348  */
29349
29350
29351 Roo.bootstrap.Alert = function(config){
29352     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29353     
29354 };
29355
29356 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29357     
29358     title: '',
29359     html: '',
29360     weight: false,
29361     faicon: false,
29362     
29363     getAutoCreate : function()
29364     {
29365         
29366         var cfg = {
29367             tag : 'div',
29368             cls : 'alert',
29369             cn : [
29370                 {
29371                     tag : 'i',
29372                     cls : 'roo-alert-icon'
29373                     
29374                 },
29375                 {
29376                     tag : 'b',
29377                     cls : 'roo-alert-title',
29378                     html : this.title
29379                 },
29380                 {
29381                     tag : 'span',
29382                     cls : 'roo-alert-text',
29383                     html : this.html
29384                 }
29385             ]
29386         };
29387         
29388         if(this.faicon){
29389             cfg.cn[0].cls += ' fa ' + this.faicon;
29390         }
29391         
29392         if(this.weight){
29393             cfg.cls += ' alert-' + this.weight;
29394         }
29395         
29396         return cfg;
29397     },
29398     
29399     initEvents: function() 
29400     {
29401         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29402     },
29403     
29404     setTitle : function(str)
29405     {
29406         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29407     },
29408     
29409     setText : function(str)
29410     {
29411         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29412     },
29413     
29414     setWeight : function(weight)
29415     {
29416         if(this.weight){
29417             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29418         }
29419         
29420         this.weight = weight;
29421         
29422         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29423     },
29424     
29425     setIcon : function(icon)
29426     {
29427         if(this.faicon){
29428             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29429         }
29430         
29431         this.faicon = icon;
29432         
29433         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29434     },
29435     
29436     hide: function() 
29437     {
29438         this.el.hide();   
29439     },
29440     
29441     show: function() 
29442     {  
29443         this.el.show();   
29444     }
29445     
29446 });
29447
29448  
29449 /*
29450 * Licence: LGPL
29451 */
29452
29453 /**
29454  * @class Roo.bootstrap.UploadCropbox
29455  * @extends Roo.bootstrap.Component
29456  * Bootstrap UploadCropbox class
29457  * @cfg {String} emptyText show when image has been loaded
29458  * @cfg {String} rotateNotify show when image too small to rotate
29459  * @cfg {Number} errorTimeout default 3000
29460  * @cfg {Number} minWidth default 300
29461  * @cfg {Number} minHeight default 300
29462  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29463  * @cfg {Boolean} isDocument (true|false) default false
29464  * @cfg {String} url action url
29465  * @cfg {String} paramName default 'imageUpload'
29466  * @cfg {String} method default POST
29467  * @cfg {Boolean} loadMask (true|false) default true
29468  * @cfg {Boolean} loadingText default 'Loading...'
29469  * 
29470  * @constructor
29471  * Create a new UploadCropbox
29472  * @param {Object} config The config object
29473  */
29474
29475 Roo.bootstrap.UploadCropbox = function(config){
29476     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29477     
29478     this.addEvents({
29479         /**
29480          * @event beforeselectfile
29481          * Fire before select file
29482          * @param {Roo.bootstrap.UploadCropbox} this
29483          */
29484         "beforeselectfile" : true,
29485         /**
29486          * @event initial
29487          * Fire after initEvent
29488          * @param {Roo.bootstrap.UploadCropbox} this
29489          */
29490         "initial" : true,
29491         /**
29492          * @event crop
29493          * Fire after initEvent
29494          * @param {Roo.bootstrap.UploadCropbox} this
29495          * @param {String} data
29496          */
29497         "crop" : true,
29498         /**
29499          * @event prepare
29500          * Fire when preparing the file data
29501          * @param {Roo.bootstrap.UploadCropbox} this
29502          * @param {Object} file
29503          */
29504         "prepare" : true,
29505         /**
29506          * @event exception
29507          * Fire when get exception
29508          * @param {Roo.bootstrap.UploadCropbox} this
29509          * @param {XMLHttpRequest} xhr
29510          */
29511         "exception" : true,
29512         /**
29513          * @event beforeloadcanvas
29514          * Fire before load the canvas
29515          * @param {Roo.bootstrap.UploadCropbox} this
29516          * @param {String} src
29517          */
29518         "beforeloadcanvas" : true,
29519         /**
29520          * @event trash
29521          * Fire when trash image
29522          * @param {Roo.bootstrap.UploadCropbox} this
29523          */
29524         "trash" : true,
29525         /**
29526          * @event download
29527          * Fire when download the image
29528          * @param {Roo.bootstrap.UploadCropbox} this
29529          */
29530         "download" : true,
29531         /**
29532          * @event footerbuttonclick
29533          * Fire when footerbuttonclick
29534          * @param {Roo.bootstrap.UploadCropbox} this
29535          * @param {String} type
29536          */
29537         "footerbuttonclick" : true,
29538         /**
29539          * @event resize
29540          * Fire when resize
29541          * @param {Roo.bootstrap.UploadCropbox} this
29542          */
29543         "resize" : true,
29544         /**
29545          * @event rotate
29546          * Fire when rotate the image
29547          * @param {Roo.bootstrap.UploadCropbox} this
29548          * @param {String} pos
29549          */
29550         "rotate" : true,
29551         /**
29552          * @event inspect
29553          * Fire when inspect the file
29554          * @param {Roo.bootstrap.UploadCropbox} this
29555          * @param {Object} file
29556          */
29557         "inspect" : true,
29558         /**
29559          * @event upload
29560          * Fire when xhr upload the file
29561          * @param {Roo.bootstrap.UploadCropbox} this
29562          * @param {Object} data
29563          */
29564         "upload" : true,
29565         /**
29566          * @event arrange
29567          * Fire when arrange the file data
29568          * @param {Roo.bootstrap.UploadCropbox} this
29569          * @param {Object} formData
29570          */
29571         "arrange" : true
29572     });
29573     
29574     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29575 };
29576
29577 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29578     
29579     emptyText : 'Click to upload image',
29580     rotateNotify : 'Image is too small to rotate',
29581     errorTimeout : 3000,
29582     scale : 0,
29583     baseScale : 1,
29584     rotate : 0,
29585     dragable : false,
29586     pinching : false,
29587     mouseX : 0,
29588     mouseY : 0,
29589     cropData : false,
29590     minWidth : 300,
29591     minHeight : 300,
29592     file : false,
29593     exif : {},
29594     baseRotate : 1,
29595     cropType : 'image/jpeg',
29596     buttons : false,
29597     canvasLoaded : false,
29598     isDocument : false,
29599     method : 'POST',
29600     paramName : 'imageUpload',
29601     loadMask : true,
29602     loadingText : 'Loading...',
29603     maskEl : false,
29604     
29605     getAutoCreate : function()
29606     {
29607         var cfg = {
29608             tag : 'div',
29609             cls : 'roo-upload-cropbox',
29610             cn : [
29611                 {
29612                     tag : 'input',
29613                     cls : 'roo-upload-cropbox-selector',
29614                     type : 'file'
29615                 },
29616                 {
29617                     tag : 'div',
29618                     cls : 'roo-upload-cropbox-body',
29619                     style : 'cursor:pointer',
29620                     cn : [
29621                         {
29622                             tag : 'div',
29623                             cls : 'roo-upload-cropbox-preview'
29624                         },
29625                         {
29626                             tag : 'div',
29627                             cls : 'roo-upload-cropbox-thumb'
29628                         },
29629                         {
29630                             tag : 'div',
29631                             cls : 'roo-upload-cropbox-empty-notify',
29632                             html : this.emptyText
29633                         },
29634                         {
29635                             tag : 'div',
29636                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29637                             html : this.rotateNotify
29638                         }
29639                     ]
29640                 },
29641                 {
29642                     tag : 'div',
29643                     cls : 'roo-upload-cropbox-footer',
29644                     cn : {
29645                         tag : 'div',
29646                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29647                         cn : []
29648                     }
29649                 }
29650             ]
29651         };
29652         
29653         return cfg;
29654     },
29655     
29656     onRender : function(ct, position)
29657     {
29658         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29659         
29660         if (this.buttons.length) {
29661             
29662             Roo.each(this.buttons, function(bb) {
29663                 
29664                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29665                 
29666                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29667                 
29668             }, this);
29669         }
29670         
29671         if(this.loadMask){
29672             this.maskEl = this.el;
29673         }
29674     },
29675     
29676     initEvents : function()
29677     {
29678         this.urlAPI = (window.createObjectURL && window) || 
29679                                 (window.URL && URL.revokeObjectURL && URL) || 
29680                                 (window.webkitURL && webkitURL);
29681                         
29682         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29683         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29684         
29685         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29686         this.selectorEl.hide();
29687         
29688         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29689         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29690         
29691         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29692         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29693         this.thumbEl.hide();
29694         
29695         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29696         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29697         
29698         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29699         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29700         this.errorEl.hide();
29701         
29702         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29703         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29704         this.footerEl.hide();
29705         
29706         this.setThumbBoxSize();
29707         
29708         this.bind();
29709         
29710         this.resize();
29711         
29712         this.fireEvent('initial', this);
29713     },
29714
29715     bind : function()
29716     {
29717         var _this = this;
29718         
29719         window.addEventListener("resize", function() { _this.resize(); } );
29720         
29721         this.bodyEl.on('click', this.beforeSelectFile, this);
29722         
29723         if(Roo.isTouch){
29724             this.bodyEl.on('touchstart', this.onTouchStart, this);
29725             this.bodyEl.on('touchmove', this.onTouchMove, this);
29726             this.bodyEl.on('touchend', this.onTouchEnd, this);
29727         }
29728         
29729         if(!Roo.isTouch){
29730             this.bodyEl.on('mousedown', this.onMouseDown, this);
29731             this.bodyEl.on('mousemove', this.onMouseMove, this);
29732             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29733             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29734             Roo.get(document).on('mouseup', this.onMouseUp, this);
29735         }
29736         
29737         this.selectorEl.on('change', this.onFileSelected, this);
29738     },
29739     
29740     reset : function()
29741     {    
29742         this.scale = 0;
29743         this.baseScale = 1;
29744         this.rotate = 0;
29745         this.baseRotate = 1;
29746         this.dragable = false;
29747         this.pinching = false;
29748         this.mouseX = 0;
29749         this.mouseY = 0;
29750         this.cropData = false;
29751         this.notifyEl.dom.innerHTML = this.emptyText;
29752         
29753         this.selectorEl.dom.value = '';
29754         
29755     },
29756     
29757     resize : function()
29758     {
29759         if(this.fireEvent('resize', this) != false){
29760             this.setThumbBoxPosition();
29761             this.setCanvasPosition();
29762         }
29763     },
29764     
29765     onFooterButtonClick : function(e, el, o, type)
29766     {
29767         switch (type) {
29768             case 'rotate-left' :
29769                 this.onRotateLeft(e);
29770                 break;
29771             case 'rotate-right' :
29772                 this.onRotateRight(e);
29773                 break;
29774             case 'picture' :
29775                 this.beforeSelectFile(e);
29776                 break;
29777             case 'trash' :
29778                 this.trash(e);
29779                 break;
29780             case 'crop' :
29781                 this.crop(e);
29782                 break;
29783             case 'download' :
29784                 this.download(e);
29785                 break;
29786             default :
29787                 break;
29788         }
29789         
29790         this.fireEvent('footerbuttonclick', this, type);
29791     },
29792     
29793     beforeSelectFile : function(e)
29794     {
29795         e.preventDefault();
29796         
29797         if(this.fireEvent('beforeselectfile', this) != false){
29798             this.selectorEl.dom.click();
29799         }
29800     },
29801     
29802     onFileSelected : function(e)
29803     {
29804         e.preventDefault();
29805         
29806         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29807             return;
29808         }
29809         
29810         var file = this.selectorEl.dom.files[0];
29811         
29812         if(this.fireEvent('inspect', this, file) != false){
29813             this.prepare(file);
29814         }
29815         
29816     },
29817     
29818     trash : function(e)
29819     {
29820         this.fireEvent('trash', this);
29821     },
29822     
29823     download : function(e)
29824     {
29825         this.fireEvent('download', this);
29826     },
29827     
29828     loadCanvas : function(src)
29829     {   
29830         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29831             
29832             this.reset();
29833             
29834             this.imageEl = document.createElement('img');
29835             
29836             var _this = this;
29837             
29838             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29839             
29840             this.imageEl.src = src;
29841         }
29842     },
29843     
29844     onLoadCanvas : function()
29845     {   
29846         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29847         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29848         
29849         this.bodyEl.un('click', this.beforeSelectFile, this);
29850         
29851         this.notifyEl.hide();
29852         this.thumbEl.show();
29853         this.footerEl.show();
29854         
29855         this.baseRotateLevel();
29856         
29857         if(this.isDocument){
29858             this.setThumbBoxSize();
29859         }
29860         
29861         this.setThumbBoxPosition();
29862         
29863         this.baseScaleLevel();
29864         
29865         this.draw();
29866         
29867         this.resize();
29868         
29869         this.canvasLoaded = true;
29870         
29871         if(this.loadMask){
29872             this.maskEl.unmask();
29873         }
29874         
29875     },
29876     
29877     setCanvasPosition : function()
29878     {   
29879         if(!this.canvasEl){
29880             return;
29881         }
29882         
29883         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29884         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29885         
29886         this.previewEl.setLeft(pw);
29887         this.previewEl.setTop(ph);
29888         
29889     },
29890     
29891     onMouseDown : function(e)
29892     {   
29893         e.stopEvent();
29894         
29895         this.dragable = true;
29896         this.pinching = false;
29897         
29898         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29899             this.dragable = false;
29900             return;
29901         }
29902         
29903         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29904         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29905         
29906     },
29907     
29908     onMouseMove : function(e)
29909     {   
29910         e.stopEvent();
29911         
29912         if(!this.canvasLoaded){
29913             return;
29914         }
29915         
29916         if (!this.dragable){
29917             return;
29918         }
29919         
29920         var minX = Math.ceil(this.thumbEl.getLeft(true));
29921         var minY = Math.ceil(this.thumbEl.getTop(true));
29922         
29923         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29924         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29925         
29926         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29927         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29928         
29929         x = x - this.mouseX;
29930         y = y - this.mouseY;
29931         
29932         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29933         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29934         
29935         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29936         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29937         
29938         this.previewEl.setLeft(bgX);
29939         this.previewEl.setTop(bgY);
29940         
29941         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29942         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29943     },
29944     
29945     onMouseUp : function(e)
29946     {   
29947         e.stopEvent();
29948         
29949         this.dragable = false;
29950     },
29951     
29952     onMouseWheel : function(e)
29953     {   
29954         e.stopEvent();
29955         
29956         this.startScale = this.scale;
29957         
29958         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29959         
29960         if(!this.zoomable()){
29961             this.scale = this.startScale;
29962             return;
29963         }
29964         
29965         this.draw();
29966         
29967         return;
29968     },
29969     
29970     zoomable : function()
29971     {
29972         var minScale = this.thumbEl.getWidth() / this.minWidth;
29973         
29974         if(this.minWidth < this.minHeight){
29975             minScale = this.thumbEl.getHeight() / this.minHeight;
29976         }
29977         
29978         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29979         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29980         
29981         if(
29982                 this.isDocument &&
29983                 (this.rotate == 0 || this.rotate == 180) && 
29984                 (
29985                     width > this.imageEl.OriginWidth || 
29986                     height > this.imageEl.OriginHeight ||
29987                     (width < this.minWidth && height < this.minHeight)
29988                 )
29989         ){
29990             return false;
29991         }
29992         
29993         if(
29994                 this.isDocument &&
29995                 (this.rotate == 90 || this.rotate == 270) && 
29996                 (
29997                     width > this.imageEl.OriginWidth || 
29998                     height > this.imageEl.OriginHeight ||
29999                     (width < this.minHeight && height < this.minWidth)
30000                 )
30001         ){
30002             return false;
30003         }
30004         
30005         if(
30006                 !this.isDocument &&
30007                 (this.rotate == 0 || this.rotate == 180) && 
30008                 (
30009                     width < this.minWidth || 
30010                     width > this.imageEl.OriginWidth || 
30011                     height < this.minHeight || 
30012                     height > this.imageEl.OriginHeight
30013                 )
30014         ){
30015             return false;
30016         }
30017         
30018         if(
30019                 !this.isDocument &&
30020                 (this.rotate == 90 || this.rotate == 270) && 
30021                 (
30022                     width < this.minHeight || 
30023                     width > this.imageEl.OriginWidth || 
30024                     height < this.minWidth || 
30025                     height > this.imageEl.OriginHeight
30026                 )
30027         ){
30028             return false;
30029         }
30030         
30031         return true;
30032         
30033     },
30034     
30035     onRotateLeft : function(e)
30036     {   
30037         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30038             
30039             var minScale = this.thumbEl.getWidth() / this.minWidth;
30040             
30041             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30042             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30043             
30044             this.startScale = this.scale;
30045             
30046             while (this.getScaleLevel() < minScale){
30047             
30048                 this.scale = this.scale + 1;
30049                 
30050                 if(!this.zoomable()){
30051                     break;
30052                 }
30053                 
30054                 if(
30055                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30056                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30057                 ){
30058                     continue;
30059                 }
30060                 
30061                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30062
30063                 this.draw();
30064                 
30065                 return;
30066             }
30067             
30068             this.scale = this.startScale;
30069             
30070             this.onRotateFail();
30071             
30072             return false;
30073         }
30074         
30075         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30076
30077         if(this.isDocument){
30078             this.setThumbBoxSize();
30079             this.setThumbBoxPosition();
30080             this.setCanvasPosition();
30081         }
30082         
30083         this.draw();
30084         
30085         this.fireEvent('rotate', this, 'left');
30086         
30087     },
30088     
30089     onRotateRight : function(e)
30090     {
30091         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30092             
30093             var minScale = this.thumbEl.getWidth() / this.minWidth;
30094         
30095             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30096             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30097             
30098             this.startScale = this.scale;
30099             
30100             while (this.getScaleLevel() < minScale){
30101             
30102                 this.scale = this.scale + 1;
30103                 
30104                 if(!this.zoomable()){
30105                     break;
30106                 }
30107                 
30108                 if(
30109                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30110                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30111                 ){
30112                     continue;
30113                 }
30114                 
30115                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30116
30117                 this.draw();
30118                 
30119                 return;
30120             }
30121             
30122             this.scale = this.startScale;
30123             
30124             this.onRotateFail();
30125             
30126             return false;
30127         }
30128         
30129         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30130
30131         if(this.isDocument){
30132             this.setThumbBoxSize();
30133             this.setThumbBoxPosition();
30134             this.setCanvasPosition();
30135         }
30136         
30137         this.draw();
30138         
30139         this.fireEvent('rotate', this, 'right');
30140     },
30141     
30142     onRotateFail : function()
30143     {
30144         this.errorEl.show(true);
30145         
30146         var _this = this;
30147         
30148         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30149     },
30150     
30151     draw : function()
30152     {
30153         this.previewEl.dom.innerHTML = '';
30154         
30155         var canvasEl = document.createElement("canvas");
30156         
30157         var contextEl = canvasEl.getContext("2d");
30158         
30159         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30160         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30161         var center = this.imageEl.OriginWidth / 2;
30162         
30163         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30164             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30165             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30166             center = this.imageEl.OriginHeight / 2;
30167         }
30168         
30169         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30170         
30171         contextEl.translate(center, center);
30172         contextEl.rotate(this.rotate * Math.PI / 180);
30173
30174         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30175         
30176         this.canvasEl = document.createElement("canvas");
30177         
30178         this.contextEl = this.canvasEl.getContext("2d");
30179         
30180         switch (this.rotate) {
30181             case 0 :
30182                 
30183                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30184                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30185                 
30186                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30187                 
30188                 break;
30189             case 90 : 
30190                 
30191                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30192                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30193                 
30194                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30195                     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);
30196                     break;
30197                 }
30198                 
30199                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30200                 
30201                 break;
30202             case 180 :
30203                 
30204                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30205                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30206                 
30207                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30208                     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);
30209                     break;
30210                 }
30211                 
30212                 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);
30213                 
30214                 break;
30215             case 270 :
30216                 
30217                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30218                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30219         
30220                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30221                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30222                     break;
30223                 }
30224                 
30225                 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);
30226                 
30227                 break;
30228             default : 
30229                 break;
30230         }
30231         
30232         this.previewEl.appendChild(this.canvasEl);
30233         
30234         this.setCanvasPosition();
30235     },
30236     
30237     crop : function()
30238     {
30239         if(!this.canvasLoaded){
30240             return;
30241         }
30242         
30243         var imageCanvas = document.createElement("canvas");
30244         
30245         var imageContext = imageCanvas.getContext("2d");
30246         
30247         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30248         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30249         
30250         var center = imageCanvas.width / 2;
30251         
30252         imageContext.translate(center, center);
30253         
30254         imageContext.rotate(this.rotate * Math.PI / 180);
30255         
30256         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30257         
30258         var canvas = document.createElement("canvas");
30259         
30260         var context = canvas.getContext("2d");
30261                 
30262         canvas.width = this.minWidth;
30263         canvas.height = this.minHeight;
30264
30265         switch (this.rotate) {
30266             case 0 :
30267                 
30268                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30269                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30270                 
30271                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30272                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30273                 
30274                 var targetWidth = this.minWidth - 2 * x;
30275                 var targetHeight = this.minHeight - 2 * y;
30276                 
30277                 var scale = 1;
30278                 
30279                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30280                     scale = targetWidth / width;
30281                 }
30282                 
30283                 if(x > 0 && y == 0){
30284                     scale = targetHeight / height;
30285                 }
30286                 
30287                 if(x > 0 && y > 0){
30288                     scale = targetWidth / width;
30289                     
30290                     if(width < height){
30291                         scale = targetHeight / height;
30292                     }
30293                 }
30294                 
30295                 context.scale(scale, scale);
30296                 
30297                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30298                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30299
30300                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30301                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30302
30303                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30304                 
30305                 break;
30306             case 90 : 
30307                 
30308                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30309                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30310                 
30311                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30312                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30313                 
30314                 var targetWidth = this.minWidth - 2 * x;
30315                 var targetHeight = this.minHeight - 2 * y;
30316                 
30317                 var scale = 1;
30318                 
30319                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30320                     scale = targetWidth / width;
30321                 }
30322                 
30323                 if(x > 0 && y == 0){
30324                     scale = targetHeight / height;
30325                 }
30326                 
30327                 if(x > 0 && y > 0){
30328                     scale = targetWidth / width;
30329                     
30330                     if(width < height){
30331                         scale = targetHeight / height;
30332                     }
30333                 }
30334                 
30335                 context.scale(scale, scale);
30336                 
30337                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30338                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30339
30340                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30341                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30342                 
30343                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30344                 
30345                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30346                 
30347                 break;
30348             case 180 :
30349                 
30350                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30351                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30352                 
30353                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30354                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30355                 
30356                 var targetWidth = this.minWidth - 2 * x;
30357                 var targetHeight = this.minHeight - 2 * y;
30358                 
30359                 var scale = 1;
30360                 
30361                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30362                     scale = targetWidth / width;
30363                 }
30364                 
30365                 if(x > 0 && y == 0){
30366                     scale = targetHeight / height;
30367                 }
30368                 
30369                 if(x > 0 && y > 0){
30370                     scale = targetWidth / width;
30371                     
30372                     if(width < height){
30373                         scale = targetHeight / height;
30374                     }
30375                 }
30376                 
30377                 context.scale(scale, scale);
30378                 
30379                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30380                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30381
30382                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30383                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30384
30385                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30386                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30387                 
30388                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30389                 
30390                 break;
30391             case 270 :
30392                 
30393                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30394                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30395                 
30396                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30397                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30398                 
30399                 var targetWidth = this.minWidth - 2 * x;
30400                 var targetHeight = this.minHeight - 2 * y;
30401                 
30402                 var scale = 1;
30403                 
30404                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30405                     scale = targetWidth / width;
30406                 }
30407                 
30408                 if(x > 0 && y == 0){
30409                     scale = targetHeight / height;
30410                 }
30411                 
30412                 if(x > 0 && y > 0){
30413                     scale = targetWidth / width;
30414                     
30415                     if(width < height){
30416                         scale = targetHeight / height;
30417                     }
30418                 }
30419                 
30420                 context.scale(scale, scale);
30421                 
30422                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30423                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30424
30425                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30426                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30427                 
30428                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30429                 
30430                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30431                 
30432                 break;
30433             default : 
30434                 break;
30435         }
30436         
30437         this.cropData = canvas.toDataURL(this.cropType);
30438         
30439         if(this.fireEvent('crop', this, this.cropData) !== false){
30440             this.process(this.file, this.cropData);
30441         }
30442         
30443         return;
30444         
30445     },
30446     
30447     setThumbBoxSize : function()
30448     {
30449         var width, height;
30450         
30451         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30452             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30453             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30454             
30455             this.minWidth = width;
30456             this.minHeight = height;
30457             
30458             if(this.rotate == 90 || this.rotate == 270){
30459                 this.minWidth = height;
30460                 this.minHeight = width;
30461             }
30462         }
30463         
30464         height = 300;
30465         width = Math.ceil(this.minWidth * height / this.minHeight);
30466         
30467         if(this.minWidth > this.minHeight){
30468             width = 300;
30469             height = Math.ceil(this.minHeight * width / this.minWidth);
30470         }
30471         
30472         this.thumbEl.setStyle({
30473             width : width + 'px',
30474             height : height + 'px'
30475         });
30476
30477         return;
30478             
30479     },
30480     
30481     setThumbBoxPosition : function()
30482     {
30483         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30484         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30485         
30486         this.thumbEl.setLeft(x);
30487         this.thumbEl.setTop(y);
30488         
30489     },
30490     
30491     baseRotateLevel : function()
30492     {
30493         this.baseRotate = 1;
30494         
30495         if(
30496                 typeof(this.exif) != 'undefined' &&
30497                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30498                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30499         ){
30500             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30501         }
30502         
30503         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30504         
30505     },
30506     
30507     baseScaleLevel : function()
30508     {
30509         var width, height;
30510         
30511         if(this.isDocument){
30512             
30513             if(this.baseRotate == 6 || this.baseRotate == 8){
30514             
30515                 height = this.thumbEl.getHeight();
30516                 this.baseScale = height / this.imageEl.OriginWidth;
30517
30518                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30519                     width = this.thumbEl.getWidth();
30520                     this.baseScale = width / this.imageEl.OriginHeight;
30521                 }
30522
30523                 return;
30524             }
30525
30526             height = this.thumbEl.getHeight();
30527             this.baseScale = height / this.imageEl.OriginHeight;
30528
30529             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30530                 width = this.thumbEl.getWidth();
30531                 this.baseScale = width / this.imageEl.OriginWidth;
30532             }
30533
30534             return;
30535         }
30536         
30537         if(this.baseRotate == 6 || this.baseRotate == 8){
30538             
30539             width = this.thumbEl.getHeight();
30540             this.baseScale = width / this.imageEl.OriginHeight;
30541             
30542             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30543                 height = this.thumbEl.getWidth();
30544                 this.baseScale = height / this.imageEl.OriginHeight;
30545             }
30546             
30547             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30548                 height = this.thumbEl.getWidth();
30549                 this.baseScale = height / this.imageEl.OriginHeight;
30550                 
30551                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30552                     width = this.thumbEl.getHeight();
30553                     this.baseScale = width / this.imageEl.OriginWidth;
30554                 }
30555             }
30556             
30557             return;
30558         }
30559         
30560         width = this.thumbEl.getWidth();
30561         this.baseScale = width / this.imageEl.OriginWidth;
30562         
30563         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30564             height = this.thumbEl.getHeight();
30565             this.baseScale = height / this.imageEl.OriginHeight;
30566         }
30567         
30568         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30569             
30570             height = this.thumbEl.getHeight();
30571             this.baseScale = height / this.imageEl.OriginHeight;
30572             
30573             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30574                 width = this.thumbEl.getWidth();
30575                 this.baseScale = width / this.imageEl.OriginWidth;
30576             }
30577             
30578         }
30579         
30580         return;
30581     },
30582     
30583     getScaleLevel : function()
30584     {
30585         return this.baseScale * Math.pow(1.1, this.scale);
30586     },
30587     
30588     onTouchStart : function(e)
30589     {
30590         if(!this.canvasLoaded){
30591             this.beforeSelectFile(e);
30592             return;
30593         }
30594         
30595         var touches = e.browserEvent.touches;
30596         
30597         if(!touches){
30598             return;
30599         }
30600         
30601         if(touches.length == 1){
30602             this.onMouseDown(e);
30603             return;
30604         }
30605         
30606         if(touches.length != 2){
30607             return;
30608         }
30609         
30610         var coords = [];
30611         
30612         for(var i = 0, finger; finger = touches[i]; i++){
30613             coords.push(finger.pageX, finger.pageY);
30614         }
30615         
30616         var x = Math.pow(coords[0] - coords[2], 2);
30617         var y = Math.pow(coords[1] - coords[3], 2);
30618         
30619         this.startDistance = Math.sqrt(x + y);
30620         
30621         this.startScale = this.scale;
30622         
30623         this.pinching = true;
30624         this.dragable = false;
30625         
30626     },
30627     
30628     onTouchMove : function(e)
30629     {
30630         if(!this.pinching && !this.dragable){
30631             return;
30632         }
30633         
30634         var touches = e.browserEvent.touches;
30635         
30636         if(!touches){
30637             return;
30638         }
30639         
30640         if(this.dragable){
30641             this.onMouseMove(e);
30642             return;
30643         }
30644         
30645         var coords = [];
30646         
30647         for(var i = 0, finger; finger = touches[i]; i++){
30648             coords.push(finger.pageX, finger.pageY);
30649         }
30650         
30651         var x = Math.pow(coords[0] - coords[2], 2);
30652         var y = Math.pow(coords[1] - coords[3], 2);
30653         
30654         this.endDistance = Math.sqrt(x + y);
30655         
30656         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30657         
30658         if(!this.zoomable()){
30659             this.scale = this.startScale;
30660             return;
30661         }
30662         
30663         this.draw();
30664         
30665     },
30666     
30667     onTouchEnd : function(e)
30668     {
30669         this.pinching = false;
30670         this.dragable = false;
30671         
30672     },
30673     
30674     process : function(file, crop)
30675     {
30676         if(this.loadMask){
30677             this.maskEl.mask(this.loadingText);
30678         }
30679         
30680         this.xhr = new XMLHttpRequest();
30681         
30682         file.xhr = this.xhr;
30683
30684         this.xhr.open(this.method, this.url, true);
30685         
30686         var headers = {
30687             "Accept": "application/json",
30688             "Cache-Control": "no-cache",
30689             "X-Requested-With": "XMLHttpRequest"
30690         };
30691         
30692         for (var headerName in headers) {
30693             var headerValue = headers[headerName];
30694             if (headerValue) {
30695                 this.xhr.setRequestHeader(headerName, headerValue);
30696             }
30697         }
30698         
30699         var _this = this;
30700         
30701         this.xhr.onload = function()
30702         {
30703             _this.xhrOnLoad(_this.xhr);
30704         }
30705         
30706         this.xhr.onerror = function()
30707         {
30708             _this.xhrOnError(_this.xhr);
30709         }
30710         
30711         var formData = new FormData();
30712
30713         formData.append('returnHTML', 'NO');
30714         
30715         if(crop){
30716             formData.append('crop', crop);
30717         }
30718         
30719         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30720             formData.append(this.paramName, file, file.name);
30721         }
30722         
30723         if(typeof(file.filename) != 'undefined'){
30724             formData.append('filename', file.filename);
30725         }
30726         
30727         if(typeof(file.mimetype) != 'undefined'){
30728             formData.append('mimetype', file.mimetype);
30729         }
30730         
30731         if(this.fireEvent('arrange', this, formData) != false){
30732             this.xhr.send(formData);
30733         };
30734     },
30735     
30736     xhrOnLoad : function(xhr)
30737     {
30738         if(this.loadMask){
30739             this.maskEl.unmask();
30740         }
30741         
30742         if (xhr.readyState !== 4) {
30743             this.fireEvent('exception', this, xhr);
30744             return;
30745         }
30746
30747         var response = Roo.decode(xhr.responseText);
30748         
30749         if(!response.success){
30750             this.fireEvent('exception', this, xhr);
30751             return;
30752         }
30753         
30754         var response = Roo.decode(xhr.responseText);
30755         
30756         this.fireEvent('upload', this, response);
30757         
30758     },
30759     
30760     xhrOnError : function()
30761     {
30762         if(this.loadMask){
30763             this.maskEl.unmask();
30764         }
30765         
30766         Roo.log('xhr on error');
30767         
30768         var response = Roo.decode(xhr.responseText);
30769           
30770         Roo.log(response);
30771         
30772     },
30773     
30774     prepare : function(file)
30775     {   
30776         if(this.loadMask){
30777             this.maskEl.mask(this.loadingText);
30778         }
30779         
30780         this.file = false;
30781         this.exif = {};
30782         
30783         if(typeof(file) === 'string'){
30784             this.loadCanvas(file);
30785             return;
30786         }
30787         
30788         if(!file || !this.urlAPI){
30789             return;
30790         }
30791         
30792         this.file = file;
30793         this.cropType = file.type;
30794         
30795         var _this = this;
30796         
30797         if(this.fireEvent('prepare', this, this.file) != false){
30798             
30799             var reader = new FileReader();
30800             
30801             reader.onload = function (e) {
30802                 if (e.target.error) {
30803                     Roo.log(e.target.error);
30804                     return;
30805                 }
30806                 
30807                 var buffer = e.target.result,
30808                     dataView = new DataView(buffer),
30809                     offset = 2,
30810                     maxOffset = dataView.byteLength - 4,
30811                     markerBytes,
30812                     markerLength;
30813                 
30814                 if (dataView.getUint16(0) === 0xffd8) {
30815                     while (offset < maxOffset) {
30816                         markerBytes = dataView.getUint16(offset);
30817                         
30818                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30819                             markerLength = dataView.getUint16(offset + 2) + 2;
30820                             if (offset + markerLength > dataView.byteLength) {
30821                                 Roo.log('Invalid meta data: Invalid segment size.');
30822                                 break;
30823                             }
30824                             
30825                             if(markerBytes == 0xffe1){
30826                                 _this.parseExifData(
30827                                     dataView,
30828                                     offset,
30829                                     markerLength
30830                                 );
30831                             }
30832                             
30833                             offset += markerLength;
30834                             
30835                             continue;
30836                         }
30837                         
30838                         break;
30839                     }
30840                     
30841                 }
30842                 
30843                 var url = _this.urlAPI.createObjectURL(_this.file);
30844                 
30845                 _this.loadCanvas(url);
30846                 
30847                 return;
30848             }
30849             
30850             reader.readAsArrayBuffer(this.file);
30851             
30852         }
30853         
30854     },
30855     
30856     parseExifData : function(dataView, offset, length)
30857     {
30858         var tiffOffset = offset + 10,
30859             littleEndian,
30860             dirOffset;
30861     
30862         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30863             // No Exif data, might be XMP data instead
30864             return;
30865         }
30866         
30867         // Check for the ASCII code for "Exif" (0x45786966):
30868         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30869             // No Exif data, might be XMP data instead
30870             return;
30871         }
30872         if (tiffOffset + 8 > dataView.byteLength) {
30873             Roo.log('Invalid Exif data: Invalid segment size.');
30874             return;
30875         }
30876         // Check for the two null bytes:
30877         if (dataView.getUint16(offset + 8) !== 0x0000) {
30878             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30879             return;
30880         }
30881         // Check the byte alignment:
30882         switch (dataView.getUint16(tiffOffset)) {
30883         case 0x4949:
30884             littleEndian = true;
30885             break;
30886         case 0x4D4D:
30887             littleEndian = false;
30888             break;
30889         default:
30890             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30891             return;
30892         }
30893         // Check for the TIFF tag marker (0x002A):
30894         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30895             Roo.log('Invalid Exif data: Missing TIFF marker.');
30896             return;
30897         }
30898         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30899         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30900         
30901         this.parseExifTags(
30902             dataView,
30903             tiffOffset,
30904             tiffOffset + dirOffset,
30905             littleEndian
30906         );
30907     },
30908     
30909     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30910     {
30911         var tagsNumber,
30912             dirEndOffset,
30913             i;
30914         if (dirOffset + 6 > dataView.byteLength) {
30915             Roo.log('Invalid Exif data: Invalid directory offset.');
30916             return;
30917         }
30918         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30919         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30920         if (dirEndOffset + 4 > dataView.byteLength) {
30921             Roo.log('Invalid Exif data: Invalid directory size.');
30922             return;
30923         }
30924         for (i = 0; i < tagsNumber; i += 1) {
30925             this.parseExifTag(
30926                 dataView,
30927                 tiffOffset,
30928                 dirOffset + 2 + 12 * i, // tag offset
30929                 littleEndian
30930             );
30931         }
30932         // Return the offset to the next directory:
30933         return dataView.getUint32(dirEndOffset, littleEndian);
30934     },
30935     
30936     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30937     {
30938         var tag = dataView.getUint16(offset, littleEndian);
30939         
30940         this.exif[tag] = this.getExifValue(
30941             dataView,
30942             tiffOffset,
30943             offset,
30944             dataView.getUint16(offset + 2, littleEndian), // tag type
30945             dataView.getUint32(offset + 4, littleEndian), // tag length
30946             littleEndian
30947         );
30948     },
30949     
30950     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30951     {
30952         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30953             tagSize,
30954             dataOffset,
30955             values,
30956             i,
30957             str,
30958             c;
30959     
30960         if (!tagType) {
30961             Roo.log('Invalid Exif data: Invalid tag type.');
30962             return;
30963         }
30964         
30965         tagSize = tagType.size * length;
30966         // Determine if the value is contained in the dataOffset bytes,
30967         // or if the value at the dataOffset is a pointer to the actual data:
30968         dataOffset = tagSize > 4 ?
30969                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30970         if (dataOffset + tagSize > dataView.byteLength) {
30971             Roo.log('Invalid Exif data: Invalid data offset.');
30972             return;
30973         }
30974         if (length === 1) {
30975             return tagType.getValue(dataView, dataOffset, littleEndian);
30976         }
30977         values = [];
30978         for (i = 0; i < length; i += 1) {
30979             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30980         }
30981         
30982         if (tagType.ascii) {
30983             str = '';
30984             // Concatenate the chars:
30985             for (i = 0; i < values.length; i += 1) {
30986                 c = values[i];
30987                 // Ignore the terminating NULL byte(s):
30988                 if (c === '\u0000') {
30989                     break;
30990                 }
30991                 str += c;
30992             }
30993             return str;
30994         }
30995         return values;
30996     }
30997     
30998 });
30999
31000 Roo.apply(Roo.bootstrap.UploadCropbox, {
31001     tags : {
31002         'Orientation': 0x0112
31003     },
31004     
31005     Orientation: {
31006             1: 0, //'top-left',
31007 //            2: 'top-right',
31008             3: 180, //'bottom-right',
31009 //            4: 'bottom-left',
31010 //            5: 'left-top',
31011             6: 90, //'right-top',
31012 //            7: 'right-bottom',
31013             8: 270 //'left-bottom'
31014     },
31015     
31016     exifTagTypes : {
31017         // byte, 8-bit unsigned int:
31018         1: {
31019             getValue: function (dataView, dataOffset) {
31020                 return dataView.getUint8(dataOffset);
31021             },
31022             size: 1
31023         },
31024         // ascii, 8-bit byte:
31025         2: {
31026             getValue: function (dataView, dataOffset) {
31027                 return String.fromCharCode(dataView.getUint8(dataOffset));
31028             },
31029             size: 1,
31030             ascii: true
31031         },
31032         // short, 16 bit int:
31033         3: {
31034             getValue: function (dataView, dataOffset, littleEndian) {
31035                 return dataView.getUint16(dataOffset, littleEndian);
31036             },
31037             size: 2
31038         },
31039         // long, 32 bit int:
31040         4: {
31041             getValue: function (dataView, dataOffset, littleEndian) {
31042                 return dataView.getUint32(dataOffset, littleEndian);
31043             },
31044             size: 4
31045         },
31046         // rational = two long values, first is numerator, second is denominator:
31047         5: {
31048             getValue: function (dataView, dataOffset, littleEndian) {
31049                 return dataView.getUint32(dataOffset, littleEndian) /
31050                     dataView.getUint32(dataOffset + 4, littleEndian);
31051             },
31052             size: 8
31053         },
31054         // slong, 32 bit signed int:
31055         9: {
31056             getValue: function (dataView, dataOffset, littleEndian) {
31057                 return dataView.getInt32(dataOffset, littleEndian);
31058             },
31059             size: 4
31060         },
31061         // srational, two slongs, first is numerator, second is denominator:
31062         10: {
31063             getValue: function (dataView, dataOffset, littleEndian) {
31064                 return dataView.getInt32(dataOffset, littleEndian) /
31065                     dataView.getInt32(dataOffset + 4, littleEndian);
31066             },
31067             size: 8
31068         }
31069     },
31070     
31071     footer : {
31072         STANDARD : [
31073             {
31074                 tag : 'div',
31075                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31076                 action : 'rotate-left',
31077                 cn : [
31078                     {
31079                         tag : 'button',
31080                         cls : 'btn btn-default',
31081                         html : '<i class="fa fa-undo"></i>'
31082                     }
31083                 ]
31084             },
31085             {
31086                 tag : 'div',
31087                 cls : 'btn-group roo-upload-cropbox-picture',
31088                 action : 'picture',
31089                 cn : [
31090                     {
31091                         tag : 'button',
31092                         cls : 'btn btn-default',
31093                         html : '<i class="fa fa-picture-o"></i>'
31094                     }
31095                 ]
31096             },
31097             {
31098                 tag : 'div',
31099                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31100                 action : 'rotate-right',
31101                 cn : [
31102                     {
31103                         tag : 'button',
31104                         cls : 'btn btn-default',
31105                         html : '<i class="fa fa-repeat"></i>'
31106                     }
31107                 ]
31108             }
31109         ],
31110         DOCUMENT : [
31111             {
31112                 tag : 'div',
31113                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31114                 action : 'rotate-left',
31115                 cn : [
31116                     {
31117                         tag : 'button',
31118                         cls : 'btn btn-default',
31119                         html : '<i class="fa fa-undo"></i>'
31120                     }
31121                 ]
31122             },
31123             {
31124                 tag : 'div',
31125                 cls : 'btn-group roo-upload-cropbox-download',
31126                 action : 'download',
31127                 cn : [
31128                     {
31129                         tag : 'button',
31130                         cls : 'btn btn-default',
31131                         html : '<i class="fa fa-download"></i>'
31132                     }
31133                 ]
31134             },
31135             {
31136                 tag : 'div',
31137                 cls : 'btn-group roo-upload-cropbox-crop',
31138                 action : 'crop',
31139                 cn : [
31140                     {
31141                         tag : 'button',
31142                         cls : 'btn btn-default',
31143                         html : '<i class="fa fa-crop"></i>'
31144                     }
31145                 ]
31146             },
31147             {
31148                 tag : 'div',
31149                 cls : 'btn-group roo-upload-cropbox-trash',
31150                 action : 'trash',
31151                 cn : [
31152                     {
31153                         tag : 'button',
31154                         cls : 'btn btn-default',
31155                         html : '<i class="fa fa-trash"></i>'
31156                     }
31157                 ]
31158             },
31159             {
31160                 tag : 'div',
31161                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31162                 action : 'rotate-right',
31163                 cn : [
31164                     {
31165                         tag : 'button',
31166                         cls : 'btn btn-default',
31167                         html : '<i class="fa fa-repeat"></i>'
31168                     }
31169                 ]
31170             }
31171         ],
31172         ROTATOR : [
31173             {
31174                 tag : 'div',
31175                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31176                 action : 'rotate-left',
31177                 cn : [
31178                     {
31179                         tag : 'button',
31180                         cls : 'btn btn-default',
31181                         html : '<i class="fa fa-undo"></i>'
31182                     }
31183                 ]
31184             },
31185             {
31186                 tag : 'div',
31187                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31188                 action : 'rotate-right',
31189                 cn : [
31190                     {
31191                         tag : 'button',
31192                         cls : 'btn btn-default',
31193                         html : '<i class="fa fa-repeat"></i>'
31194                     }
31195                 ]
31196             }
31197         ]
31198     }
31199 });
31200
31201 /*
31202 * Licence: LGPL
31203 */
31204
31205 /**
31206  * @class Roo.bootstrap.DocumentManager
31207  * @extends Roo.bootstrap.Component
31208  * Bootstrap DocumentManager class
31209  * @cfg {String} paramName default 'imageUpload'
31210  * @cfg {String} toolTipName default 'filename'
31211  * @cfg {String} method default POST
31212  * @cfg {String} url action url
31213  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31214  * @cfg {Boolean} multiple multiple upload default true
31215  * @cfg {Number} thumbSize default 300
31216  * @cfg {String} fieldLabel
31217  * @cfg {Number} labelWidth default 4
31218  * @cfg {String} labelAlign (left|top) default left
31219  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31220 * @cfg {Number} labellg set the width of label (1-12)
31221  * @cfg {Number} labelmd set the width of label (1-12)
31222  * @cfg {Number} labelsm set the width of label (1-12)
31223  * @cfg {Number} labelxs set the width of label (1-12)
31224  * 
31225  * @constructor
31226  * Create a new DocumentManager
31227  * @param {Object} config The config object
31228  */
31229
31230 Roo.bootstrap.DocumentManager = function(config){
31231     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31232     
31233     this.files = [];
31234     this.delegates = [];
31235     
31236     this.addEvents({
31237         /**
31238          * @event initial
31239          * Fire when initial the DocumentManager
31240          * @param {Roo.bootstrap.DocumentManager} this
31241          */
31242         "initial" : true,
31243         /**
31244          * @event inspect
31245          * inspect selected file
31246          * @param {Roo.bootstrap.DocumentManager} this
31247          * @param {File} file
31248          */
31249         "inspect" : true,
31250         /**
31251          * @event exception
31252          * Fire when xhr load exception
31253          * @param {Roo.bootstrap.DocumentManager} this
31254          * @param {XMLHttpRequest} xhr
31255          */
31256         "exception" : true,
31257         /**
31258          * @event afterupload
31259          * Fire when xhr load exception
31260          * @param {Roo.bootstrap.DocumentManager} this
31261          * @param {XMLHttpRequest} xhr
31262          */
31263         "afterupload" : true,
31264         /**
31265          * @event prepare
31266          * prepare the form data
31267          * @param {Roo.bootstrap.DocumentManager} this
31268          * @param {Object} formData
31269          */
31270         "prepare" : true,
31271         /**
31272          * @event remove
31273          * Fire when remove the file
31274          * @param {Roo.bootstrap.DocumentManager} this
31275          * @param {Object} file
31276          */
31277         "remove" : true,
31278         /**
31279          * @event refresh
31280          * Fire after refresh the file
31281          * @param {Roo.bootstrap.DocumentManager} this
31282          */
31283         "refresh" : true,
31284         /**
31285          * @event click
31286          * Fire after click the image
31287          * @param {Roo.bootstrap.DocumentManager} this
31288          * @param {Object} file
31289          */
31290         "click" : true,
31291         /**
31292          * @event edit
31293          * Fire when upload a image and editable set to true
31294          * @param {Roo.bootstrap.DocumentManager} this
31295          * @param {Object} file
31296          */
31297         "edit" : true,
31298         /**
31299          * @event beforeselectfile
31300          * Fire before select file
31301          * @param {Roo.bootstrap.DocumentManager} this
31302          */
31303         "beforeselectfile" : true,
31304         /**
31305          * @event process
31306          * Fire before process file
31307          * @param {Roo.bootstrap.DocumentManager} this
31308          * @param {Object} file
31309          */
31310         "process" : true,
31311         /**
31312          * @event previewrendered
31313          * Fire when preview rendered
31314          * @param {Roo.bootstrap.DocumentManager} this
31315          * @param {Object} file
31316          */
31317         "previewrendered" : true,
31318         /**
31319          */
31320         "previewResize" : true
31321         
31322     });
31323 };
31324
31325 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31326     
31327     boxes : 0,
31328     inputName : '',
31329     thumbSize : 300,
31330     multiple : true,
31331     files : false,
31332     method : 'POST',
31333     url : '',
31334     paramName : 'imageUpload',
31335     toolTipName : 'filename',
31336     fieldLabel : '',
31337     labelWidth : 4,
31338     labelAlign : 'left',
31339     editable : true,
31340     delegates : false,
31341     xhr : false, 
31342     
31343     labellg : 0,
31344     labelmd : 0,
31345     labelsm : 0,
31346     labelxs : 0,
31347     
31348     getAutoCreate : function()
31349     {   
31350         var managerWidget = {
31351             tag : 'div',
31352             cls : 'roo-document-manager',
31353             cn : [
31354                 {
31355                     tag : 'input',
31356                     cls : 'roo-document-manager-selector',
31357                     type : 'file'
31358                 },
31359                 {
31360                     tag : 'div',
31361                     cls : 'roo-document-manager-uploader',
31362                     cn : [
31363                         {
31364                             tag : 'div',
31365                             cls : 'roo-document-manager-upload-btn',
31366                             html : '<i class="fa fa-plus"></i>'
31367                         }
31368                     ]
31369                     
31370                 }
31371             ]
31372         };
31373         
31374         var content = [
31375             {
31376                 tag : 'div',
31377                 cls : 'column col-md-12',
31378                 cn : managerWidget
31379             }
31380         ];
31381         
31382         if(this.fieldLabel.length){
31383             
31384             content = [
31385                 {
31386                     tag : 'div',
31387                     cls : 'column col-md-12',
31388                     html : this.fieldLabel
31389                 },
31390                 {
31391                     tag : 'div',
31392                     cls : 'column col-md-12',
31393                     cn : managerWidget
31394                 }
31395             ];
31396
31397             if(this.labelAlign == 'left'){
31398                 content = [
31399                     {
31400                         tag : 'div',
31401                         cls : 'column',
31402                         html : this.fieldLabel
31403                     },
31404                     {
31405                         tag : 'div',
31406                         cls : 'column',
31407                         cn : managerWidget
31408                     }
31409                 ];
31410                 
31411                 if(this.labelWidth > 12){
31412                     content[0].style = "width: " + this.labelWidth + 'px';
31413                 }
31414
31415                 if(this.labelWidth < 13 && this.labelmd == 0){
31416                     this.labelmd = this.labelWidth;
31417                 }
31418
31419                 if(this.labellg > 0){
31420                     content[0].cls += ' col-lg-' + this.labellg;
31421                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31422                 }
31423
31424                 if(this.labelmd > 0){
31425                     content[0].cls += ' col-md-' + this.labelmd;
31426                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31427                 }
31428
31429                 if(this.labelsm > 0){
31430                     content[0].cls += ' col-sm-' + this.labelsm;
31431                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31432                 }
31433
31434                 if(this.labelxs > 0){
31435                     content[0].cls += ' col-xs-' + this.labelxs;
31436                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31437                 }
31438                 
31439             }
31440         }
31441         
31442         var cfg = {
31443             tag : 'div',
31444             cls : 'row clearfix',
31445             cn : content
31446         };
31447         
31448         return cfg;
31449         
31450     },
31451     
31452     initEvents : function()
31453     {
31454         this.managerEl = this.el.select('.roo-document-manager', true).first();
31455         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31456         
31457         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31458         this.selectorEl.hide();
31459         
31460         if(this.multiple){
31461             this.selectorEl.attr('multiple', 'multiple');
31462         }
31463         
31464         this.selectorEl.on('change', this.onFileSelected, this);
31465         
31466         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31467         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31468         
31469         this.uploader.on('click', this.onUploaderClick, this);
31470         
31471         this.renderProgressDialog();
31472         
31473         var _this = this;
31474         
31475         window.addEventListener("resize", function() { _this.refresh(); } );
31476         
31477         this.fireEvent('initial', this);
31478     },
31479     
31480     renderProgressDialog : function()
31481     {
31482         var _this = this;
31483         
31484         this.progressDialog = new Roo.bootstrap.Modal({
31485             cls : 'roo-document-manager-progress-dialog',
31486             allow_close : false,
31487             animate : false,
31488             title : '',
31489             buttons : [
31490                 {
31491                     name  :'cancel',
31492                     weight : 'danger',
31493                     html : 'Cancel'
31494                 }
31495             ], 
31496             listeners : { 
31497                 btnclick : function() {
31498                     _this.uploadCancel();
31499                     this.hide();
31500                 }
31501             }
31502         });
31503          
31504         this.progressDialog.render(Roo.get(document.body));
31505          
31506         this.progress = new Roo.bootstrap.Progress({
31507             cls : 'roo-document-manager-progress',
31508             active : true,
31509             striped : true
31510         });
31511         
31512         this.progress.render(this.progressDialog.getChildContainer());
31513         
31514         this.progressBar = new Roo.bootstrap.ProgressBar({
31515             cls : 'roo-document-manager-progress-bar',
31516             aria_valuenow : 0,
31517             aria_valuemin : 0,
31518             aria_valuemax : 12,
31519             panel : 'success'
31520         });
31521         
31522         this.progressBar.render(this.progress.getChildContainer());
31523     },
31524     
31525     onUploaderClick : function(e)
31526     {
31527         e.preventDefault();
31528      
31529         if(this.fireEvent('beforeselectfile', this) != false){
31530             this.selectorEl.dom.click();
31531         }
31532         
31533     },
31534     
31535     onFileSelected : function(e)
31536     {
31537         e.preventDefault();
31538         
31539         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31540             return;
31541         }
31542         
31543         Roo.each(this.selectorEl.dom.files, function(file){
31544             if(this.fireEvent('inspect', this, file) != false){
31545                 this.files.push(file);
31546             }
31547         }, this);
31548         
31549         this.queue();
31550         
31551     },
31552     
31553     queue : function()
31554     {
31555         this.selectorEl.dom.value = '';
31556         
31557         if(!this.files || !this.files.length){
31558             return;
31559         }
31560         
31561         if(this.boxes > 0 && this.files.length > this.boxes){
31562             this.files = this.files.slice(0, this.boxes);
31563         }
31564         
31565         this.uploader.show();
31566         
31567         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31568             this.uploader.hide();
31569         }
31570         
31571         var _this = this;
31572         
31573         var files = [];
31574         
31575         var docs = [];
31576         
31577         Roo.each(this.files, function(file){
31578             
31579             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31580                 var f = this.renderPreview(file);
31581                 files.push(f);
31582                 return;
31583             }
31584             
31585             if(file.type.indexOf('image') != -1){
31586                 this.delegates.push(
31587                     (function(){
31588                         _this.process(file);
31589                     }).createDelegate(this)
31590                 );
31591         
31592                 return;
31593             }
31594             
31595             docs.push(
31596                 (function(){
31597                     _this.process(file);
31598                 }).createDelegate(this)
31599             );
31600             
31601         }, this);
31602         
31603         this.files = files;
31604         
31605         this.delegates = this.delegates.concat(docs);
31606         
31607         if(!this.delegates.length){
31608             this.refresh();
31609             return;
31610         }
31611         
31612         this.progressBar.aria_valuemax = this.delegates.length;
31613         
31614         this.arrange();
31615         
31616         return;
31617     },
31618     
31619     arrange : function()
31620     {
31621         if(!this.delegates.length){
31622             this.progressDialog.hide();
31623             this.refresh();
31624             return;
31625         }
31626         
31627         var delegate = this.delegates.shift();
31628         
31629         this.progressDialog.show();
31630         
31631         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31632         
31633         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31634         
31635         delegate();
31636     },
31637     
31638     refresh : function()
31639     {
31640         this.uploader.show();
31641         
31642         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31643             this.uploader.hide();
31644         }
31645         
31646         Roo.isTouch ? this.closable(false) : this.closable(true);
31647         
31648         this.fireEvent('refresh', this);
31649     },
31650     
31651     onRemove : function(e, el, o)
31652     {
31653         e.preventDefault();
31654         
31655         this.fireEvent('remove', this, o);
31656         
31657     },
31658     
31659     remove : function(o)
31660     {
31661         var files = [];
31662         
31663         Roo.each(this.files, function(file){
31664             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31665                 files.push(file);
31666                 return;
31667             }
31668
31669             o.target.remove();
31670
31671         }, this);
31672         
31673         this.files = files;
31674         
31675         this.refresh();
31676     },
31677     
31678     clear : function()
31679     {
31680         Roo.each(this.files, function(file){
31681             if(!file.target){
31682                 return;
31683             }
31684             
31685             file.target.remove();
31686
31687         }, this);
31688         
31689         this.files = [];
31690         
31691         this.refresh();
31692     },
31693     
31694     onClick : function(e, el, o)
31695     {
31696         e.preventDefault();
31697         
31698         this.fireEvent('click', this, o);
31699         
31700     },
31701     
31702     closable : function(closable)
31703     {
31704         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31705             
31706             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31707             
31708             if(closable){
31709                 el.show();
31710                 return;
31711             }
31712             
31713             el.hide();
31714             
31715         }, this);
31716     },
31717     
31718     xhrOnLoad : function(xhr)
31719     {
31720         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31721             el.remove();
31722         }, this);
31723         
31724         if (xhr.readyState !== 4) {
31725             this.arrange();
31726             this.fireEvent('exception', this, xhr);
31727             return;
31728         }
31729
31730         var response = Roo.decode(xhr.responseText);
31731         
31732         if(!response.success){
31733             this.arrange();
31734             this.fireEvent('exception', this, xhr);
31735             return;
31736         }
31737         
31738         var file = this.renderPreview(response.data);
31739         
31740         this.files.push(file);
31741         
31742         this.arrange();
31743         
31744         this.fireEvent('afterupload', this, xhr);
31745         
31746     },
31747     
31748     xhrOnError : function(xhr)
31749     {
31750         Roo.log('xhr on error');
31751         
31752         var response = Roo.decode(xhr.responseText);
31753           
31754         Roo.log(response);
31755         
31756         this.arrange();
31757     },
31758     
31759     process : function(file)
31760     {
31761         if(this.fireEvent('process', this, file) !== false){
31762             if(this.editable && file.type.indexOf('image') != -1){
31763                 this.fireEvent('edit', this, file);
31764                 return;
31765             }
31766
31767             this.uploadStart(file, false);
31768
31769             return;
31770         }
31771         
31772     },
31773     
31774     uploadStart : function(file, crop)
31775     {
31776         this.xhr = new XMLHttpRequest();
31777         
31778         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31779             this.arrange();
31780             return;
31781         }
31782         
31783         file.xhr = this.xhr;
31784             
31785         this.managerEl.createChild({
31786             tag : 'div',
31787             cls : 'roo-document-manager-loading',
31788             cn : [
31789                 {
31790                     tag : 'div',
31791                     tooltip : file.name,
31792                     cls : 'roo-document-manager-thumb',
31793                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31794                 }
31795             ]
31796
31797         });
31798
31799         this.xhr.open(this.method, this.url, true);
31800         
31801         var headers = {
31802             "Accept": "application/json",
31803             "Cache-Control": "no-cache",
31804             "X-Requested-With": "XMLHttpRequest"
31805         };
31806         
31807         for (var headerName in headers) {
31808             var headerValue = headers[headerName];
31809             if (headerValue) {
31810                 this.xhr.setRequestHeader(headerName, headerValue);
31811             }
31812         }
31813         
31814         var _this = this;
31815         
31816         this.xhr.onload = function()
31817         {
31818             _this.xhrOnLoad(_this.xhr);
31819         }
31820         
31821         this.xhr.onerror = function()
31822         {
31823             _this.xhrOnError(_this.xhr);
31824         }
31825         
31826         var formData = new FormData();
31827
31828         formData.append('returnHTML', 'NO');
31829         
31830         if(crop){
31831             formData.append('crop', crop);
31832         }
31833         
31834         formData.append(this.paramName, file, file.name);
31835         
31836         var options = {
31837             file : file, 
31838             manually : false
31839         };
31840         
31841         if(this.fireEvent('prepare', this, formData, options) != false){
31842             
31843             if(options.manually){
31844                 return;
31845             }
31846             
31847             this.xhr.send(formData);
31848             return;
31849         };
31850         
31851         this.uploadCancel();
31852     },
31853     
31854     uploadCancel : function()
31855     {
31856         if (this.xhr) {
31857             this.xhr.abort();
31858         }
31859         
31860         this.delegates = [];
31861         
31862         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31863             el.remove();
31864         }, this);
31865         
31866         this.arrange();
31867     },
31868     
31869     renderPreview : function(file)
31870     {
31871         if(typeof(file.target) != 'undefined' && file.target){
31872             return file;
31873         }
31874         
31875         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31876         
31877         var previewEl = this.managerEl.createChild({
31878             tag : 'div',
31879             cls : 'roo-document-manager-preview',
31880             cn : [
31881                 {
31882                     tag : 'div',
31883                     tooltip : file[this.toolTipName],
31884                     cls : 'roo-document-manager-thumb',
31885                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31886                 },
31887                 {
31888                     tag : 'button',
31889                     cls : 'close',
31890                     html : '<i class="fa fa-times-circle"></i>'
31891                 }
31892             ]
31893         });
31894
31895         var close = previewEl.select('button.close', true).first();
31896
31897         close.on('click', this.onRemove, this, file);
31898
31899         file.target = previewEl;
31900
31901         var image = previewEl.select('img', true).first();
31902         
31903         var _this = this;
31904         
31905         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31906         
31907         image.on('click', this.onClick, this, file);
31908         
31909         this.fireEvent('previewrendered', this, file);
31910         
31911         return file;
31912         
31913     },
31914     
31915     onPreviewLoad : function(file, image)
31916     {
31917         if(typeof(file.target) == 'undefined' || !file.target){
31918             return;
31919         }
31920         
31921         var width = image.dom.naturalWidth || image.dom.width;
31922         var height = image.dom.naturalHeight || image.dom.height;
31923         
31924         if(!this.previewResize) {
31925             return;
31926         }
31927         
31928         if(width > height){
31929             file.target.addClass('wide');
31930             return;
31931         }
31932         
31933         file.target.addClass('tall');
31934         return;
31935         
31936     },
31937     
31938     uploadFromSource : function(file, crop)
31939     {
31940         this.xhr = new XMLHttpRequest();
31941         
31942         this.managerEl.createChild({
31943             tag : 'div',
31944             cls : 'roo-document-manager-loading',
31945             cn : [
31946                 {
31947                     tag : 'div',
31948                     tooltip : file.name,
31949                     cls : 'roo-document-manager-thumb',
31950                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31951                 }
31952             ]
31953
31954         });
31955
31956         this.xhr.open(this.method, this.url, true);
31957         
31958         var headers = {
31959             "Accept": "application/json",
31960             "Cache-Control": "no-cache",
31961             "X-Requested-With": "XMLHttpRequest"
31962         };
31963         
31964         for (var headerName in headers) {
31965             var headerValue = headers[headerName];
31966             if (headerValue) {
31967                 this.xhr.setRequestHeader(headerName, headerValue);
31968             }
31969         }
31970         
31971         var _this = this;
31972         
31973         this.xhr.onload = function()
31974         {
31975             _this.xhrOnLoad(_this.xhr);
31976         }
31977         
31978         this.xhr.onerror = function()
31979         {
31980             _this.xhrOnError(_this.xhr);
31981         }
31982         
31983         var formData = new FormData();
31984
31985         formData.append('returnHTML', 'NO');
31986         
31987         formData.append('crop', crop);
31988         
31989         if(typeof(file.filename) != 'undefined'){
31990             formData.append('filename', file.filename);
31991         }
31992         
31993         if(typeof(file.mimetype) != 'undefined'){
31994             formData.append('mimetype', file.mimetype);
31995         }
31996         
31997         Roo.log(formData);
31998         
31999         if(this.fireEvent('prepare', this, formData) != false){
32000             this.xhr.send(formData);
32001         };
32002     }
32003 });
32004
32005 /*
32006 * Licence: LGPL
32007 */
32008
32009 /**
32010  * @class Roo.bootstrap.DocumentViewer
32011  * @extends Roo.bootstrap.Component
32012  * Bootstrap DocumentViewer class
32013  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32014  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32015  * 
32016  * @constructor
32017  * Create a new DocumentViewer
32018  * @param {Object} config The config object
32019  */
32020
32021 Roo.bootstrap.DocumentViewer = function(config){
32022     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32023     
32024     this.addEvents({
32025         /**
32026          * @event initial
32027          * Fire after initEvent
32028          * @param {Roo.bootstrap.DocumentViewer} this
32029          */
32030         "initial" : true,
32031         /**
32032          * @event click
32033          * Fire after click
32034          * @param {Roo.bootstrap.DocumentViewer} this
32035          */
32036         "click" : true,
32037         /**
32038          * @event download
32039          * Fire after download button
32040          * @param {Roo.bootstrap.DocumentViewer} this
32041          */
32042         "download" : true,
32043         /**
32044          * @event trash
32045          * Fire after trash button
32046          * @param {Roo.bootstrap.DocumentViewer} this
32047          */
32048         "trash" : true
32049         
32050     });
32051 };
32052
32053 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32054     
32055     showDownload : true,
32056     
32057     showTrash : true,
32058     
32059     getAutoCreate : function()
32060     {
32061         var cfg = {
32062             tag : 'div',
32063             cls : 'roo-document-viewer',
32064             cn : [
32065                 {
32066                     tag : 'div',
32067                     cls : 'roo-document-viewer-body',
32068                     cn : [
32069                         {
32070                             tag : 'div',
32071                             cls : 'roo-document-viewer-thumb',
32072                             cn : [
32073                                 {
32074                                     tag : 'img',
32075                                     cls : 'roo-document-viewer-image'
32076                                 }
32077                             ]
32078                         }
32079                     ]
32080                 },
32081                 {
32082                     tag : 'div',
32083                     cls : 'roo-document-viewer-footer',
32084                     cn : {
32085                         tag : 'div',
32086                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32087                         cn : [
32088                             {
32089                                 tag : 'div',
32090                                 cls : 'btn-group roo-document-viewer-download',
32091                                 cn : [
32092                                     {
32093                                         tag : 'button',
32094                                         cls : 'btn btn-default',
32095                                         html : '<i class="fa fa-download"></i>'
32096                                     }
32097                                 ]
32098                             },
32099                             {
32100                                 tag : 'div',
32101                                 cls : 'btn-group roo-document-viewer-trash',
32102                                 cn : [
32103                                     {
32104                                         tag : 'button',
32105                                         cls : 'btn btn-default',
32106                                         html : '<i class="fa fa-trash"></i>'
32107                                     }
32108                                 ]
32109                             }
32110                         ]
32111                     }
32112                 }
32113             ]
32114         };
32115         
32116         return cfg;
32117     },
32118     
32119     initEvents : function()
32120     {
32121         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32122         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32123         
32124         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32125         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32126         
32127         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32128         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32129         
32130         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32131         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32132         
32133         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32134         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32135         
32136         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32137         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32138         
32139         this.bodyEl.on('click', this.onClick, this);
32140         this.downloadBtn.on('click', this.onDownload, this);
32141         this.trashBtn.on('click', this.onTrash, this);
32142         
32143         this.downloadBtn.hide();
32144         this.trashBtn.hide();
32145         
32146         if(this.showDownload){
32147             this.downloadBtn.show();
32148         }
32149         
32150         if(this.showTrash){
32151             this.trashBtn.show();
32152         }
32153         
32154         if(!this.showDownload && !this.showTrash) {
32155             this.footerEl.hide();
32156         }
32157         
32158     },
32159     
32160     initial : function()
32161     {
32162         this.fireEvent('initial', this);
32163         
32164     },
32165     
32166     onClick : function(e)
32167     {
32168         e.preventDefault();
32169         
32170         this.fireEvent('click', this);
32171     },
32172     
32173     onDownload : function(e)
32174     {
32175         e.preventDefault();
32176         
32177         this.fireEvent('download', this);
32178     },
32179     
32180     onTrash : function(e)
32181     {
32182         e.preventDefault();
32183         
32184         this.fireEvent('trash', this);
32185     }
32186     
32187 });
32188 /*
32189  * - LGPL
32190  *
32191  * nav progress bar
32192  * 
32193  */
32194
32195 /**
32196  * @class Roo.bootstrap.NavProgressBar
32197  * @extends Roo.bootstrap.Component
32198  * Bootstrap NavProgressBar class
32199  * 
32200  * @constructor
32201  * Create a new nav progress bar
32202  * @param {Object} config The config object
32203  */
32204
32205 Roo.bootstrap.NavProgressBar = function(config){
32206     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32207
32208     this.bullets = this.bullets || [];
32209    
32210 //    Roo.bootstrap.NavProgressBar.register(this);
32211      this.addEvents({
32212         /**
32213              * @event changed
32214              * Fires when the active item changes
32215              * @param {Roo.bootstrap.NavProgressBar} this
32216              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32217              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32218          */
32219         'changed': true
32220      });
32221     
32222 };
32223
32224 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32225     
32226     bullets : [],
32227     barItems : [],
32228     
32229     getAutoCreate : function()
32230     {
32231         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32232         
32233         cfg = {
32234             tag : 'div',
32235             cls : 'roo-navigation-bar-group',
32236             cn : [
32237                 {
32238                     tag : 'div',
32239                     cls : 'roo-navigation-top-bar'
32240                 },
32241                 {
32242                     tag : 'div',
32243                     cls : 'roo-navigation-bullets-bar',
32244                     cn : [
32245                         {
32246                             tag : 'ul',
32247                             cls : 'roo-navigation-bar'
32248                         }
32249                     ]
32250                 },
32251                 
32252                 {
32253                     tag : 'div',
32254                     cls : 'roo-navigation-bottom-bar'
32255                 }
32256             ]
32257             
32258         };
32259         
32260         return cfg;
32261         
32262     },
32263     
32264     initEvents: function() 
32265     {
32266         
32267     },
32268     
32269     onRender : function(ct, position) 
32270     {
32271         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32272         
32273         if(this.bullets.length){
32274             Roo.each(this.bullets, function(b){
32275                this.addItem(b);
32276             }, this);
32277         }
32278         
32279         this.format();
32280         
32281     },
32282     
32283     addItem : function(cfg)
32284     {
32285         var item = new Roo.bootstrap.NavProgressItem(cfg);
32286         
32287         item.parentId = this.id;
32288         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32289         
32290         if(cfg.html){
32291             var top = new Roo.bootstrap.Element({
32292                 tag : 'div',
32293                 cls : 'roo-navigation-bar-text'
32294             });
32295             
32296             var bottom = new Roo.bootstrap.Element({
32297                 tag : 'div',
32298                 cls : 'roo-navigation-bar-text'
32299             });
32300             
32301             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32302             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32303             
32304             var topText = new Roo.bootstrap.Element({
32305                 tag : 'span',
32306                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32307             });
32308             
32309             var bottomText = new Roo.bootstrap.Element({
32310                 tag : 'span',
32311                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32312             });
32313             
32314             topText.onRender(top.el, null);
32315             bottomText.onRender(bottom.el, null);
32316             
32317             item.topEl = top;
32318             item.bottomEl = bottom;
32319         }
32320         
32321         this.barItems.push(item);
32322         
32323         return item;
32324     },
32325     
32326     getActive : function()
32327     {
32328         var active = false;
32329         
32330         Roo.each(this.barItems, function(v){
32331             
32332             if (!v.isActive()) {
32333                 return;
32334             }
32335             
32336             active = v;
32337             return false;
32338             
32339         });
32340         
32341         return active;
32342     },
32343     
32344     setActiveItem : function(item)
32345     {
32346         var prev = false;
32347         
32348         Roo.each(this.barItems, function(v){
32349             if (v.rid == item.rid) {
32350                 return ;
32351             }
32352             
32353             if (v.isActive()) {
32354                 v.setActive(false);
32355                 prev = v;
32356             }
32357         });
32358
32359         item.setActive(true);
32360         
32361         this.fireEvent('changed', this, item, prev);
32362     },
32363     
32364     getBarItem: function(rid)
32365     {
32366         var ret = false;
32367         
32368         Roo.each(this.barItems, function(e) {
32369             if (e.rid != rid) {
32370                 return;
32371             }
32372             
32373             ret =  e;
32374             return false;
32375         });
32376         
32377         return ret;
32378     },
32379     
32380     indexOfItem : function(item)
32381     {
32382         var index = false;
32383         
32384         Roo.each(this.barItems, function(v, i){
32385             
32386             if (v.rid != item.rid) {
32387                 return;
32388             }
32389             
32390             index = i;
32391             return false
32392         });
32393         
32394         return index;
32395     },
32396     
32397     setActiveNext : function()
32398     {
32399         var i = this.indexOfItem(this.getActive());
32400         
32401         if (i > this.barItems.length) {
32402             return;
32403         }
32404         
32405         this.setActiveItem(this.barItems[i+1]);
32406     },
32407     
32408     setActivePrev : function()
32409     {
32410         var i = this.indexOfItem(this.getActive());
32411         
32412         if (i  < 1) {
32413             return;
32414         }
32415         
32416         this.setActiveItem(this.barItems[i-1]);
32417     },
32418     
32419     format : function()
32420     {
32421         if(!this.barItems.length){
32422             return;
32423         }
32424      
32425         var width = 100 / this.barItems.length;
32426         
32427         Roo.each(this.barItems, function(i){
32428             i.el.setStyle('width', width + '%');
32429             i.topEl.el.setStyle('width', width + '%');
32430             i.bottomEl.el.setStyle('width', width + '%');
32431         }, this);
32432         
32433     }
32434     
32435 });
32436 /*
32437  * - LGPL
32438  *
32439  * Nav Progress Item
32440  * 
32441  */
32442
32443 /**
32444  * @class Roo.bootstrap.NavProgressItem
32445  * @extends Roo.bootstrap.Component
32446  * Bootstrap NavProgressItem class
32447  * @cfg {String} rid the reference id
32448  * @cfg {Boolean} active (true|false) Is item active default false
32449  * @cfg {Boolean} disabled (true|false) Is item active default false
32450  * @cfg {String} html
32451  * @cfg {String} position (top|bottom) text position default bottom
32452  * @cfg {String} icon show icon instead of number
32453  * 
32454  * @constructor
32455  * Create a new NavProgressItem
32456  * @param {Object} config The config object
32457  */
32458 Roo.bootstrap.NavProgressItem = function(config){
32459     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32460     this.addEvents({
32461         // raw events
32462         /**
32463          * @event click
32464          * The raw click event for the entire grid.
32465          * @param {Roo.bootstrap.NavProgressItem} this
32466          * @param {Roo.EventObject} e
32467          */
32468         "click" : true
32469     });
32470    
32471 };
32472
32473 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32474     
32475     rid : '',
32476     active : false,
32477     disabled : false,
32478     html : '',
32479     position : 'bottom',
32480     icon : false,
32481     
32482     getAutoCreate : function()
32483     {
32484         var iconCls = 'roo-navigation-bar-item-icon';
32485         
32486         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32487         
32488         var cfg = {
32489             tag: 'li',
32490             cls: 'roo-navigation-bar-item',
32491             cn : [
32492                 {
32493                     tag : 'i',
32494                     cls : iconCls
32495                 }
32496             ]
32497         };
32498         
32499         if(this.active){
32500             cfg.cls += ' active';
32501         }
32502         if(this.disabled){
32503             cfg.cls += ' disabled';
32504         }
32505         
32506         return cfg;
32507     },
32508     
32509     disable : function()
32510     {
32511         this.setDisabled(true);
32512     },
32513     
32514     enable : function()
32515     {
32516         this.setDisabled(false);
32517     },
32518     
32519     initEvents: function() 
32520     {
32521         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32522         
32523         this.iconEl.on('click', this.onClick, this);
32524     },
32525     
32526     onClick : function(e)
32527     {
32528         e.preventDefault();
32529         
32530         if(this.disabled){
32531             return;
32532         }
32533         
32534         if(this.fireEvent('click', this, e) === false){
32535             return;
32536         };
32537         
32538         this.parent().setActiveItem(this);
32539     },
32540     
32541     isActive: function () 
32542     {
32543         return this.active;
32544     },
32545     
32546     setActive : function(state)
32547     {
32548         if(this.active == state){
32549             return;
32550         }
32551         
32552         this.active = state;
32553         
32554         if (state) {
32555             this.el.addClass('active');
32556             return;
32557         }
32558         
32559         this.el.removeClass('active');
32560         
32561         return;
32562     },
32563     
32564     setDisabled : function(state)
32565     {
32566         if(this.disabled == state){
32567             return;
32568         }
32569         
32570         this.disabled = state;
32571         
32572         if (state) {
32573             this.el.addClass('disabled');
32574             return;
32575         }
32576         
32577         this.el.removeClass('disabled');
32578     },
32579     
32580     tooltipEl : function()
32581     {
32582         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32583     }
32584 });
32585  
32586
32587  /*
32588  * - LGPL
32589  *
32590  * FieldLabel
32591  * 
32592  */
32593
32594 /**
32595  * @class Roo.bootstrap.FieldLabel
32596  * @extends Roo.bootstrap.Component
32597  * Bootstrap FieldLabel class
32598  * @cfg {String} html contents of the element
32599  * @cfg {String} tag tag of the element default label
32600  * @cfg {String} cls class of the element
32601  * @cfg {String} target label target 
32602  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32603  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32604  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32605  * @cfg {String} iconTooltip default "This field is required"
32606  * @cfg {String} indicatorpos (left|right) default left
32607  * 
32608  * @constructor
32609  * Create a new FieldLabel
32610  * @param {Object} config The config object
32611  */
32612
32613 Roo.bootstrap.FieldLabel = function(config){
32614     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32615     
32616     this.addEvents({
32617             /**
32618              * @event invalid
32619              * Fires after the field has been marked as invalid.
32620              * @param {Roo.form.FieldLabel} this
32621              * @param {String} msg The validation message
32622              */
32623             invalid : true,
32624             /**
32625              * @event valid
32626              * Fires after the field has been validated with no errors.
32627              * @param {Roo.form.FieldLabel} this
32628              */
32629             valid : true
32630         });
32631 };
32632
32633 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32634     
32635     tag: 'label',
32636     cls: '',
32637     html: '',
32638     target: '',
32639     allowBlank : true,
32640     invalidClass : 'has-warning',
32641     validClass : 'has-success',
32642     iconTooltip : 'This field is required',
32643     indicatorpos : 'left',
32644     
32645     getAutoCreate : function(){
32646         
32647         var cls = "";
32648         if (!this.allowBlank) {
32649             cls  = "visible";
32650         }
32651         
32652         var cfg = {
32653             tag : this.tag,
32654             cls : 'roo-bootstrap-field-label ' + this.cls,
32655             for : this.target,
32656             cn : [
32657                 {
32658                     tag : 'i',
32659                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32660                     tooltip : this.iconTooltip
32661                 },
32662                 {
32663                     tag : 'span',
32664                     html : this.html
32665                 }
32666             ] 
32667         };
32668         
32669         if(this.indicatorpos == 'right'){
32670             var cfg = {
32671                 tag : this.tag,
32672                 cls : 'roo-bootstrap-field-label ' + this.cls,
32673                 for : this.target,
32674                 cn : [
32675                     {
32676                         tag : 'span',
32677                         html : this.html
32678                     },
32679                     {
32680                         tag : 'i',
32681                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32682                         tooltip : this.iconTooltip
32683                     }
32684                 ] 
32685             };
32686         }
32687         
32688         return cfg;
32689     },
32690     
32691     initEvents: function() 
32692     {
32693         Roo.bootstrap.Element.superclass.initEvents.call(this);
32694         
32695         this.indicator = this.indicatorEl();
32696         
32697         if(this.indicator){
32698             this.indicator.removeClass('visible');
32699             this.indicator.addClass('invisible');
32700         }
32701         
32702         Roo.bootstrap.FieldLabel.register(this);
32703     },
32704     
32705     indicatorEl : function()
32706     {
32707         var indicator = this.el.select('i.roo-required-indicator',true).first();
32708         
32709         if(!indicator){
32710             return false;
32711         }
32712         
32713         return indicator;
32714         
32715     },
32716     
32717     /**
32718      * Mark this field as valid
32719      */
32720     markValid : function()
32721     {
32722         if(this.indicator){
32723             this.indicator.removeClass('visible');
32724             this.indicator.addClass('invisible');
32725         }
32726         if (Roo.bootstrap.version == 3) {
32727             this.el.removeClass(this.invalidClass);
32728             this.el.addClass(this.validClass);
32729         } else {
32730             this.el.removeClass('is-invalid');
32731             this.el.addClass('is-valid');
32732         }
32733         
32734         
32735         this.fireEvent('valid', this);
32736     },
32737     
32738     /**
32739      * Mark this field as invalid
32740      * @param {String} msg The validation message
32741      */
32742     markInvalid : function(msg)
32743     {
32744         if(this.indicator){
32745             this.indicator.removeClass('invisible');
32746             this.indicator.addClass('visible');
32747         }
32748           if (Roo.bootstrap.version == 3) {
32749             this.el.removeClass(this.validClass);
32750             this.el.addClass(this.invalidClass);
32751         } else {
32752             this.el.removeClass('is-valid');
32753             this.el.addClass('is-invalid');
32754         }
32755         
32756         
32757         this.fireEvent('invalid', this, msg);
32758     }
32759     
32760    
32761 });
32762
32763 Roo.apply(Roo.bootstrap.FieldLabel, {
32764     
32765     groups: {},
32766     
32767      /**
32768     * register a FieldLabel Group
32769     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32770     */
32771     register : function(label)
32772     {
32773         if(this.groups.hasOwnProperty(label.target)){
32774             return;
32775         }
32776      
32777         this.groups[label.target] = label;
32778         
32779     },
32780     /**
32781     * fetch a FieldLabel Group based on the target
32782     * @param {string} target
32783     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32784     */
32785     get: function(target) {
32786         if (typeof(this.groups[target]) == 'undefined') {
32787             return false;
32788         }
32789         
32790         return this.groups[target] ;
32791     }
32792 });
32793
32794  
32795
32796  /*
32797  * - LGPL
32798  *
32799  * page DateSplitField.
32800  * 
32801  */
32802
32803
32804 /**
32805  * @class Roo.bootstrap.DateSplitField
32806  * @extends Roo.bootstrap.Component
32807  * Bootstrap DateSplitField class
32808  * @cfg {string} fieldLabel - the label associated
32809  * @cfg {Number} labelWidth set the width of label (0-12)
32810  * @cfg {String} labelAlign (top|left)
32811  * @cfg {Boolean} dayAllowBlank (true|false) default false
32812  * @cfg {Boolean} monthAllowBlank (true|false) default false
32813  * @cfg {Boolean} yearAllowBlank (true|false) default false
32814  * @cfg {string} dayPlaceholder 
32815  * @cfg {string} monthPlaceholder
32816  * @cfg {string} yearPlaceholder
32817  * @cfg {string} dayFormat default 'd'
32818  * @cfg {string} monthFormat default 'm'
32819  * @cfg {string} yearFormat default 'Y'
32820  * @cfg {Number} labellg set the width of label (1-12)
32821  * @cfg {Number} labelmd set the width of label (1-12)
32822  * @cfg {Number} labelsm set the width of label (1-12)
32823  * @cfg {Number} labelxs set the width of label (1-12)
32824
32825  *     
32826  * @constructor
32827  * Create a new DateSplitField
32828  * @param {Object} config The config object
32829  */
32830
32831 Roo.bootstrap.DateSplitField = function(config){
32832     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32833     
32834     this.addEvents({
32835         // raw events
32836          /**
32837          * @event years
32838          * getting the data of years
32839          * @param {Roo.bootstrap.DateSplitField} this
32840          * @param {Object} years
32841          */
32842         "years" : true,
32843         /**
32844          * @event days
32845          * getting the data of days
32846          * @param {Roo.bootstrap.DateSplitField} this
32847          * @param {Object} days
32848          */
32849         "days" : true,
32850         /**
32851          * @event invalid
32852          * Fires after the field has been marked as invalid.
32853          * @param {Roo.form.Field} this
32854          * @param {String} msg The validation message
32855          */
32856         invalid : true,
32857        /**
32858          * @event valid
32859          * Fires after the field has been validated with no errors.
32860          * @param {Roo.form.Field} this
32861          */
32862         valid : true
32863     });
32864 };
32865
32866 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32867     
32868     fieldLabel : '',
32869     labelAlign : 'top',
32870     labelWidth : 3,
32871     dayAllowBlank : false,
32872     monthAllowBlank : false,
32873     yearAllowBlank : false,
32874     dayPlaceholder : '',
32875     monthPlaceholder : '',
32876     yearPlaceholder : '',
32877     dayFormat : 'd',
32878     monthFormat : 'm',
32879     yearFormat : 'Y',
32880     isFormField : true,
32881     labellg : 0,
32882     labelmd : 0,
32883     labelsm : 0,
32884     labelxs : 0,
32885     
32886     getAutoCreate : function()
32887     {
32888         var cfg = {
32889             tag : 'div',
32890             cls : 'row roo-date-split-field-group',
32891             cn : [
32892                 {
32893                     tag : 'input',
32894                     type : 'hidden',
32895                     cls : 'form-hidden-field roo-date-split-field-group-value',
32896                     name : this.name
32897                 }
32898             ]
32899         };
32900         
32901         var labelCls = 'col-md-12';
32902         var contentCls = 'col-md-4';
32903         
32904         if(this.fieldLabel){
32905             
32906             var label = {
32907                 tag : 'div',
32908                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32909                 cn : [
32910                     {
32911                         tag : 'label',
32912                         html : this.fieldLabel
32913                     }
32914                 ]
32915             };
32916             
32917             if(this.labelAlign == 'left'){
32918             
32919                 if(this.labelWidth > 12){
32920                     label.style = "width: " + this.labelWidth + 'px';
32921                 }
32922
32923                 if(this.labelWidth < 13 && this.labelmd == 0){
32924                     this.labelmd = this.labelWidth;
32925                 }
32926
32927                 if(this.labellg > 0){
32928                     labelCls = ' col-lg-' + this.labellg;
32929                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32930                 }
32931
32932                 if(this.labelmd > 0){
32933                     labelCls = ' col-md-' + this.labelmd;
32934                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32935                 }
32936
32937                 if(this.labelsm > 0){
32938                     labelCls = ' col-sm-' + this.labelsm;
32939                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32940                 }
32941
32942                 if(this.labelxs > 0){
32943                     labelCls = ' col-xs-' + this.labelxs;
32944                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32945                 }
32946             }
32947             
32948             label.cls += ' ' + labelCls;
32949             
32950             cfg.cn.push(label);
32951         }
32952         
32953         Roo.each(['day', 'month', 'year'], function(t){
32954             cfg.cn.push({
32955                 tag : 'div',
32956                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32957             });
32958         }, this);
32959         
32960         return cfg;
32961     },
32962     
32963     inputEl: function ()
32964     {
32965         return this.el.select('.roo-date-split-field-group-value', true).first();
32966     },
32967     
32968     onRender : function(ct, position) 
32969     {
32970         var _this = this;
32971         
32972         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32973         
32974         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32975         
32976         this.dayField = new Roo.bootstrap.ComboBox({
32977             allowBlank : this.dayAllowBlank,
32978             alwaysQuery : true,
32979             displayField : 'value',
32980             editable : false,
32981             fieldLabel : '',
32982             forceSelection : true,
32983             mode : 'local',
32984             placeholder : this.dayPlaceholder,
32985             selectOnFocus : true,
32986             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32987             triggerAction : 'all',
32988             typeAhead : true,
32989             valueField : 'value',
32990             store : new Roo.data.SimpleStore({
32991                 data : (function() {    
32992                     var days = [];
32993                     _this.fireEvent('days', _this, days);
32994                     return days;
32995                 })(),
32996                 fields : [ 'value' ]
32997             }),
32998             listeners : {
32999                 select : function (_self, record, index)
33000                 {
33001                     _this.setValue(_this.getValue());
33002                 }
33003             }
33004         });
33005
33006         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33007         
33008         this.monthField = new Roo.bootstrap.MonthField({
33009             after : '<i class=\"fa fa-calendar\"></i>',
33010             allowBlank : this.monthAllowBlank,
33011             placeholder : this.monthPlaceholder,
33012             readOnly : true,
33013             listeners : {
33014                 render : function (_self)
33015                 {
33016                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33017                         e.preventDefault();
33018                         _self.focus();
33019                     });
33020                 },
33021                 select : function (_self, oldvalue, newvalue)
33022                 {
33023                     _this.setValue(_this.getValue());
33024                 }
33025             }
33026         });
33027         
33028         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33029         
33030         this.yearField = new Roo.bootstrap.ComboBox({
33031             allowBlank : this.yearAllowBlank,
33032             alwaysQuery : true,
33033             displayField : 'value',
33034             editable : false,
33035             fieldLabel : '',
33036             forceSelection : true,
33037             mode : 'local',
33038             placeholder : this.yearPlaceholder,
33039             selectOnFocus : true,
33040             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33041             triggerAction : 'all',
33042             typeAhead : true,
33043             valueField : 'value',
33044             store : new Roo.data.SimpleStore({
33045                 data : (function() {
33046                     var years = [];
33047                     _this.fireEvent('years', _this, years);
33048                     return years;
33049                 })(),
33050                 fields : [ 'value' ]
33051             }),
33052             listeners : {
33053                 select : function (_self, record, index)
33054                 {
33055                     _this.setValue(_this.getValue());
33056                 }
33057             }
33058         });
33059
33060         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33061     },
33062     
33063     setValue : function(v, format)
33064     {
33065         this.inputEl.dom.value = v;
33066         
33067         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33068         
33069         var d = Date.parseDate(v, f);
33070         
33071         if(!d){
33072             this.validate();
33073             return;
33074         }
33075         
33076         this.setDay(d.format(this.dayFormat));
33077         this.setMonth(d.format(this.monthFormat));
33078         this.setYear(d.format(this.yearFormat));
33079         
33080         this.validate();
33081         
33082         return;
33083     },
33084     
33085     setDay : function(v)
33086     {
33087         this.dayField.setValue(v);
33088         this.inputEl.dom.value = this.getValue();
33089         this.validate();
33090         return;
33091     },
33092     
33093     setMonth : function(v)
33094     {
33095         this.monthField.setValue(v, true);
33096         this.inputEl.dom.value = this.getValue();
33097         this.validate();
33098         return;
33099     },
33100     
33101     setYear : function(v)
33102     {
33103         this.yearField.setValue(v);
33104         this.inputEl.dom.value = this.getValue();
33105         this.validate();
33106         return;
33107     },
33108     
33109     getDay : function()
33110     {
33111         return this.dayField.getValue();
33112     },
33113     
33114     getMonth : function()
33115     {
33116         return this.monthField.getValue();
33117     },
33118     
33119     getYear : function()
33120     {
33121         return this.yearField.getValue();
33122     },
33123     
33124     getValue : function()
33125     {
33126         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33127         
33128         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33129         
33130         return date;
33131     },
33132     
33133     reset : function()
33134     {
33135         this.setDay('');
33136         this.setMonth('');
33137         this.setYear('');
33138         this.inputEl.dom.value = '';
33139         this.validate();
33140         return;
33141     },
33142     
33143     validate : function()
33144     {
33145         var d = this.dayField.validate();
33146         var m = this.monthField.validate();
33147         var y = this.yearField.validate();
33148         
33149         var valid = true;
33150         
33151         if(
33152                 (!this.dayAllowBlank && !d) ||
33153                 (!this.monthAllowBlank && !m) ||
33154                 (!this.yearAllowBlank && !y)
33155         ){
33156             valid = false;
33157         }
33158         
33159         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33160             return valid;
33161         }
33162         
33163         if(valid){
33164             this.markValid();
33165             return valid;
33166         }
33167         
33168         this.markInvalid();
33169         
33170         return valid;
33171     },
33172     
33173     markValid : function()
33174     {
33175         
33176         var label = this.el.select('label', true).first();
33177         var icon = this.el.select('i.fa-star', true).first();
33178
33179         if(label && icon){
33180             icon.remove();
33181         }
33182         
33183         this.fireEvent('valid', this);
33184     },
33185     
33186      /**
33187      * Mark this field as invalid
33188      * @param {String} msg The validation message
33189      */
33190     markInvalid : function(msg)
33191     {
33192         
33193         var label = this.el.select('label', true).first();
33194         var icon = this.el.select('i.fa-star', true).first();
33195
33196         if(label && !icon){
33197             this.el.select('.roo-date-split-field-label', true).createChild({
33198                 tag : 'i',
33199                 cls : 'text-danger fa fa-lg fa-star',
33200                 tooltip : 'This field is required',
33201                 style : 'margin-right:5px;'
33202             }, label, true);
33203         }
33204         
33205         this.fireEvent('invalid', this, msg);
33206     },
33207     
33208     clearInvalid : function()
33209     {
33210         var label = this.el.select('label', true).first();
33211         var icon = this.el.select('i.fa-star', true).first();
33212
33213         if(label && icon){
33214             icon.remove();
33215         }
33216         
33217         this.fireEvent('valid', this);
33218     },
33219     
33220     getName: function()
33221     {
33222         return this.name;
33223     }
33224     
33225 });
33226
33227  /**
33228  *
33229  * This is based on 
33230  * http://masonry.desandro.com
33231  *
33232  * The idea is to render all the bricks based on vertical width...
33233  *
33234  * The original code extends 'outlayer' - we might need to use that....
33235  * 
33236  */
33237
33238
33239 /**
33240  * @class Roo.bootstrap.LayoutMasonry
33241  * @extends Roo.bootstrap.Component
33242  * Bootstrap Layout Masonry class
33243  * 
33244  * @constructor
33245  * Create a new Element
33246  * @param {Object} config The config object
33247  */
33248
33249 Roo.bootstrap.LayoutMasonry = function(config){
33250     
33251     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33252     
33253     this.bricks = [];
33254     
33255     Roo.bootstrap.LayoutMasonry.register(this);
33256     
33257     this.addEvents({
33258         // raw events
33259         /**
33260          * @event layout
33261          * Fire after layout the items
33262          * @param {Roo.bootstrap.LayoutMasonry} this
33263          * @param {Roo.EventObject} e
33264          */
33265         "layout" : true
33266     });
33267     
33268 };
33269
33270 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33271     
33272     /**
33273      * @cfg {Boolean} isLayoutInstant = no animation?
33274      */   
33275     isLayoutInstant : false, // needed?
33276    
33277     /**
33278      * @cfg {Number} boxWidth  width of the columns
33279      */   
33280     boxWidth : 450,
33281     
33282       /**
33283      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33284      */   
33285     boxHeight : 0,
33286     
33287     /**
33288      * @cfg {Number} padWidth padding below box..
33289      */   
33290     padWidth : 10, 
33291     
33292     /**
33293      * @cfg {Number} gutter gutter width..
33294      */   
33295     gutter : 10,
33296     
33297      /**
33298      * @cfg {Number} maxCols maximum number of columns
33299      */   
33300     
33301     maxCols: 0,
33302     
33303     /**
33304      * @cfg {Boolean} isAutoInitial defalut true
33305      */   
33306     isAutoInitial : true, 
33307     
33308     containerWidth: 0,
33309     
33310     /**
33311      * @cfg {Boolean} isHorizontal defalut false
33312      */   
33313     isHorizontal : false, 
33314
33315     currentSize : null,
33316     
33317     tag: 'div',
33318     
33319     cls: '',
33320     
33321     bricks: null, //CompositeElement
33322     
33323     cols : 1,
33324     
33325     _isLayoutInited : false,
33326     
33327 //    isAlternative : false, // only use for vertical layout...
33328     
33329     /**
33330      * @cfg {Number} alternativePadWidth padding below box..
33331      */   
33332     alternativePadWidth : 50,
33333     
33334     selectedBrick : [],
33335     
33336     getAutoCreate : function(){
33337         
33338         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33339         
33340         var cfg = {
33341             tag: this.tag,
33342             cls: 'blog-masonary-wrapper ' + this.cls,
33343             cn : {
33344                 cls : 'mas-boxes masonary'
33345             }
33346         };
33347         
33348         return cfg;
33349     },
33350     
33351     getChildContainer: function( )
33352     {
33353         if (this.boxesEl) {
33354             return this.boxesEl;
33355         }
33356         
33357         this.boxesEl = this.el.select('.mas-boxes').first();
33358         
33359         return this.boxesEl;
33360     },
33361     
33362     
33363     initEvents : function()
33364     {
33365         var _this = this;
33366         
33367         if(this.isAutoInitial){
33368             Roo.log('hook children rendered');
33369             this.on('childrenrendered', function() {
33370                 Roo.log('children rendered');
33371                 _this.initial();
33372             } ,this);
33373         }
33374     },
33375     
33376     initial : function()
33377     {
33378         this.selectedBrick = [];
33379         
33380         this.currentSize = this.el.getBox(true);
33381         
33382         Roo.EventManager.onWindowResize(this.resize, this); 
33383
33384         if(!this.isAutoInitial){
33385             this.layout();
33386             return;
33387         }
33388         
33389         this.layout();
33390         
33391         return;
33392         //this.layout.defer(500,this);
33393         
33394     },
33395     
33396     resize : function()
33397     {
33398         var cs = this.el.getBox(true);
33399         
33400         if (
33401                 this.currentSize.width == cs.width && 
33402                 this.currentSize.x == cs.x && 
33403                 this.currentSize.height == cs.height && 
33404                 this.currentSize.y == cs.y 
33405         ) {
33406             Roo.log("no change in with or X or Y");
33407             return;
33408         }
33409         
33410         this.currentSize = cs;
33411         
33412         this.layout();
33413         
33414     },
33415     
33416     layout : function()
33417     {   
33418         this._resetLayout();
33419         
33420         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33421         
33422         this.layoutItems( isInstant );
33423       
33424         this._isLayoutInited = true;
33425         
33426         this.fireEvent('layout', this);
33427         
33428     },
33429     
33430     _resetLayout : function()
33431     {
33432         if(this.isHorizontal){
33433             this.horizontalMeasureColumns();
33434             return;
33435         }
33436         
33437         this.verticalMeasureColumns();
33438         
33439     },
33440     
33441     verticalMeasureColumns : function()
33442     {
33443         this.getContainerWidth();
33444         
33445 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33446 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33447 //            return;
33448 //        }
33449         
33450         var boxWidth = this.boxWidth + this.padWidth;
33451         
33452         if(this.containerWidth < this.boxWidth){
33453             boxWidth = this.containerWidth
33454         }
33455         
33456         var containerWidth = this.containerWidth;
33457         
33458         var cols = Math.floor(containerWidth / boxWidth);
33459         
33460         this.cols = Math.max( cols, 1 );
33461         
33462         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33463         
33464         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33465         
33466         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33467         
33468         this.colWidth = boxWidth + avail - this.padWidth;
33469         
33470         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33471         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33472     },
33473     
33474     horizontalMeasureColumns : function()
33475     {
33476         this.getContainerWidth();
33477         
33478         var boxWidth = this.boxWidth;
33479         
33480         if(this.containerWidth < boxWidth){
33481             boxWidth = this.containerWidth;
33482         }
33483         
33484         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33485         
33486         this.el.setHeight(boxWidth);
33487         
33488     },
33489     
33490     getContainerWidth : function()
33491     {
33492         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33493     },
33494     
33495     layoutItems : function( isInstant )
33496     {
33497         Roo.log(this.bricks);
33498         
33499         var items = Roo.apply([], this.bricks);
33500         
33501         if(this.isHorizontal){
33502             this._horizontalLayoutItems( items , isInstant );
33503             return;
33504         }
33505         
33506 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33507 //            this._verticalAlternativeLayoutItems( items , isInstant );
33508 //            return;
33509 //        }
33510         
33511         this._verticalLayoutItems( items , isInstant );
33512         
33513     },
33514     
33515     _verticalLayoutItems : function ( items , isInstant)
33516     {
33517         if ( !items || !items.length ) {
33518             return;
33519         }
33520         
33521         var standard = [
33522             ['xs', 'xs', 'xs', 'tall'],
33523             ['xs', 'xs', 'tall'],
33524             ['xs', 'xs', 'sm'],
33525             ['xs', 'xs', 'xs'],
33526             ['xs', 'tall'],
33527             ['xs', 'sm'],
33528             ['xs', 'xs'],
33529             ['xs'],
33530             
33531             ['sm', 'xs', 'xs'],
33532             ['sm', 'xs'],
33533             ['sm'],
33534             
33535             ['tall', 'xs', 'xs', 'xs'],
33536             ['tall', 'xs', 'xs'],
33537             ['tall', 'xs'],
33538             ['tall']
33539             
33540         ];
33541         
33542         var queue = [];
33543         
33544         var boxes = [];
33545         
33546         var box = [];
33547         
33548         Roo.each(items, function(item, k){
33549             
33550             switch (item.size) {
33551                 // these layouts take up a full box,
33552                 case 'md' :
33553                 case 'md-left' :
33554                 case 'md-right' :
33555                 case 'wide' :
33556                     
33557                     if(box.length){
33558                         boxes.push(box);
33559                         box = [];
33560                     }
33561                     
33562                     boxes.push([item]);
33563                     
33564                     break;
33565                     
33566                 case 'xs' :
33567                 case 'sm' :
33568                 case 'tall' :
33569                     
33570                     box.push(item);
33571                     
33572                     break;
33573                 default :
33574                     break;
33575                     
33576             }
33577             
33578         }, this);
33579         
33580         if(box.length){
33581             boxes.push(box);
33582             box = [];
33583         }
33584         
33585         var filterPattern = function(box, length)
33586         {
33587             if(!box.length){
33588                 return;
33589             }
33590             
33591             var match = false;
33592             
33593             var pattern = box.slice(0, length);
33594             
33595             var format = [];
33596             
33597             Roo.each(pattern, function(i){
33598                 format.push(i.size);
33599             }, this);
33600             
33601             Roo.each(standard, function(s){
33602                 
33603                 if(String(s) != String(format)){
33604                     return;
33605                 }
33606                 
33607                 match = true;
33608                 return false;
33609                 
33610             }, this);
33611             
33612             if(!match && length == 1){
33613                 return;
33614             }
33615             
33616             if(!match){
33617                 filterPattern(box, length - 1);
33618                 return;
33619             }
33620                 
33621             queue.push(pattern);
33622
33623             box = box.slice(length, box.length);
33624
33625             filterPattern(box, 4);
33626
33627             return;
33628             
33629         }
33630         
33631         Roo.each(boxes, function(box, k){
33632             
33633             if(!box.length){
33634                 return;
33635             }
33636             
33637             if(box.length == 1){
33638                 queue.push(box);
33639                 return;
33640             }
33641             
33642             filterPattern(box, 4);
33643             
33644         }, this);
33645         
33646         this._processVerticalLayoutQueue( queue, isInstant );
33647         
33648     },
33649     
33650 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33651 //    {
33652 //        if ( !items || !items.length ) {
33653 //            return;
33654 //        }
33655 //
33656 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33657 //        
33658 //    },
33659     
33660     _horizontalLayoutItems : function ( items , isInstant)
33661     {
33662         if ( !items || !items.length || items.length < 3) {
33663             return;
33664         }
33665         
33666         items.reverse();
33667         
33668         var eItems = items.slice(0, 3);
33669         
33670         items = items.slice(3, items.length);
33671         
33672         var standard = [
33673             ['xs', 'xs', 'xs', 'wide'],
33674             ['xs', 'xs', 'wide'],
33675             ['xs', 'xs', 'sm'],
33676             ['xs', 'xs', 'xs'],
33677             ['xs', 'wide'],
33678             ['xs', 'sm'],
33679             ['xs', 'xs'],
33680             ['xs'],
33681             
33682             ['sm', 'xs', 'xs'],
33683             ['sm', 'xs'],
33684             ['sm'],
33685             
33686             ['wide', 'xs', 'xs', 'xs'],
33687             ['wide', 'xs', 'xs'],
33688             ['wide', 'xs'],
33689             ['wide'],
33690             
33691             ['wide-thin']
33692         ];
33693         
33694         var queue = [];
33695         
33696         var boxes = [];
33697         
33698         var box = [];
33699         
33700         Roo.each(items, function(item, k){
33701             
33702             switch (item.size) {
33703                 case 'md' :
33704                 case 'md-left' :
33705                 case 'md-right' :
33706                 case 'tall' :
33707                     
33708                     if(box.length){
33709                         boxes.push(box);
33710                         box = [];
33711                     }
33712                     
33713                     boxes.push([item]);
33714                     
33715                     break;
33716                     
33717                 case 'xs' :
33718                 case 'sm' :
33719                 case 'wide' :
33720                 case 'wide-thin' :
33721                     
33722                     box.push(item);
33723                     
33724                     break;
33725                 default :
33726                     break;
33727                     
33728             }
33729             
33730         }, this);
33731         
33732         if(box.length){
33733             boxes.push(box);
33734             box = [];
33735         }
33736         
33737         var filterPattern = function(box, length)
33738         {
33739             if(!box.length){
33740                 return;
33741             }
33742             
33743             var match = false;
33744             
33745             var pattern = box.slice(0, length);
33746             
33747             var format = [];
33748             
33749             Roo.each(pattern, function(i){
33750                 format.push(i.size);
33751             }, this);
33752             
33753             Roo.each(standard, function(s){
33754                 
33755                 if(String(s) != String(format)){
33756                     return;
33757                 }
33758                 
33759                 match = true;
33760                 return false;
33761                 
33762             }, this);
33763             
33764             if(!match && length == 1){
33765                 return;
33766             }
33767             
33768             if(!match){
33769                 filterPattern(box, length - 1);
33770                 return;
33771             }
33772                 
33773             queue.push(pattern);
33774
33775             box = box.slice(length, box.length);
33776
33777             filterPattern(box, 4);
33778
33779             return;
33780             
33781         }
33782         
33783         Roo.each(boxes, function(box, k){
33784             
33785             if(!box.length){
33786                 return;
33787             }
33788             
33789             if(box.length == 1){
33790                 queue.push(box);
33791                 return;
33792             }
33793             
33794             filterPattern(box, 4);
33795             
33796         }, this);
33797         
33798         
33799         var prune = [];
33800         
33801         var pos = this.el.getBox(true);
33802         
33803         var minX = pos.x;
33804         
33805         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33806         
33807         var hit_end = false;
33808         
33809         Roo.each(queue, function(box){
33810             
33811             if(hit_end){
33812                 
33813                 Roo.each(box, function(b){
33814                 
33815                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33816                     b.el.hide();
33817
33818                 }, this);
33819
33820                 return;
33821             }
33822             
33823             var mx = 0;
33824             
33825             Roo.each(box, function(b){
33826                 
33827                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33828                 b.el.show();
33829
33830                 mx = Math.max(mx, b.x);
33831                 
33832             }, this);
33833             
33834             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33835             
33836             if(maxX < minX){
33837                 
33838                 Roo.each(box, function(b){
33839                 
33840                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33841                     b.el.hide();
33842                     
33843                 }, this);
33844                 
33845                 hit_end = true;
33846                 
33847                 return;
33848             }
33849             
33850             prune.push(box);
33851             
33852         }, this);
33853         
33854         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33855     },
33856     
33857     /** Sets position of item in DOM
33858     * @param {Element} item
33859     * @param {Number} x - horizontal position
33860     * @param {Number} y - vertical position
33861     * @param {Boolean} isInstant - disables transitions
33862     */
33863     _processVerticalLayoutQueue : function( queue, isInstant )
33864     {
33865         var pos = this.el.getBox(true);
33866         var x = pos.x;
33867         var y = pos.y;
33868         var maxY = [];
33869         
33870         for (var i = 0; i < this.cols; i++){
33871             maxY[i] = pos.y;
33872         }
33873         
33874         Roo.each(queue, function(box, k){
33875             
33876             var col = k % this.cols;
33877             
33878             Roo.each(box, function(b,kk){
33879                 
33880                 b.el.position('absolute');
33881                 
33882                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33883                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33884                 
33885                 if(b.size == 'md-left' || b.size == 'md-right'){
33886                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33887                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33888                 }
33889                 
33890                 b.el.setWidth(width);
33891                 b.el.setHeight(height);
33892                 // iframe?
33893                 b.el.select('iframe',true).setSize(width,height);
33894                 
33895             }, this);
33896             
33897             for (var i = 0; i < this.cols; i++){
33898                 
33899                 if(maxY[i] < maxY[col]){
33900                     col = i;
33901                     continue;
33902                 }
33903                 
33904                 col = Math.min(col, i);
33905                 
33906             }
33907             
33908             x = pos.x + col * (this.colWidth + this.padWidth);
33909             
33910             y = maxY[col];
33911             
33912             var positions = [];
33913             
33914             switch (box.length){
33915                 case 1 :
33916                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33917                     break;
33918                 case 2 :
33919                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33920                     break;
33921                 case 3 :
33922                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33923                     break;
33924                 case 4 :
33925                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33926                     break;
33927                 default :
33928                     break;
33929             }
33930             
33931             Roo.each(box, function(b,kk){
33932                 
33933                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33934                 
33935                 var sz = b.el.getSize();
33936                 
33937                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33938                 
33939             }, this);
33940             
33941         }, this);
33942         
33943         var mY = 0;
33944         
33945         for (var i = 0; i < this.cols; i++){
33946             mY = Math.max(mY, maxY[i]);
33947         }
33948         
33949         this.el.setHeight(mY - pos.y);
33950         
33951     },
33952     
33953 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33954 //    {
33955 //        var pos = this.el.getBox(true);
33956 //        var x = pos.x;
33957 //        var y = pos.y;
33958 //        var maxX = pos.right;
33959 //        
33960 //        var maxHeight = 0;
33961 //        
33962 //        Roo.each(items, function(item, k){
33963 //            
33964 //            var c = k % 2;
33965 //            
33966 //            item.el.position('absolute');
33967 //                
33968 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33969 //
33970 //            item.el.setWidth(width);
33971 //
33972 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33973 //
33974 //            item.el.setHeight(height);
33975 //            
33976 //            if(c == 0){
33977 //                item.el.setXY([x, y], isInstant ? false : true);
33978 //            } else {
33979 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33980 //            }
33981 //            
33982 //            y = y + height + this.alternativePadWidth;
33983 //            
33984 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33985 //            
33986 //        }, this);
33987 //        
33988 //        this.el.setHeight(maxHeight);
33989 //        
33990 //    },
33991     
33992     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33993     {
33994         var pos = this.el.getBox(true);
33995         
33996         var minX = pos.x;
33997         var minY = pos.y;
33998         
33999         var maxX = pos.right;
34000         
34001         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34002         
34003         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34004         
34005         Roo.each(queue, function(box, k){
34006             
34007             Roo.each(box, function(b, kk){
34008                 
34009                 b.el.position('absolute');
34010                 
34011                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34012                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34013                 
34014                 if(b.size == 'md-left' || b.size == 'md-right'){
34015                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34016                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34017                 }
34018                 
34019                 b.el.setWidth(width);
34020                 b.el.setHeight(height);
34021                 
34022             }, this);
34023             
34024             if(!box.length){
34025                 return;
34026             }
34027             
34028             var positions = [];
34029             
34030             switch (box.length){
34031                 case 1 :
34032                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34033                     break;
34034                 case 2 :
34035                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34036                     break;
34037                 case 3 :
34038                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34039                     break;
34040                 case 4 :
34041                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34042                     break;
34043                 default :
34044                     break;
34045             }
34046             
34047             Roo.each(box, function(b,kk){
34048                 
34049                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34050                 
34051                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34052                 
34053             }, this);
34054             
34055         }, this);
34056         
34057     },
34058     
34059     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34060     {
34061         Roo.each(eItems, function(b,k){
34062             
34063             b.size = (k == 0) ? 'sm' : 'xs';
34064             b.x = (k == 0) ? 2 : 1;
34065             b.y = (k == 0) ? 2 : 1;
34066             
34067             b.el.position('absolute');
34068             
34069             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34070                 
34071             b.el.setWidth(width);
34072             
34073             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34074             
34075             b.el.setHeight(height);
34076             
34077         }, this);
34078
34079         var positions = [];
34080         
34081         positions.push({
34082             x : maxX - this.unitWidth * 2 - this.gutter,
34083             y : minY
34084         });
34085         
34086         positions.push({
34087             x : maxX - this.unitWidth,
34088             y : minY + (this.unitWidth + this.gutter) * 2
34089         });
34090         
34091         positions.push({
34092             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34093             y : minY
34094         });
34095         
34096         Roo.each(eItems, function(b,k){
34097             
34098             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34099
34100         }, this);
34101         
34102     },
34103     
34104     getVerticalOneBoxColPositions : function(x, y, box)
34105     {
34106         var pos = [];
34107         
34108         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34109         
34110         if(box[0].size == 'md-left'){
34111             rand = 0;
34112         }
34113         
34114         if(box[0].size == 'md-right'){
34115             rand = 1;
34116         }
34117         
34118         pos.push({
34119             x : x + (this.unitWidth + this.gutter) * rand,
34120             y : y
34121         });
34122         
34123         return pos;
34124     },
34125     
34126     getVerticalTwoBoxColPositions : function(x, y, box)
34127     {
34128         var pos = [];
34129         
34130         if(box[0].size == 'xs'){
34131             
34132             pos.push({
34133                 x : x,
34134                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34135             });
34136
34137             pos.push({
34138                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34139                 y : y
34140             });
34141             
34142             return pos;
34143             
34144         }
34145         
34146         pos.push({
34147             x : x,
34148             y : y
34149         });
34150
34151         pos.push({
34152             x : x + (this.unitWidth + this.gutter) * 2,
34153             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34154         });
34155         
34156         return pos;
34157         
34158     },
34159     
34160     getVerticalThreeBoxColPositions : function(x, y, box)
34161     {
34162         var pos = [];
34163         
34164         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34165             
34166             pos.push({
34167                 x : x,
34168                 y : y
34169             });
34170
34171             pos.push({
34172                 x : x + (this.unitWidth + this.gutter) * 1,
34173                 y : y
34174             });
34175             
34176             pos.push({
34177                 x : x + (this.unitWidth + this.gutter) * 2,
34178                 y : y
34179             });
34180             
34181             return pos;
34182             
34183         }
34184         
34185         if(box[0].size == 'xs' && box[1].size == 'xs'){
34186             
34187             pos.push({
34188                 x : x,
34189                 y : y
34190             });
34191
34192             pos.push({
34193                 x : x,
34194                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34195             });
34196             
34197             pos.push({
34198                 x : x + (this.unitWidth + this.gutter) * 1,
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
34214         });
34215
34216         pos.push({
34217             x : x + (this.unitWidth + this.gutter) * 2,
34218             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34219         });
34220             
34221         return pos;
34222         
34223     },
34224     
34225     getVerticalFourBoxColPositions : function(x, y, box)
34226     {
34227         var pos = [];
34228         
34229         if(box[0].size == 'xs'){
34230             
34231             pos.push({
34232                 x : x,
34233                 y : y
34234             });
34235
34236             pos.push({
34237                 x : x,
34238                 y : y + (this.unitHeight + this.gutter) * 1
34239             });
34240             
34241             pos.push({
34242                 x : x,
34243                 y : y + (this.unitHeight + this.gutter) * 2
34244             });
34245             
34246             pos.push({
34247                 x : x + (this.unitWidth + this.gutter) * 1,
34248                 y : y
34249             });
34250             
34251             return pos;
34252             
34253         }
34254         
34255         pos.push({
34256             x : x,
34257             y : y
34258         });
34259
34260         pos.push({
34261             x : x + (this.unitWidth + this.gutter) * 2,
34262             y : y
34263         });
34264
34265         pos.push({
34266             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34267             y : y + (this.unitHeight + this.gutter) * 1
34268         });
34269
34270         pos.push({
34271             x : x + (this.unitWidth + this.gutter) * 2,
34272             y : y + (this.unitWidth + this.gutter) * 2
34273         });
34274
34275         return pos;
34276         
34277     },
34278     
34279     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34280     {
34281         var pos = [];
34282         
34283         if(box[0].size == 'md-left'){
34284             pos.push({
34285                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34286                 y : minY
34287             });
34288             
34289             return pos;
34290         }
34291         
34292         if(box[0].size == 'md-right'){
34293             pos.push({
34294                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34295                 y : minY + (this.unitWidth + this.gutter) * 1
34296             });
34297             
34298             return pos;
34299         }
34300         
34301         var rand = Math.floor(Math.random() * (4 - box[0].y));
34302         
34303         pos.push({
34304             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34305             y : minY + (this.unitWidth + this.gutter) * rand
34306         });
34307         
34308         return pos;
34309         
34310     },
34311     
34312     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34313     {
34314         var pos = [];
34315         
34316         if(box[0].size == 'xs'){
34317             
34318             pos.push({
34319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34320                 y : minY
34321             });
34322
34323             pos.push({
34324                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34325                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34326             });
34327             
34328             return pos;
34329             
34330         }
34331         
34332         pos.push({
34333             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34334             y : minY
34335         });
34336
34337         pos.push({
34338             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34339             y : minY + (this.unitWidth + this.gutter) * 2
34340         });
34341         
34342         return pos;
34343         
34344     },
34345     
34346     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34347     {
34348         var pos = [];
34349         
34350         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34351             
34352             pos.push({
34353                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34354                 y : minY
34355             });
34356
34357             pos.push({
34358                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34359                 y : minY + (this.unitWidth + this.gutter) * 1
34360             });
34361             
34362             pos.push({
34363                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34364                 y : minY + (this.unitWidth + this.gutter) * 2
34365             });
34366             
34367             return pos;
34368             
34369         }
34370         
34371         if(box[0].size == 'xs' && box[1].size == 'xs'){
34372             
34373             pos.push({
34374                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34375                 y : minY
34376             });
34377
34378             pos.push({
34379                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34380                 y : minY
34381             });
34382             
34383             pos.push({
34384                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34385                 y : minY + (this.unitWidth + this.gutter) * 1
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         pos.push({
34403             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34404             y : minY + (this.unitWidth + this.gutter) * 2
34405         });
34406             
34407         return pos;
34408         
34409     },
34410     
34411     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34412     {
34413         var pos = [];
34414         
34415         if(box[0].size == 'xs'){
34416             
34417             pos.push({
34418                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34419                 y : minY
34420             });
34421
34422             pos.push({
34423                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34424                 y : minY
34425             });
34426             
34427             pos.push({
34428                 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),
34429                 y : minY
34430             });
34431             
34432             pos.push({
34433                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34434                 y : minY + (this.unitWidth + this.gutter) * 1
34435             });
34436             
34437             return pos;
34438             
34439         }
34440         
34441         pos.push({
34442             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34443             y : minY
34444         });
34445         
34446         pos.push({
34447             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34448             y : minY + (this.unitWidth + this.gutter) * 2
34449         });
34450         
34451         pos.push({
34452             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34453             y : minY + (this.unitWidth + this.gutter) * 2
34454         });
34455         
34456         pos.push({
34457             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),
34458             y : minY + (this.unitWidth + this.gutter) * 2
34459         });
34460
34461         return pos;
34462         
34463     },
34464     
34465     /**
34466     * remove a Masonry Brick
34467     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34468     */
34469     removeBrick : function(brick_id)
34470     {
34471         if (!brick_id) {
34472             return;
34473         }
34474         
34475         for (var i = 0; i<this.bricks.length; i++) {
34476             if (this.bricks[i].id == brick_id) {
34477                 this.bricks.splice(i,1);
34478                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34479                 this.initial();
34480             }
34481         }
34482     },
34483     
34484     /**
34485     * adds a Masonry Brick
34486     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34487     */
34488     addBrick : function(cfg)
34489     {
34490         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34491         //this.register(cn);
34492         cn.parentId = this.id;
34493         cn.render(this.el);
34494         return cn;
34495     },
34496     
34497     /**
34498     * register a Masonry Brick
34499     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34500     */
34501     
34502     register : function(brick)
34503     {
34504         this.bricks.push(brick);
34505         brick.masonryId = this.id;
34506     },
34507     
34508     /**
34509     * clear all the Masonry Brick
34510     */
34511     clearAll : function()
34512     {
34513         this.bricks = [];
34514         //this.getChildContainer().dom.innerHTML = "";
34515         this.el.dom.innerHTML = '';
34516     },
34517     
34518     getSelected : function()
34519     {
34520         if (!this.selectedBrick) {
34521             return false;
34522         }
34523         
34524         return this.selectedBrick;
34525     }
34526 });
34527
34528 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34529     
34530     groups: {},
34531      /**
34532     * register a Masonry Layout
34533     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34534     */
34535     
34536     register : function(layout)
34537     {
34538         this.groups[layout.id] = layout;
34539     },
34540     /**
34541     * fetch a  Masonry Layout based on the masonry layout ID
34542     * @param {string} the masonry layout to add
34543     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34544     */
34545     
34546     get: function(layout_id) {
34547         if (typeof(this.groups[layout_id]) == 'undefined') {
34548             return false;
34549         }
34550         return this.groups[layout_id] ;
34551     }
34552     
34553     
34554     
34555 });
34556
34557  
34558
34559  /**
34560  *
34561  * This is based on 
34562  * http://masonry.desandro.com
34563  *
34564  * The idea is to render all the bricks based on vertical width...
34565  *
34566  * The original code extends 'outlayer' - we might need to use that....
34567  * 
34568  */
34569
34570
34571 /**
34572  * @class Roo.bootstrap.LayoutMasonryAuto
34573  * @extends Roo.bootstrap.Component
34574  * Bootstrap Layout Masonry class
34575  * 
34576  * @constructor
34577  * Create a new Element
34578  * @param {Object} config The config object
34579  */
34580
34581 Roo.bootstrap.LayoutMasonryAuto = function(config){
34582     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34583 };
34584
34585 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34586     
34587       /**
34588      * @cfg {Boolean} isFitWidth  - resize the width..
34589      */   
34590     isFitWidth : false,  // options..
34591     /**
34592      * @cfg {Boolean} isOriginLeft = left align?
34593      */   
34594     isOriginLeft : true,
34595     /**
34596      * @cfg {Boolean} isOriginTop = top align?
34597      */   
34598     isOriginTop : false,
34599     /**
34600      * @cfg {Boolean} isLayoutInstant = no animation?
34601      */   
34602     isLayoutInstant : false, // needed?
34603     /**
34604      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34605      */   
34606     isResizingContainer : true,
34607     /**
34608      * @cfg {Number} columnWidth  width of the columns 
34609      */   
34610     
34611     columnWidth : 0,
34612     
34613     /**
34614      * @cfg {Number} maxCols maximum number of columns
34615      */   
34616     
34617     maxCols: 0,
34618     /**
34619      * @cfg {Number} padHeight padding below box..
34620      */   
34621     
34622     padHeight : 10, 
34623     
34624     /**
34625      * @cfg {Boolean} isAutoInitial defalut true
34626      */   
34627     
34628     isAutoInitial : true, 
34629     
34630     // private?
34631     gutter : 0,
34632     
34633     containerWidth: 0,
34634     initialColumnWidth : 0,
34635     currentSize : null,
34636     
34637     colYs : null, // array.
34638     maxY : 0,
34639     padWidth: 10,
34640     
34641     
34642     tag: 'div',
34643     cls: '',
34644     bricks: null, //CompositeElement
34645     cols : 0, // array?
34646     // element : null, // wrapped now this.el
34647     _isLayoutInited : null, 
34648     
34649     
34650     getAutoCreate : function(){
34651         
34652         var cfg = {
34653             tag: this.tag,
34654             cls: 'blog-masonary-wrapper ' + this.cls,
34655             cn : {
34656                 cls : 'mas-boxes masonary'
34657             }
34658         };
34659         
34660         return cfg;
34661     },
34662     
34663     getChildContainer: function( )
34664     {
34665         if (this.boxesEl) {
34666             return this.boxesEl;
34667         }
34668         
34669         this.boxesEl = this.el.select('.mas-boxes').first();
34670         
34671         return this.boxesEl;
34672     },
34673     
34674     
34675     initEvents : function()
34676     {
34677         var _this = this;
34678         
34679         if(this.isAutoInitial){
34680             Roo.log('hook children rendered');
34681             this.on('childrenrendered', function() {
34682                 Roo.log('children rendered');
34683                 _this.initial();
34684             } ,this);
34685         }
34686         
34687     },
34688     
34689     initial : function()
34690     {
34691         this.reloadItems();
34692
34693         this.currentSize = this.el.getBox(true);
34694
34695         /// was window resize... - let's see if this works..
34696         Roo.EventManager.onWindowResize(this.resize, this); 
34697
34698         if(!this.isAutoInitial){
34699             this.layout();
34700             return;
34701         }
34702         
34703         this.layout.defer(500,this);
34704     },
34705     
34706     reloadItems: function()
34707     {
34708         this.bricks = this.el.select('.masonry-brick', true);
34709         
34710         this.bricks.each(function(b) {
34711             //Roo.log(b.getSize());
34712             if (!b.attr('originalwidth')) {
34713                 b.attr('originalwidth',  b.getSize().width);
34714             }
34715             
34716         });
34717         
34718         Roo.log(this.bricks.elements.length);
34719     },
34720     
34721     resize : function()
34722     {
34723         Roo.log('resize');
34724         var cs = this.el.getBox(true);
34725         
34726         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34727             Roo.log("no change in with or X");
34728             return;
34729         }
34730         this.currentSize = cs;
34731         this.layout();
34732     },
34733     
34734     layout : function()
34735     {
34736          Roo.log('layout');
34737         this._resetLayout();
34738         //this._manageStamps();
34739       
34740         // don't animate first layout
34741         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34742         this.layoutItems( isInstant );
34743       
34744         // flag for initalized
34745         this._isLayoutInited = true;
34746     },
34747     
34748     layoutItems : function( isInstant )
34749     {
34750         //var items = this._getItemsForLayout( this.items );
34751         // original code supports filtering layout items.. we just ignore it..
34752         
34753         this._layoutItems( this.bricks , isInstant );
34754       
34755         this._postLayout();
34756     },
34757     _layoutItems : function ( items , isInstant)
34758     {
34759        //this.fireEvent( 'layout', this, items );
34760     
34761
34762         if ( !items || !items.elements.length ) {
34763           // no items, emit event with empty array
34764             return;
34765         }
34766
34767         var queue = [];
34768         items.each(function(item) {
34769             Roo.log("layout item");
34770             Roo.log(item);
34771             // get x/y object from method
34772             var position = this._getItemLayoutPosition( item );
34773             // enqueue
34774             position.item = item;
34775             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34776             queue.push( position );
34777         }, this);
34778       
34779         this._processLayoutQueue( queue );
34780     },
34781     /** Sets position of item in DOM
34782     * @param {Element} item
34783     * @param {Number} x - horizontal position
34784     * @param {Number} y - vertical position
34785     * @param {Boolean} isInstant - disables transitions
34786     */
34787     _processLayoutQueue : function( queue )
34788     {
34789         for ( var i=0, len = queue.length; i < len; i++ ) {
34790             var obj = queue[i];
34791             obj.item.position('absolute');
34792             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34793         }
34794     },
34795       
34796     
34797     /**
34798     * Any logic you want to do after each layout,
34799     * i.e. size the container
34800     */
34801     _postLayout : function()
34802     {
34803         this.resizeContainer();
34804     },
34805     
34806     resizeContainer : function()
34807     {
34808         if ( !this.isResizingContainer ) {
34809             return;
34810         }
34811         var size = this._getContainerSize();
34812         if ( size ) {
34813             this.el.setSize(size.width,size.height);
34814             this.boxesEl.setSize(size.width,size.height);
34815         }
34816     },
34817     
34818     
34819     
34820     _resetLayout : function()
34821     {
34822         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34823         this.colWidth = this.el.getWidth();
34824         //this.gutter = this.el.getWidth(); 
34825         
34826         this.measureColumns();
34827
34828         // reset column Y
34829         var i = this.cols;
34830         this.colYs = [];
34831         while (i--) {
34832             this.colYs.push( 0 );
34833         }
34834     
34835         this.maxY = 0;
34836     },
34837
34838     measureColumns : function()
34839     {
34840         this.getContainerWidth();
34841       // if columnWidth is 0, default to outerWidth of first item
34842         if ( !this.columnWidth ) {
34843             var firstItem = this.bricks.first();
34844             Roo.log(firstItem);
34845             this.columnWidth  = this.containerWidth;
34846             if (firstItem && firstItem.attr('originalwidth') ) {
34847                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34848             }
34849             // columnWidth fall back to item of first element
34850             Roo.log("set column width?");
34851                         this.initialColumnWidth = this.columnWidth  ;
34852
34853             // if first elem has no width, default to size of container
34854             
34855         }
34856         
34857         
34858         if (this.initialColumnWidth) {
34859             this.columnWidth = this.initialColumnWidth;
34860         }
34861         
34862         
34863             
34864         // column width is fixed at the top - however if container width get's smaller we should
34865         // reduce it...
34866         
34867         // this bit calcs how man columns..
34868             
34869         var columnWidth = this.columnWidth += this.gutter;
34870       
34871         // calculate columns
34872         var containerWidth = this.containerWidth + this.gutter;
34873         
34874         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34875         // fix rounding errors, typically with gutters
34876         var excess = columnWidth - containerWidth % columnWidth;
34877         
34878         
34879         // if overshoot is less than a pixel, round up, otherwise floor it
34880         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34881         cols = Math[ mathMethod ]( cols );
34882         this.cols = Math.max( cols, 1 );
34883         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34884         
34885          // padding positioning..
34886         var totalColWidth = this.cols * this.columnWidth;
34887         var padavail = this.containerWidth - totalColWidth;
34888         // so for 2 columns - we need 3 'pads'
34889         
34890         var padNeeded = (1+this.cols) * this.padWidth;
34891         
34892         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34893         
34894         this.columnWidth += padExtra
34895         //this.padWidth = Math.floor(padavail /  ( this.cols));
34896         
34897         // adjust colum width so that padding is fixed??
34898         
34899         // we have 3 columns ... total = width * 3
34900         // we have X left over... that should be used by 
34901         
34902         //if (this.expandC) {
34903             
34904         //}
34905         
34906         
34907         
34908     },
34909     
34910     getContainerWidth : function()
34911     {
34912        /* // container is parent if fit width
34913         var container = this.isFitWidth ? this.element.parentNode : this.element;
34914         // check that this.size and size are there
34915         // IE8 triggers resize on body size change, so they might not be
34916         
34917         var size = getSize( container );  //FIXME
34918         this.containerWidth = size && size.innerWidth; //FIXME
34919         */
34920          
34921         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34922         
34923     },
34924     
34925     _getItemLayoutPosition : function( item )  // what is item?
34926     {
34927         // we resize the item to our columnWidth..
34928       
34929         item.setWidth(this.columnWidth);
34930         item.autoBoxAdjust  = false;
34931         
34932         var sz = item.getSize();
34933  
34934         // how many columns does this brick span
34935         var remainder = this.containerWidth % this.columnWidth;
34936         
34937         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34938         // round if off by 1 pixel, otherwise use ceil
34939         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34940         colSpan = Math.min( colSpan, this.cols );
34941         
34942         // normally this should be '1' as we dont' currently allow multi width columns..
34943         
34944         var colGroup = this._getColGroup( colSpan );
34945         // get the minimum Y value from the columns
34946         var minimumY = Math.min.apply( Math, colGroup );
34947         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34948         
34949         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34950          
34951         // position the brick
34952         var position = {
34953             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34954             y: this.currentSize.y + minimumY + this.padHeight
34955         };
34956         
34957         Roo.log(position);
34958         // apply setHeight to necessary columns
34959         var setHeight = minimumY + sz.height + this.padHeight;
34960         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34961         
34962         var setSpan = this.cols + 1 - colGroup.length;
34963         for ( var i = 0; i < setSpan; i++ ) {
34964           this.colYs[ shortColIndex + i ] = setHeight ;
34965         }
34966       
34967         return position;
34968     },
34969     
34970     /**
34971      * @param {Number} colSpan - number of columns the element spans
34972      * @returns {Array} colGroup
34973      */
34974     _getColGroup : function( colSpan )
34975     {
34976         if ( colSpan < 2 ) {
34977           // if brick spans only one column, use all the column Ys
34978           return this.colYs;
34979         }
34980       
34981         var colGroup = [];
34982         // how many different places could this brick fit horizontally
34983         var groupCount = this.cols + 1 - colSpan;
34984         // for each group potential horizontal position
34985         for ( var i = 0; i < groupCount; i++ ) {
34986           // make an array of colY values for that one group
34987           var groupColYs = this.colYs.slice( i, i + colSpan );
34988           // and get the max value of the array
34989           colGroup[i] = Math.max.apply( Math, groupColYs );
34990         }
34991         return colGroup;
34992     },
34993     /*
34994     _manageStamp : function( stamp )
34995     {
34996         var stampSize =  stamp.getSize();
34997         var offset = stamp.getBox();
34998         // get the columns that this stamp affects
34999         var firstX = this.isOriginLeft ? offset.x : offset.right;
35000         var lastX = firstX + stampSize.width;
35001         var firstCol = Math.floor( firstX / this.columnWidth );
35002         firstCol = Math.max( 0, firstCol );
35003         
35004         var lastCol = Math.floor( lastX / this.columnWidth );
35005         // lastCol should not go over if multiple of columnWidth #425
35006         lastCol -= lastX % this.columnWidth ? 0 : 1;
35007         lastCol = Math.min( this.cols - 1, lastCol );
35008         
35009         // set colYs to bottom of the stamp
35010         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35011             stampSize.height;
35012             
35013         for ( var i = firstCol; i <= lastCol; i++ ) {
35014           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35015         }
35016     },
35017     */
35018     
35019     _getContainerSize : function()
35020     {
35021         this.maxY = Math.max.apply( Math, this.colYs );
35022         var size = {
35023             height: this.maxY
35024         };
35025       
35026         if ( this.isFitWidth ) {
35027             size.width = this._getContainerFitWidth();
35028         }
35029       
35030         return size;
35031     },
35032     
35033     _getContainerFitWidth : function()
35034     {
35035         var unusedCols = 0;
35036         // count unused columns
35037         var i = this.cols;
35038         while ( --i ) {
35039           if ( this.colYs[i] !== 0 ) {
35040             break;
35041           }
35042           unusedCols++;
35043         }
35044         // fit container to columns that have been used
35045         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35046     },
35047     
35048     needsResizeLayout : function()
35049     {
35050         var previousWidth = this.containerWidth;
35051         this.getContainerWidth();
35052         return previousWidth !== this.containerWidth;
35053     }
35054  
35055 });
35056
35057  
35058
35059  /*
35060  * - LGPL
35061  *
35062  * element
35063  * 
35064  */
35065
35066 /**
35067  * @class Roo.bootstrap.MasonryBrick
35068  * @extends Roo.bootstrap.Component
35069  * Bootstrap MasonryBrick class
35070  * 
35071  * @constructor
35072  * Create a new MasonryBrick
35073  * @param {Object} config The config object
35074  */
35075
35076 Roo.bootstrap.MasonryBrick = function(config){
35077     
35078     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35079     
35080     Roo.bootstrap.MasonryBrick.register(this);
35081     
35082     this.addEvents({
35083         // raw events
35084         /**
35085          * @event click
35086          * When a MasonryBrick is clcik
35087          * @param {Roo.bootstrap.MasonryBrick} this
35088          * @param {Roo.EventObject} e
35089          */
35090         "click" : true
35091     });
35092 };
35093
35094 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35095     
35096     /**
35097      * @cfg {String} title
35098      */   
35099     title : '',
35100     /**
35101      * @cfg {String} html
35102      */   
35103     html : '',
35104     /**
35105      * @cfg {String} bgimage
35106      */   
35107     bgimage : '',
35108     /**
35109      * @cfg {String} videourl
35110      */   
35111     videourl : '',
35112     /**
35113      * @cfg {String} cls
35114      */   
35115     cls : '',
35116     /**
35117      * @cfg {String} href
35118      */   
35119     href : '',
35120     /**
35121      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35122      */   
35123     size : 'xs',
35124     
35125     /**
35126      * @cfg {String} placetitle (center|bottom)
35127      */   
35128     placetitle : '',
35129     
35130     /**
35131      * @cfg {Boolean} isFitContainer defalut true
35132      */   
35133     isFitContainer : true, 
35134     
35135     /**
35136      * @cfg {Boolean} preventDefault defalut false
35137      */   
35138     preventDefault : false, 
35139     
35140     /**
35141      * @cfg {Boolean} inverse defalut false
35142      */   
35143     maskInverse : false, 
35144     
35145     getAutoCreate : function()
35146     {
35147         if(!this.isFitContainer){
35148             return this.getSplitAutoCreate();
35149         }
35150         
35151         var cls = 'masonry-brick masonry-brick-full';
35152         
35153         if(this.href.length){
35154             cls += ' masonry-brick-link';
35155         }
35156         
35157         if(this.bgimage.length){
35158             cls += ' masonry-brick-image';
35159         }
35160         
35161         if(this.maskInverse){
35162             cls += ' mask-inverse';
35163         }
35164         
35165         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35166             cls += ' enable-mask';
35167         }
35168         
35169         if(this.size){
35170             cls += ' masonry-' + this.size + '-brick';
35171         }
35172         
35173         if(this.placetitle.length){
35174             
35175             switch (this.placetitle) {
35176                 case 'center' :
35177                     cls += ' masonry-center-title';
35178                     break;
35179                 case 'bottom' :
35180                     cls += ' masonry-bottom-title';
35181                     break;
35182                 default:
35183                     break;
35184             }
35185             
35186         } else {
35187             if(!this.html.length && !this.bgimage.length){
35188                 cls += ' masonry-center-title';
35189             }
35190
35191             if(!this.html.length && this.bgimage.length){
35192                 cls += ' masonry-bottom-title';
35193             }
35194         }
35195         
35196         if(this.cls){
35197             cls += ' ' + this.cls;
35198         }
35199         
35200         var cfg = {
35201             tag: (this.href.length) ? 'a' : 'div',
35202             cls: cls,
35203             cn: [
35204                 {
35205                     tag: 'div',
35206                     cls: 'masonry-brick-mask'
35207                 },
35208                 {
35209                     tag: 'div',
35210                     cls: 'masonry-brick-paragraph',
35211                     cn: []
35212                 }
35213             ]
35214         };
35215         
35216         if(this.href.length){
35217             cfg.href = this.href;
35218         }
35219         
35220         var cn = cfg.cn[1].cn;
35221         
35222         if(this.title.length){
35223             cn.push({
35224                 tag: 'h4',
35225                 cls: 'masonry-brick-title',
35226                 html: this.title
35227             });
35228         }
35229         
35230         if(this.html.length){
35231             cn.push({
35232                 tag: 'p',
35233                 cls: 'masonry-brick-text',
35234                 html: this.html
35235             });
35236         }
35237         
35238         if (!this.title.length && !this.html.length) {
35239             cfg.cn[1].cls += ' hide';
35240         }
35241         
35242         if(this.bgimage.length){
35243             cfg.cn.push({
35244                 tag: 'img',
35245                 cls: 'masonry-brick-image-view',
35246                 src: this.bgimage
35247             });
35248         }
35249         
35250         if(this.videourl.length){
35251             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35252             // youtube support only?
35253             cfg.cn.push({
35254                 tag: 'iframe',
35255                 cls: 'masonry-brick-image-view',
35256                 src: vurl,
35257                 frameborder : 0,
35258                 allowfullscreen : true
35259             });
35260         }
35261         
35262         return cfg;
35263         
35264     },
35265     
35266     getSplitAutoCreate : function()
35267     {
35268         var cls = 'masonry-brick masonry-brick-split';
35269         
35270         if(this.href.length){
35271             cls += ' masonry-brick-link';
35272         }
35273         
35274         if(this.bgimage.length){
35275             cls += ' masonry-brick-image';
35276         }
35277         
35278         if(this.size){
35279             cls += ' masonry-' + this.size + '-brick';
35280         }
35281         
35282         switch (this.placetitle) {
35283             case 'center' :
35284                 cls += ' masonry-center-title';
35285                 break;
35286             case 'bottom' :
35287                 cls += ' masonry-bottom-title';
35288                 break;
35289             default:
35290                 if(!this.bgimage.length){
35291                     cls += ' masonry-center-title';
35292                 }
35293
35294                 if(this.bgimage.length){
35295                     cls += ' masonry-bottom-title';
35296                 }
35297                 break;
35298         }
35299         
35300         if(this.cls){
35301             cls += ' ' + this.cls;
35302         }
35303         
35304         var cfg = {
35305             tag: (this.href.length) ? 'a' : 'div',
35306             cls: cls,
35307             cn: [
35308                 {
35309                     tag: 'div',
35310                     cls: 'masonry-brick-split-head',
35311                     cn: [
35312                         {
35313                             tag: 'div',
35314                             cls: 'masonry-brick-paragraph',
35315                             cn: []
35316                         }
35317                     ]
35318                 },
35319                 {
35320                     tag: 'div',
35321                     cls: 'masonry-brick-split-body',
35322                     cn: []
35323                 }
35324             ]
35325         };
35326         
35327         if(this.href.length){
35328             cfg.href = this.href;
35329         }
35330         
35331         if(this.title.length){
35332             cfg.cn[0].cn[0].cn.push({
35333                 tag: 'h4',
35334                 cls: 'masonry-brick-title',
35335                 html: this.title
35336             });
35337         }
35338         
35339         if(this.html.length){
35340             cfg.cn[1].cn.push({
35341                 tag: 'p',
35342                 cls: 'masonry-brick-text',
35343                 html: this.html
35344             });
35345         }
35346
35347         if(this.bgimage.length){
35348             cfg.cn[0].cn.push({
35349                 tag: 'img',
35350                 cls: 'masonry-brick-image-view',
35351                 src: this.bgimage
35352             });
35353         }
35354         
35355         if(this.videourl.length){
35356             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35357             // youtube support only?
35358             cfg.cn[0].cn.cn.push({
35359                 tag: 'iframe',
35360                 cls: 'masonry-brick-image-view',
35361                 src: vurl,
35362                 frameborder : 0,
35363                 allowfullscreen : true
35364             });
35365         }
35366         
35367         return cfg;
35368     },
35369     
35370     initEvents: function() 
35371     {
35372         switch (this.size) {
35373             case 'xs' :
35374                 this.x = 1;
35375                 this.y = 1;
35376                 break;
35377             case 'sm' :
35378                 this.x = 2;
35379                 this.y = 2;
35380                 break;
35381             case 'md' :
35382             case 'md-left' :
35383             case 'md-right' :
35384                 this.x = 3;
35385                 this.y = 3;
35386                 break;
35387             case 'tall' :
35388                 this.x = 2;
35389                 this.y = 3;
35390                 break;
35391             case 'wide' :
35392                 this.x = 3;
35393                 this.y = 2;
35394                 break;
35395             case 'wide-thin' :
35396                 this.x = 3;
35397                 this.y = 1;
35398                 break;
35399                         
35400             default :
35401                 break;
35402         }
35403         
35404         if(Roo.isTouch){
35405             this.el.on('touchstart', this.onTouchStart, this);
35406             this.el.on('touchmove', this.onTouchMove, this);
35407             this.el.on('touchend', this.onTouchEnd, this);
35408             this.el.on('contextmenu', this.onContextMenu, this);
35409         } else {
35410             this.el.on('mouseenter'  ,this.enter, this);
35411             this.el.on('mouseleave', this.leave, this);
35412             this.el.on('click', this.onClick, this);
35413         }
35414         
35415         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35416             this.parent().bricks.push(this);   
35417         }
35418         
35419     },
35420     
35421     onClick: function(e, el)
35422     {
35423         var time = this.endTimer - this.startTimer;
35424         // Roo.log(e.preventDefault());
35425         if(Roo.isTouch){
35426             if(time > 1000){
35427                 e.preventDefault();
35428                 return;
35429             }
35430         }
35431         
35432         if(!this.preventDefault){
35433             return;
35434         }
35435         
35436         e.preventDefault();
35437         
35438         if (this.activeClass != '') {
35439             this.selectBrick();
35440         }
35441         
35442         this.fireEvent('click', this, e);
35443     },
35444     
35445     enter: function(e, el)
35446     {
35447         e.preventDefault();
35448         
35449         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35450             return;
35451         }
35452         
35453         if(this.bgimage.length && this.html.length){
35454             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35455         }
35456     },
35457     
35458     leave: function(e, el)
35459     {
35460         e.preventDefault();
35461         
35462         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35463             return;
35464         }
35465         
35466         if(this.bgimage.length && this.html.length){
35467             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35468         }
35469     },
35470     
35471     onTouchStart: function(e, el)
35472     {
35473 //        e.preventDefault();
35474         
35475         this.touchmoved = false;
35476         
35477         if(!this.isFitContainer){
35478             return;
35479         }
35480         
35481         if(!this.bgimage.length || !this.html.length){
35482             return;
35483         }
35484         
35485         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35486         
35487         this.timer = new Date().getTime();
35488         
35489     },
35490     
35491     onTouchMove: function(e, el)
35492     {
35493         this.touchmoved = true;
35494     },
35495     
35496     onContextMenu : function(e,el)
35497     {
35498         e.preventDefault();
35499         e.stopPropagation();
35500         return false;
35501     },
35502     
35503     onTouchEnd: function(e, el)
35504     {
35505 //        e.preventDefault();
35506         
35507         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35508         
35509             this.leave(e,el);
35510             
35511             return;
35512         }
35513         
35514         if(!this.bgimage.length || !this.html.length){
35515             
35516             if(this.href.length){
35517                 window.location.href = this.href;
35518             }
35519             
35520             return;
35521         }
35522         
35523         if(!this.isFitContainer){
35524             return;
35525         }
35526         
35527         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35528         
35529         window.location.href = this.href;
35530     },
35531     
35532     //selection on single brick only
35533     selectBrick : function() {
35534         
35535         if (!this.parentId) {
35536             return;
35537         }
35538         
35539         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35540         var index = m.selectedBrick.indexOf(this.id);
35541         
35542         if ( index > -1) {
35543             m.selectedBrick.splice(index,1);
35544             this.el.removeClass(this.activeClass);
35545             return;
35546         }
35547         
35548         for(var i = 0; i < m.selectedBrick.length; i++) {
35549             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35550             b.el.removeClass(b.activeClass);
35551         }
35552         
35553         m.selectedBrick = [];
35554         
35555         m.selectedBrick.push(this.id);
35556         this.el.addClass(this.activeClass);
35557         return;
35558     },
35559     
35560     isSelected : function(){
35561         return this.el.hasClass(this.activeClass);
35562         
35563     }
35564 });
35565
35566 Roo.apply(Roo.bootstrap.MasonryBrick, {
35567     
35568     //groups: {},
35569     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35570      /**
35571     * register a Masonry Brick
35572     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35573     */
35574     
35575     register : function(brick)
35576     {
35577         //this.groups[brick.id] = brick;
35578         this.groups.add(brick.id, brick);
35579     },
35580     /**
35581     * fetch a  masonry brick based on the masonry brick ID
35582     * @param {string} the masonry brick to add
35583     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35584     */
35585     
35586     get: function(brick_id) 
35587     {
35588         // if (typeof(this.groups[brick_id]) == 'undefined') {
35589         //     return false;
35590         // }
35591         // return this.groups[brick_id] ;
35592         
35593         if(this.groups.key(brick_id)) {
35594             return this.groups.key(brick_id);
35595         }
35596         
35597         return false;
35598     }
35599     
35600     
35601     
35602 });
35603
35604  /*
35605  * - LGPL
35606  *
35607  * element
35608  * 
35609  */
35610
35611 /**
35612  * @class Roo.bootstrap.Brick
35613  * @extends Roo.bootstrap.Component
35614  * Bootstrap Brick class
35615  * 
35616  * @constructor
35617  * Create a new Brick
35618  * @param {Object} config The config object
35619  */
35620
35621 Roo.bootstrap.Brick = function(config){
35622     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35623     
35624     this.addEvents({
35625         // raw events
35626         /**
35627          * @event click
35628          * When a Brick is click
35629          * @param {Roo.bootstrap.Brick} this
35630          * @param {Roo.EventObject} e
35631          */
35632         "click" : true
35633     });
35634 };
35635
35636 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35637     
35638     /**
35639      * @cfg {String} title
35640      */   
35641     title : '',
35642     /**
35643      * @cfg {String} html
35644      */   
35645     html : '',
35646     /**
35647      * @cfg {String} bgimage
35648      */   
35649     bgimage : '',
35650     /**
35651      * @cfg {String} cls
35652      */   
35653     cls : '',
35654     /**
35655      * @cfg {String} href
35656      */   
35657     href : '',
35658     /**
35659      * @cfg {String} video
35660      */   
35661     video : '',
35662     /**
35663      * @cfg {Boolean} square
35664      */   
35665     square : true,
35666     
35667     getAutoCreate : function()
35668     {
35669         var cls = 'roo-brick';
35670         
35671         if(this.href.length){
35672             cls += ' roo-brick-link';
35673         }
35674         
35675         if(this.bgimage.length){
35676             cls += ' roo-brick-image';
35677         }
35678         
35679         if(!this.html.length && !this.bgimage.length){
35680             cls += ' roo-brick-center-title';
35681         }
35682         
35683         if(!this.html.length && this.bgimage.length){
35684             cls += ' roo-brick-bottom-title';
35685         }
35686         
35687         if(this.cls){
35688             cls += ' ' + this.cls;
35689         }
35690         
35691         var cfg = {
35692             tag: (this.href.length) ? 'a' : 'div',
35693             cls: cls,
35694             cn: [
35695                 {
35696                     tag: 'div',
35697                     cls: 'roo-brick-paragraph',
35698                     cn: []
35699                 }
35700             ]
35701         };
35702         
35703         if(this.href.length){
35704             cfg.href = this.href;
35705         }
35706         
35707         var cn = cfg.cn[0].cn;
35708         
35709         if(this.title.length){
35710             cn.push({
35711                 tag: 'h4',
35712                 cls: 'roo-brick-title',
35713                 html: this.title
35714             });
35715         }
35716         
35717         if(this.html.length){
35718             cn.push({
35719                 tag: 'p',
35720                 cls: 'roo-brick-text',
35721                 html: this.html
35722             });
35723         } else {
35724             cn.cls += ' hide';
35725         }
35726         
35727         if(this.bgimage.length){
35728             cfg.cn.push({
35729                 tag: 'img',
35730                 cls: 'roo-brick-image-view',
35731                 src: this.bgimage
35732             });
35733         }
35734         
35735         return cfg;
35736     },
35737     
35738     initEvents: function() 
35739     {
35740         if(this.title.length || this.html.length){
35741             this.el.on('mouseenter'  ,this.enter, this);
35742             this.el.on('mouseleave', this.leave, this);
35743         }
35744         
35745         Roo.EventManager.onWindowResize(this.resize, this); 
35746         
35747         if(this.bgimage.length){
35748             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35749             this.imageEl.on('load', this.onImageLoad, this);
35750             return;
35751         }
35752         
35753         this.resize();
35754     },
35755     
35756     onImageLoad : function()
35757     {
35758         this.resize();
35759     },
35760     
35761     resize : function()
35762     {
35763         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35764         
35765         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35766         
35767         if(this.bgimage.length){
35768             var image = this.el.select('.roo-brick-image-view', true).first();
35769             
35770             image.setWidth(paragraph.getWidth());
35771             
35772             if(this.square){
35773                 image.setHeight(paragraph.getWidth());
35774             }
35775             
35776             this.el.setHeight(image.getHeight());
35777             paragraph.setHeight(image.getHeight());
35778             
35779         }
35780         
35781     },
35782     
35783     enter: function(e, el)
35784     {
35785         e.preventDefault();
35786         
35787         if(this.bgimage.length){
35788             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35789             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35790         }
35791     },
35792     
35793     leave: function(e, el)
35794     {
35795         e.preventDefault();
35796         
35797         if(this.bgimage.length){
35798             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35799             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35800         }
35801     }
35802     
35803 });
35804
35805  
35806
35807  /*
35808  * - LGPL
35809  *
35810  * Number field 
35811  */
35812
35813 /**
35814  * @class Roo.bootstrap.NumberField
35815  * @extends Roo.bootstrap.Input
35816  * Bootstrap NumberField class
35817  * 
35818  * 
35819  * 
35820  * 
35821  * @constructor
35822  * Create a new NumberField
35823  * @param {Object} config The config object
35824  */
35825
35826 Roo.bootstrap.NumberField = function(config){
35827     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35828 };
35829
35830 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35831     
35832     /**
35833      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35834      */
35835     allowDecimals : true,
35836     /**
35837      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35838      */
35839     decimalSeparator : ".",
35840     /**
35841      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35842      */
35843     decimalPrecision : 2,
35844     /**
35845      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35846      */
35847     allowNegative : true,
35848     
35849     /**
35850      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35851      */
35852     allowZero: true,
35853     /**
35854      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35855      */
35856     minValue : Number.NEGATIVE_INFINITY,
35857     /**
35858      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35859      */
35860     maxValue : Number.MAX_VALUE,
35861     /**
35862      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35863      */
35864     minText : "The minimum value for this field is {0}",
35865     /**
35866      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35867      */
35868     maxText : "The maximum value for this field is {0}",
35869     /**
35870      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35871      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35872      */
35873     nanText : "{0} is not a valid number",
35874     /**
35875      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35876      */
35877     thousandsDelimiter : false,
35878     /**
35879      * @cfg {String} valueAlign alignment of value
35880      */
35881     valueAlign : "left",
35882
35883     getAutoCreate : function()
35884     {
35885         var hiddenInput = {
35886             tag: 'input',
35887             type: 'hidden',
35888             id: Roo.id(),
35889             cls: 'hidden-number-input'
35890         };
35891         
35892         if (this.name) {
35893             hiddenInput.name = this.name;
35894         }
35895         
35896         this.name = '';
35897         
35898         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35899         
35900         this.name = hiddenInput.name;
35901         
35902         if(cfg.cn.length > 0) {
35903             cfg.cn.push(hiddenInput);
35904         }
35905         
35906         return cfg;
35907     },
35908
35909     // private
35910     initEvents : function()
35911     {   
35912         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35913         
35914         var allowed = "0123456789";
35915         
35916         if(this.allowDecimals){
35917             allowed += this.decimalSeparator;
35918         }
35919         
35920         if(this.allowNegative){
35921             allowed += "-";
35922         }
35923         
35924         if(this.thousandsDelimiter) {
35925             allowed += ",";
35926         }
35927         
35928         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35929         
35930         var keyPress = function(e){
35931             
35932             var k = e.getKey();
35933             
35934             var c = e.getCharCode();
35935             
35936             if(
35937                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35938                     allowed.indexOf(String.fromCharCode(c)) === -1
35939             ){
35940                 e.stopEvent();
35941                 return;
35942             }
35943             
35944             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35945                 return;
35946             }
35947             
35948             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35949                 e.stopEvent();
35950             }
35951         };
35952         
35953         this.el.on("keypress", keyPress, this);
35954     },
35955     
35956     validateValue : function(value)
35957     {
35958         
35959         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35960             return false;
35961         }
35962         
35963         var num = this.parseValue(value);
35964         
35965         if(isNaN(num)){
35966             this.markInvalid(String.format(this.nanText, value));
35967             return false;
35968         }
35969         
35970         if(num < this.minValue){
35971             this.markInvalid(String.format(this.minText, this.minValue));
35972             return false;
35973         }
35974         
35975         if(num > this.maxValue){
35976             this.markInvalid(String.format(this.maxText, this.maxValue));
35977             return false;
35978         }
35979         
35980         return true;
35981     },
35982
35983     getValue : function()
35984     {
35985         var v = this.hiddenEl().getValue();
35986         
35987         return this.fixPrecision(this.parseValue(v));
35988     },
35989
35990     parseValue : function(value)
35991     {
35992         if(this.thousandsDelimiter) {
35993             value += "";
35994             r = new RegExp(",", "g");
35995             value = value.replace(r, "");
35996         }
35997         
35998         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35999         return isNaN(value) ? '' : value;
36000     },
36001
36002     fixPrecision : function(value)
36003     {
36004         if(this.thousandsDelimiter) {
36005             value += "";
36006             r = new RegExp(",", "g");
36007             value = value.replace(r, "");
36008         }
36009         
36010         var nan = isNaN(value);
36011         
36012         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36013             return nan ? '' : value;
36014         }
36015         return parseFloat(value).toFixed(this.decimalPrecision);
36016     },
36017
36018     setValue : function(v)
36019     {
36020         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36021         
36022         this.value = v;
36023         
36024         if(this.rendered){
36025             
36026             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36027             
36028             this.inputEl().dom.value = (v == '') ? '' :
36029                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36030             
36031             if(!this.allowZero && v === '0') {
36032                 this.hiddenEl().dom.value = '';
36033                 this.inputEl().dom.value = '';
36034             }
36035             
36036             this.validate();
36037         }
36038     },
36039
36040     decimalPrecisionFcn : function(v)
36041     {
36042         return Math.floor(v);
36043     },
36044
36045     beforeBlur : function()
36046     {
36047         var v = this.parseValue(this.getRawValue());
36048         
36049         if(v || v === 0 || v === ''){
36050             this.setValue(v);
36051         }
36052     },
36053     
36054     hiddenEl : function()
36055     {
36056         return this.el.select('input.hidden-number-input',true).first();
36057     }
36058     
36059 });
36060
36061  
36062
36063 /*
36064 * Licence: LGPL
36065 */
36066
36067 /**
36068  * @class Roo.bootstrap.DocumentSlider
36069  * @extends Roo.bootstrap.Component
36070  * Bootstrap DocumentSlider class
36071  * 
36072  * @constructor
36073  * Create a new DocumentViewer
36074  * @param {Object} config The config object
36075  */
36076
36077 Roo.bootstrap.DocumentSlider = function(config){
36078     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36079     
36080     this.files = [];
36081     
36082     this.addEvents({
36083         /**
36084          * @event initial
36085          * Fire after initEvent
36086          * @param {Roo.bootstrap.DocumentSlider} this
36087          */
36088         "initial" : true,
36089         /**
36090          * @event update
36091          * Fire after update
36092          * @param {Roo.bootstrap.DocumentSlider} this
36093          */
36094         "update" : true,
36095         /**
36096          * @event click
36097          * Fire after click
36098          * @param {Roo.bootstrap.DocumentSlider} this
36099          */
36100         "click" : true
36101     });
36102 };
36103
36104 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36105     
36106     files : false,
36107     
36108     indicator : 0,
36109     
36110     getAutoCreate : function()
36111     {
36112         var cfg = {
36113             tag : 'div',
36114             cls : 'roo-document-slider',
36115             cn : [
36116                 {
36117                     tag : 'div',
36118                     cls : 'roo-document-slider-header',
36119                     cn : [
36120                         {
36121                             tag : 'div',
36122                             cls : 'roo-document-slider-header-title'
36123                         }
36124                     ]
36125                 },
36126                 {
36127                     tag : 'div',
36128                     cls : 'roo-document-slider-body',
36129                     cn : [
36130                         {
36131                             tag : 'div',
36132                             cls : 'roo-document-slider-prev',
36133                             cn : [
36134                                 {
36135                                     tag : 'i',
36136                                     cls : 'fa fa-chevron-left'
36137                                 }
36138                             ]
36139                         },
36140                         {
36141                             tag : 'div',
36142                             cls : 'roo-document-slider-thumb',
36143                             cn : [
36144                                 {
36145                                     tag : 'img',
36146                                     cls : 'roo-document-slider-image'
36147                                 }
36148                             ]
36149                         },
36150                         {
36151                             tag : 'div',
36152                             cls : 'roo-document-slider-next',
36153                             cn : [
36154                                 {
36155                                     tag : 'i',
36156                                     cls : 'fa fa-chevron-right'
36157                                 }
36158                             ]
36159                         }
36160                     ]
36161                 }
36162             ]
36163         };
36164         
36165         return cfg;
36166     },
36167     
36168     initEvents : function()
36169     {
36170         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36171         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36172         
36173         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36174         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36175         
36176         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36178         
36179         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36181         
36182         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36184         
36185         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36186         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36187         
36188         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36189         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.thumbEl.on('click', this.onClick, this);
36192         
36193         this.prevIndicator.on('click', this.prev, this);
36194         
36195         this.nextIndicator.on('click', this.next, this);
36196         
36197     },
36198     
36199     initial : function()
36200     {
36201         if(this.files.length){
36202             this.indicator = 1;
36203             this.update()
36204         }
36205         
36206         this.fireEvent('initial', this);
36207     },
36208     
36209     update : function()
36210     {
36211         this.imageEl.attr('src', this.files[this.indicator - 1]);
36212         
36213         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36214         
36215         this.prevIndicator.show();
36216         
36217         if(this.indicator == 1){
36218             this.prevIndicator.hide();
36219         }
36220         
36221         this.nextIndicator.show();
36222         
36223         if(this.indicator == this.files.length){
36224             this.nextIndicator.hide();
36225         }
36226         
36227         this.thumbEl.scrollTo('top');
36228         
36229         this.fireEvent('update', this);
36230     },
36231     
36232     onClick : function(e)
36233     {
36234         e.preventDefault();
36235         
36236         this.fireEvent('click', this);
36237     },
36238     
36239     prev : function(e)
36240     {
36241         e.preventDefault();
36242         
36243         this.indicator = Math.max(1, this.indicator - 1);
36244         
36245         this.update();
36246     },
36247     
36248     next : function(e)
36249     {
36250         e.preventDefault();
36251         
36252         this.indicator = Math.min(this.files.length, this.indicator + 1);
36253         
36254         this.update();
36255     }
36256 });
36257 /*
36258  * - LGPL
36259  *
36260  * RadioSet
36261  *
36262  *
36263  */
36264
36265 /**
36266  * @class Roo.bootstrap.RadioSet
36267  * @extends Roo.bootstrap.Input
36268  * Bootstrap RadioSet class
36269  * @cfg {String} indicatorpos (left|right) default left
36270  * @cfg {Boolean} inline (true|false) inline the element (default true)
36271  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36272  * @constructor
36273  * Create a new RadioSet
36274  * @param {Object} config The config object
36275  */
36276
36277 Roo.bootstrap.RadioSet = function(config){
36278     
36279     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36280     
36281     this.radioes = [];
36282     
36283     Roo.bootstrap.RadioSet.register(this);
36284     
36285     this.addEvents({
36286         /**
36287         * @event check
36288         * Fires when the element is checked or unchecked.
36289         * @param {Roo.bootstrap.RadioSet} this This radio
36290         * @param {Roo.bootstrap.Radio} item The checked item
36291         */
36292        check : true,
36293        /**
36294         * @event click
36295         * Fires when the element is click.
36296         * @param {Roo.bootstrap.RadioSet} this This radio set
36297         * @param {Roo.bootstrap.Radio} item The checked item
36298         * @param {Roo.EventObject} e The event object
36299         */
36300        click : true
36301     });
36302     
36303 };
36304
36305 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36306
36307     radioes : false,
36308     
36309     inline : true,
36310     
36311     weight : '',
36312     
36313     indicatorpos : 'left',
36314     
36315     getAutoCreate : function()
36316     {
36317         var label = {
36318             tag : 'label',
36319             cls : 'roo-radio-set-label',
36320             cn : [
36321                 {
36322                     tag : 'span',
36323                     html : this.fieldLabel
36324                 }
36325             ]
36326         };
36327         if (Roo.bootstrap.version == 3) {
36328             
36329             
36330             if(this.indicatorpos == 'left'){
36331                 label.cn.unshift({
36332                     tag : 'i',
36333                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36334                     tooltip : 'This field is required'
36335                 });
36336             } else {
36337                 label.cn.push({
36338                     tag : 'i',
36339                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36340                     tooltip : 'This field is required'
36341                 });
36342             }
36343         }
36344         var items = {
36345             tag : 'div',
36346             cls : 'roo-radio-set-items'
36347         };
36348         
36349         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36350         
36351         if (align === 'left' && this.fieldLabel.length) {
36352             
36353             items = {
36354                 cls : "roo-radio-set-right", 
36355                 cn: [
36356                     items
36357                 ]
36358             };
36359             
36360             if(this.labelWidth > 12){
36361                 label.style = "width: " + this.labelWidth + 'px';
36362             }
36363             
36364             if(this.labelWidth < 13 && this.labelmd == 0){
36365                 this.labelmd = this.labelWidth;
36366             }
36367             
36368             if(this.labellg > 0){
36369                 label.cls += ' col-lg-' + this.labellg;
36370                 items.cls += ' col-lg-' + (12 - this.labellg);
36371             }
36372             
36373             if(this.labelmd > 0){
36374                 label.cls += ' col-md-' + this.labelmd;
36375                 items.cls += ' col-md-' + (12 - this.labelmd);
36376             }
36377             
36378             if(this.labelsm > 0){
36379                 label.cls += ' col-sm-' + this.labelsm;
36380                 items.cls += ' col-sm-' + (12 - this.labelsm);
36381             }
36382             
36383             if(this.labelxs > 0){
36384                 label.cls += ' col-xs-' + this.labelxs;
36385                 items.cls += ' col-xs-' + (12 - this.labelxs);
36386             }
36387         }
36388         
36389         var cfg = {
36390             tag : 'div',
36391             cls : 'roo-radio-set',
36392             cn : [
36393                 {
36394                     tag : 'input',
36395                     cls : 'roo-radio-set-input',
36396                     type : 'hidden',
36397                     name : this.name,
36398                     value : this.value ? this.value :  ''
36399                 },
36400                 label,
36401                 items
36402             ]
36403         };
36404         
36405         if(this.weight.length){
36406             cfg.cls += ' roo-radio-' + this.weight;
36407         }
36408         
36409         if(this.inline) {
36410             cfg.cls += ' roo-radio-set-inline';
36411         }
36412         
36413         var settings=this;
36414         ['xs','sm','md','lg'].map(function(size){
36415             if (settings[size]) {
36416                 cfg.cls += ' col-' + size + '-' + settings[size];
36417             }
36418         });
36419         
36420         return cfg;
36421         
36422     },
36423
36424     initEvents : function()
36425     {
36426         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36427         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36428         
36429         if(!this.fieldLabel.length){
36430             this.labelEl.hide();
36431         }
36432         
36433         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36434         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36435         
36436         this.indicator = this.indicatorEl();
36437         
36438         if(this.indicator){
36439             this.indicator.addClass('invisible');
36440         }
36441         
36442         this.originalValue = this.getValue();
36443         
36444     },
36445     
36446     inputEl: function ()
36447     {
36448         return this.el.select('.roo-radio-set-input', true).first();
36449     },
36450     
36451     getChildContainer : function()
36452     {
36453         return this.itemsEl;
36454     },
36455     
36456     register : function(item)
36457     {
36458         this.radioes.push(item);
36459         
36460     },
36461     
36462     validate : function()
36463     {   
36464         if(this.getVisibilityEl().hasClass('hidden')){
36465             return true;
36466         }
36467         
36468         var valid = false;
36469         
36470         Roo.each(this.radioes, function(i){
36471             if(!i.checked){
36472                 return;
36473             }
36474             
36475             valid = true;
36476             return false;
36477         });
36478         
36479         if(this.allowBlank) {
36480             return true;
36481         }
36482         
36483         if(this.disabled || valid){
36484             this.markValid();
36485             return true;
36486         }
36487         
36488         this.markInvalid();
36489         return false;
36490         
36491     },
36492     
36493     markValid : function()
36494     {
36495         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36496             this.indicatorEl().removeClass('visible');
36497             this.indicatorEl().addClass('invisible');
36498         }
36499         
36500         
36501         if (Roo.bootstrap.version == 3) {
36502             this.el.removeClass([this.invalidClass, this.validClass]);
36503             this.el.addClass(this.validClass);
36504         } else {
36505             this.el.removeClass(['is-invalid','is-valid']);
36506             this.el.addClass(['is-valid']);
36507         }
36508         this.fireEvent('valid', this);
36509     },
36510     
36511     markInvalid : function(msg)
36512     {
36513         if(this.allowBlank || this.disabled){
36514             return;
36515         }
36516         
36517         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36518             this.indicatorEl().removeClass('invisible');
36519             this.indicatorEl().addClass('visible');
36520         }
36521         if (Roo.bootstrap.version == 3) {
36522             this.el.removeClass([this.invalidClass, this.validClass]);
36523             this.el.addClass(this.invalidClass);
36524         } else {
36525             this.el.removeClass(['is-invalid','is-valid']);
36526             this.el.addClass(['is-invalid']);
36527         }
36528         
36529         this.fireEvent('invalid', this, msg);
36530         
36531     },
36532     
36533     setValue : function(v, suppressEvent)
36534     {   
36535         if(this.value === v){
36536             return;
36537         }
36538         
36539         this.value = v;
36540         
36541         if(this.rendered){
36542             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36543         }
36544         
36545         Roo.each(this.radioes, function(i){
36546             i.checked = false;
36547             i.el.removeClass('checked');
36548         });
36549         
36550         Roo.each(this.radioes, function(i){
36551             
36552             if(i.value === v || i.value.toString() === v.toString()){
36553                 i.checked = true;
36554                 i.el.addClass('checked');
36555                 
36556                 if(suppressEvent !== true){
36557                     this.fireEvent('check', this, i);
36558                 }
36559                 
36560                 return false;
36561             }
36562             
36563         }, this);
36564         
36565         this.validate();
36566     },
36567     
36568     clearInvalid : function(){
36569         
36570         if(!this.el || this.preventMark){
36571             return;
36572         }
36573         
36574         this.el.removeClass([this.invalidClass]);
36575         
36576         this.fireEvent('valid', this);
36577     }
36578     
36579 });
36580
36581 Roo.apply(Roo.bootstrap.RadioSet, {
36582     
36583     groups: {},
36584     
36585     register : function(set)
36586     {
36587         this.groups[set.name] = set;
36588     },
36589     
36590     get: function(name) 
36591     {
36592         if (typeof(this.groups[name]) == 'undefined') {
36593             return false;
36594         }
36595         
36596         return this.groups[name] ;
36597     }
36598     
36599 });
36600 /*
36601  * Based on:
36602  * Ext JS Library 1.1.1
36603  * Copyright(c) 2006-2007, Ext JS, LLC.
36604  *
36605  * Originally Released Under LGPL - original licence link has changed is not relivant.
36606  *
36607  * Fork - LGPL
36608  * <script type="text/javascript">
36609  */
36610
36611
36612 /**
36613  * @class Roo.bootstrap.SplitBar
36614  * @extends Roo.util.Observable
36615  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36616  * <br><br>
36617  * Usage:
36618  * <pre><code>
36619 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36620                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36621 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36622 split.minSize = 100;
36623 split.maxSize = 600;
36624 split.animate = true;
36625 split.on('moved', splitterMoved);
36626 </code></pre>
36627  * @constructor
36628  * Create a new SplitBar
36629  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36630  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36631  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36632  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36633                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36634                         position of the SplitBar).
36635  */
36636 Roo.bootstrap.SplitBar = function(cfg){
36637     
36638     /** @private */
36639     
36640     //{
36641     //  dragElement : elm
36642     //  resizingElement: el,
36643         // optional..
36644     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36645     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36646         // existingProxy ???
36647     //}
36648     
36649     this.el = Roo.get(cfg.dragElement, true);
36650     this.el.dom.unselectable = "on";
36651     /** @private */
36652     this.resizingEl = Roo.get(cfg.resizingElement, true);
36653
36654     /**
36655      * @private
36656      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36657      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36658      * @type Number
36659      */
36660     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36661     
36662     /**
36663      * The minimum size of the resizing element. (Defaults to 0)
36664      * @type Number
36665      */
36666     this.minSize = 0;
36667     
36668     /**
36669      * The maximum size of the resizing element. (Defaults to 2000)
36670      * @type Number
36671      */
36672     this.maxSize = 2000;
36673     
36674     /**
36675      * Whether to animate the transition to the new size
36676      * @type Boolean
36677      */
36678     this.animate = false;
36679     
36680     /**
36681      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36682      * @type Boolean
36683      */
36684     this.useShim = false;
36685     
36686     /** @private */
36687     this.shim = null;
36688     
36689     if(!cfg.existingProxy){
36690         /** @private */
36691         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36692     }else{
36693         this.proxy = Roo.get(cfg.existingProxy).dom;
36694     }
36695     /** @private */
36696     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36697     
36698     /** @private */
36699     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36700     
36701     /** @private */
36702     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36703     
36704     /** @private */
36705     this.dragSpecs = {};
36706     
36707     /**
36708      * @private The adapter to use to positon and resize elements
36709      */
36710     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36711     this.adapter.init(this);
36712     
36713     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36714         /** @private */
36715         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36716         this.el.addClass("roo-splitbar-h");
36717     }else{
36718         /** @private */
36719         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36720         this.el.addClass("roo-splitbar-v");
36721     }
36722     
36723     this.addEvents({
36724         /**
36725          * @event resize
36726          * Fires when the splitter is moved (alias for {@link #event-moved})
36727          * @param {Roo.bootstrap.SplitBar} this
36728          * @param {Number} newSize the new width or height
36729          */
36730         "resize" : true,
36731         /**
36732          * @event moved
36733          * Fires when the splitter is moved
36734          * @param {Roo.bootstrap.SplitBar} this
36735          * @param {Number} newSize the new width or height
36736          */
36737         "moved" : true,
36738         /**
36739          * @event beforeresize
36740          * Fires before the splitter is dragged
36741          * @param {Roo.bootstrap.SplitBar} this
36742          */
36743         "beforeresize" : true,
36744
36745         "beforeapply" : true
36746     });
36747
36748     Roo.util.Observable.call(this);
36749 };
36750
36751 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36752     onStartProxyDrag : function(x, y){
36753         this.fireEvent("beforeresize", this);
36754         if(!this.overlay){
36755             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36756             o.unselectable();
36757             o.enableDisplayMode("block");
36758             // all splitbars share the same overlay
36759             Roo.bootstrap.SplitBar.prototype.overlay = o;
36760         }
36761         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36762         this.overlay.show();
36763         Roo.get(this.proxy).setDisplayed("block");
36764         var size = this.adapter.getElementSize(this);
36765         this.activeMinSize = this.getMinimumSize();;
36766         this.activeMaxSize = this.getMaximumSize();;
36767         var c1 = size - this.activeMinSize;
36768         var c2 = Math.max(this.activeMaxSize - size, 0);
36769         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36770             this.dd.resetConstraints();
36771             this.dd.setXConstraint(
36772                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36773                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36774             );
36775             this.dd.setYConstraint(0, 0);
36776         }else{
36777             this.dd.resetConstraints();
36778             this.dd.setXConstraint(0, 0);
36779             this.dd.setYConstraint(
36780                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36781                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36782             );
36783          }
36784         this.dragSpecs.startSize = size;
36785         this.dragSpecs.startPoint = [x, y];
36786         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36787     },
36788     
36789     /** 
36790      * @private Called after the drag operation by the DDProxy
36791      */
36792     onEndProxyDrag : function(e){
36793         Roo.get(this.proxy).setDisplayed(false);
36794         var endPoint = Roo.lib.Event.getXY(e);
36795         if(this.overlay){
36796             this.overlay.hide();
36797         }
36798         var newSize;
36799         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36800             newSize = this.dragSpecs.startSize + 
36801                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36802                     endPoint[0] - this.dragSpecs.startPoint[0] :
36803                     this.dragSpecs.startPoint[0] - endPoint[0]
36804                 );
36805         }else{
36806             newSize = this.dragSpecs.startSize + 
36807                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36808                     endPoint[1] - this.dragSpecs.startPoint[1] :
36809                     this.dragSpecs.startPoint[1] - endPoint[1]
36810                 );
36811         }
36812         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36813         if(newSize != this.dragSpecs.startSize){
36814             if(this.fireEvent('beforeapply', this, newSize) !== false){
36815                 this.adapter.setElementSize(this, newSize);
36816                 this.fireEvent("moved", this, newSize);
36817                 this.fireEvent("resize", this, newSize);
36818             }
36819         }
36820     },
36821     
36822     /**
36823      * Get the adapter this SplitBar uses
36824      * @return The adapter object
36825      */
36826     getAdapter : function(){
36827         return this.adapter;
36828     },
36829     
36830     /**
36831      * Set the adapter this SplitBar uses
36832      * @param {Object} adapter A SplitBar adapter object
36833      */
36834     setAdapter : function(adapter){
36835         this.adapter = adapter;
36836         this.adapter.init(this);
36837     },
36838     
36839     /**
36840      * Gets the minimum size for the resizing element
36841      * @return {Number} The minimum size
36842      */
36843     getMinimumSize : function(){
36844         return this.minSize;
36845     },
36846     
36847     /**
36848      * Sets the minimum size for the resizing element
36849      * @param {Number} minSize The minimum size
36850      */
36851     setMinimumSize : function(minSize){
36852         this.minSize = minSize;
36853     },
36854     
36855     /**
36856      * Gets the maximum size for the resizing element
36857      * @return {Number} The maximum size
36858      */
36859     getMaximumSize : function(){
36860         return this.maxSize;
36861     },
36862     
36863     /**
36864      * Sets the maximum size for the resizing element
36865      * @param {Number} maxSize The maximum size
36866      */
36867     setMaximumSize : function(maxSize){
36868         this.maxSize = maxSize;
36869     },
36870     
36871     /**
36872      * Sets the initialize size for the resizing element
36873      * @param {Number} size The initial size
36874      */
36875     setCurrentSize : function(size){
36876         var oldAnimate = this.animate;
36877         this.animate = false;
36878         this.adapter.setElementSize(this, size);
36879         this.animate = oldAnimate;
36880     },
36881     
36882     /**
36883      * Destroy this splitbar. 
36884      * @param {Boolean} removeEl True to remove the element
36885      */
36886     destroy : function(removeEl){
36887         if(this.shim){
36888             this.shim.remove();
36889         }
36890         this.dd.unreg();
36891         this.proxy.parentNode.removeChild(this.proxy);
36892         if(removeEl){
36893             this.el.remove();
36894         }
36895     }
36896 });
36897
36898 /**
36899  * @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.
36900  */
36901 Roo.bootstrap.SplitBar.createProxy = function(dir){
36902     var proxy = new Roo.Element(document.createElement("div"));
36903     proxy.unselectable();
36904     var cls = 'roo-splitbar-proxy';
36905     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36906     document.body.appendChild(proxy.dom);
36907     return proxy.dom;
36908 };
36909
36910 /** 
36911  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36912  * Default Adapter. It assumes the splitter and resizing element are not positioned
36913  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36914  */
36915 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36916 };
36917
36918 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36919     // do nothing for now
36920     init : function(s){
36921     
36922     },
36923     /**
36924      * Called before drag operations to get the current size of the resizing element. 
36925      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36926      */
36927      getElementSize : function(s){
36928         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36929             return s.resizingEl.getWidth();
36930         }else{
36931             return s.resizingEl.getHeight();
36932         }
36933     },
36934     
36935     /**
36936      * Called after drag operations to set the size of the resizing element.
36937      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36938      * @param {Number} newSize The new size to set
36939      * @param {Function} onComplete A function to be invoked when resizing is complete
36940      */
36941     setElementSize : function(s, newSize, onComplete){
36942         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36943             if(!s.animate){
36944                 s.resizingEl.setWidth(newSize);
36945                 if(onComplete){
36946                     onComplete(s, newSize);
36947                 }
36948             }else{
36949                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36950             }
36951         }else{
36952             
36953             if(!s.animate){
36954                 s.resizingEl.setHeight(newSize);
36955                 if(onComplete){
36956                     onComplete(s, newSize);
36957                 }
36958             }else{
36959                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36960             }
36961         }
36962     }
36963 };
36964
36965 /** 
36966  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36967  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36968  * Adapter that  moves the splitter element to align with the resized sizing element. 
36969  * Used with an absolute positioned SplitBar.
36970  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36971  * document.body, make sure you assign an id to the body element.
36972  */
36973 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36974     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36975     this.container = Roo.get(container);
36976 };
36977
36978 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36979     init : function(s){
36980         this.basic.init(s);
36981     },
36982     
36983     getElementSize : function(s){
36984         return this.basic.getElementSize(s);
36985     },
36986     
36987     setElementSize : function(s, newSize, onComplete){
36988         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36989     },
36990     
36991     moveSplitter : function(s){
36992         var yes = Roo.bootstrap.SplitBar;
36993         switch(s.placement){
36994             case yes.LEFT:
36995                 s.el.setX(s.resizingEl.getRight());
36996                 break;
36997             case yes.RIGHT:
36998                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36999                 break;
37000             case yes.TOP:
37001                 s.el.setY(s.resizingEl.getBottom());
37002                 break;
37003             case yes.BOTTOM:
37004                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37005                 break;
37006         }
37007     }
37008 };
37009
37010 /**
37011  * Orientation constant - Create a vertical SplitBar
37012  * @static
37013  * @type Number
37014  */
37015 Roo.bootstrap.SplitBar.VERTICAL = 1;
37016
37017 /**
37018  * Orientation constant - Create a horizontal SplitBar
37019  * @static
37020  * @type Number
37021  */
37022 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37023
37024 /**
37025  * Placement constant - The resizing element is to the left of the splitter element
37026  * @static
37027  * @type Number
37028  */
37029 Roo.bootstrap.SplitBar.LEFT = 1;
37030
37031 /**
37032  * Placement constant - The resizing element is to the right of the splitter element
37033  * @static
37034  * @type Number
37035  */
37036 Roo.bootstrap.SplitBar.RIGHT = 2;
37037
37038 /**
37039  * Placement constant - The resizing element is positioned above the splitter element
37040  * @static
37041  * @type Number
37042  */
37043 Roo.bootstrap.SplitBar.TOP = 3;
37044
37045 /**
37046  * Placement constant - The resizing element is positioned under splitter element
37047  * @static
37048  * @type Number
37049  */
37050 Roo.bootstrap.SplitBar.BOTTOM = 4;
37051 Roo.namespace("Roo.bootstrap.layout");/*
37052  * Based on:
37053  * Ext JS Library 1.1.1
37054  * Copyright(c) 2006-2007, Ext JS, LLC.
37055  *
37056  * Originally Released Under LGPL - original licence link has changed is not relivant.
37057  *
37058  * Fork - LGPL
37059  * <script type="text/javascript">
37060  */
37061
37062 /**
37063  * @class Roo.bootstrap.layout.Manager
37064  * @extends Roo.bootstrap.Component
37065  * Base class for layout managers.
37066  */
37067 Roo.bootstrap.layout.Manager = function(config)
37068 {
37069     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37070
37071
37072
37073
37074
37075     /** false to disable window resize monitoring @type Boolean */
37076     this.monitorWindowResize = true;
37077     this.regions = {};
37078     this.addEvents({
37079         /**
37080          * @event layout
37081          * Fires when a layout is performed.
37082          * @param {Roo.LayoutManager} this
37083          */
37084         "layout" : true,
37085         /**
37086          * @event regionresized
37087          * Fires when the user resizes a region.
37088          * @param {Roo.LayoutRegion} region The resized region
37089          * @param {Number} newSize The new size (width for east/west, height for north/south)
37090          */
37091         "regionresized" : true,
37092         /**
37093          * @event regioncollapsed
37094          * Fires when a region is collapsed.
37095          * @param {Roo.LayoutRegion} region The collapsed region
37096          */
37097         "regioncollapsed" : true,
37098         /**
37099          * @event regionexpanded
37100          * Fires when a region is expanded.
37101          * @param {Roo.LayoutRegion} region The expanded region
37102          */
37103         "regionexpanded" : true
37104     });
37105     this.updating = false;
37106
37107     if (config.el) {
37108         this.el = Roo.get(config.el);
37109         this.initEvents();
37110     }
37111
37112 };
37113
37114 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37115
37116
37117     regions : null,
37118
37119     monitorWindowResize : true,
37120
37121
37122     updating : false,
37123
37124
37125     onRender : function(ct, position)
37126     {
37127         if(!this.el){
37128             this.el = Roo.get(ct);
37129             this.initEvents();
37130         }
37131         //this.fireEvent('render',this);
37132     },
37133
37134
37135     initEvents: function()
37136     {
37137
37138
37139         // ie scrollbar fix
37140         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37141             document.body.scroll = "no";
37142         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37143             this.el.position('relative');
37144         }
37145         this.id = this.el.id;
37146         this.el.addClass("roo-layout-container");
37147         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37148         if(this.el.dom != document.body ) {
37149             this.el.on('resize', this.layout,this);
37150             this.el.on('show', this.layout,this);
37151         }
37152
37153     },
37154
37155     /**
37156      * Returns true if this layout is currently being updated
37157      * @return {Boolean}
37158      */
37159     isUpdating : function(){
37160         return this.updating;
37161     },
37162
37163     /**
37164      * Suspend the LayoutManager from doing auto-layouts while
37165      * making multiple add or remove calls
37166      */
37167     beginUpdate : function(){
37168         this.updating = true;
37169     },
37170
37171     /**
37172      * Restore auto-layouts and optionally disable the manager from performing a layout
37173      * @param {Boolean} noLayout true to disable a layout update
37174      */
37175     endUpdate : function(noLayout){
37176         this.updating = false;
37177         if(!noLayout){
37178             this.layout();
37179         }
37180     },
37181
37182     layout: function(){
37183         // abstract...
37184     },
37185
37186     onRegionResized : function(region, newSize){
37187         this.fireEvent("regionresized", region, newSize);
37188         this.layout();
37189     },
37190
37191     onRegionCollapsed : function(region){
37192         this.fireEvent("regioncollapsed", region);
37193     },
37194
37195     onRegionExpanded : function(region){
37196         this.fireEvent("regionexpanded", region);
37197     },
37198
37199     /**
37200      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37201      * performs box-model adjustments.
37202      * @return {Object} The size as an object {width: (the width), height: (the height)}
37203      */
37204     getViewSize : function()
37205     {
37206         var size;
37207         if(this.el.dom != document.body){
37208             size = this.el.getSize();
37209         }else{
37210             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37211         }
37212         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37213         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37214         return size;
37215     },
37216
37217     /**
37218      * Returns the Element this layout is bound to.
37219      * @return {Roo.Element}
37220      */
37221     getEl : function(){
37222         return this.el;
37223     },
37224
37225     /**
37226      * Returns the specified region.
37227      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37228      * @return {Roo.LayoutRegion}
37229      */
37230     getRegion : function(target){
37231         return this.regions[target.toLowerCase()];
37232     },
37233
37234     onWindowResize : function(){
37235         if(this.monitorWindowResize){
37236             this.layout();
37237         }
37238     }
37239 });
37240 /*
37241  * Based on:
37242  * Ext JS Library 1.1.1
37243  * Copyright(c) 2006-2007, Ext JS, LLC.
37244  *
37245  * Originally Released Under LGPL - original licence link has changed is not relivant.
37246  *
37247  * Fork - LGPL
37248  * <script type="text/javascript">
37249  */
37250 /**
37251  * @class Roo.bootstrap.layout.Border
37252  * @extends Roo.bootstrap.layout.Manager
37253  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37254  * please see: examples/bootstrap/nested.html<br><br>
37255  
37256 <b>The container the layout is rendered into can be either the body element or any other element.
37257 If it is not the body element, the container needs to either be an absolute positioned element,
37258 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37259 the container size if it is not the body element.</b>
37260
37261 * @constructor
37262 * Create a new Border
37263 * @param {Object} config Configuration options
37264  */
37265 Roo.bootstrap.layout.Border = function(config){
37266     config = config || {};
37267     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37268     
37269     
37270     
37271     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37272         if(config[region]){
37273             config[region].region = region;
37274             this.addRegion(config[region]);
37275         }
37276     },this);
37277     
37278 };
37279
37280 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37281
37282 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37283     
37284     parent : false, // this might point to a 'nest' or a ???
37285     
37286     /**
37287      * Creates and adds a new region if it doesn't already exist.
37288      * @param {String} target The target region key (north, south, east, west or center).
37289      * @param {Object} config The regions config object
37290      * @return {BorderLayoutRegion} The new region
37291      */
37292     addRegion : function(config)
37293     {
37294         if(!this.regions[config.region]){
37295             var r = this.factory(config);
37296             this.bindRegion(r);
37297         }
37298         return this.regions[config.region];
37299     },
37300
37301     // private (kinda)
37302     bindRegion : function(r){
37303         this.regions[r.config.region] = r;
37304         
37305         r.on("visibilitychange",    this.layout, this);
37306         r.on("paneladded",          this.layout, this);
37307         r.on("panelremoved",        this.layout, this);
37308         r.on("invalidated",         this.layout, this);
37309         r.on("resized",             this.onRegionResized, this);
37310         r.on("collapsed",           this.onRegionCollapsed, this);
37311         r.on("expanded",            this.onRegionExpanded, this);
37312     },
37313
37314     /**
37315      * Performs a layout update.
37316      */
37317     layout : function()
37318     {
37319         if(this.updating) {
37320             return;
37321         }
37322         
37323         // render all the rebions if they have not been done alreayd?
37324         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37325             if(this.regions[region] && !this.regions[region].bodyEl){
37326                 this.regions[region].onRender(this.el)
37327             }
37328         },this);
37329         
37330         var size = this.getViewSize();
37331         var w = size.width;
37332         var h = size.height;
37333         var centerW = w;
37334         var centerH = h;
37335         var centerY = 0;
37336         var centerX = 0;
37337         //var x = 0, y = 0;
37338
37339         var rs = this.regions;
37340         var north = rs["north"];
37341         var south = rs["south"]; 
37342         var west = rs["west"];
37343         var east = rs["east"];
37344         var center = rs["center"];
37345         //if(this.hideOnLayout){ // not supported anymore
37346             //c.el.setStyle("display", "none");
37347         //}
37348         if(north && north.isVisible()){
37349             var b = north.getBox();
37350             var m = north.getMargins();
37351             b.width = w - (m.left+m.right);
37352             b.x = m.left;
37353             b.y = m.top;
37354             centerY = b.height + b.y + m.bottom;
37355             centerH -= centerY;
37356             north.updateBox(this.safeBox(b));
37357         }
37358         if(south && south.isVisible()){
37359             var b = south.getBox();
37360             var m = south.getMargins();
37361             b.width = w - (m.left+m.right);
37362             b.x = m.left;
37363             var totalHeight = (b.height + m.top + m.bottom);
37364             b.y = h - totalHeight + m.top;
37365             centerH -= totalHeight;
37366             south.updateBox(this.safeBox(b));
37367         }
37368         if(west && west.isVisible()){
37369             var b = west.getBox();
37370             var m = west.getMargins();
37371             b.height = centerH - (m.top+m.bottom);
37372             b.x = m.left;
37373             b.y = centerY + m.top;
37374             var totalWidth = (b.width + m.left + m.right);
37375             centerX += totalWidth;
37376             centerW -= totalWidth;
37377             west.updateBox(this.safeBox(b));
37378         }
37379         if(east && east.isVisible()){
37380             var b = east.getBox();
37381             var m = east.getMargins();
37382             b.height = centerH - (m.top+m.bottom);
37383             var totalWidth = (b.width + m.left + m.right);
37384             b.x = w - totalWidth + m.left;
37385             b.y = centerY + m.top;
37386             centerW -= totalWidth;
37387             east.updateBox(this.safeBox(b));
37388         }
37389         if(center){
37390             var m = center.getMargins();
37391             var centerBox = {
37392                 x: centerX + m.left,
37393                 y: centerY + m.top,
37394                 width: centerW - (m.left+m.right),
37395                 height: centerH - (m.top+m.bottom)
37396             };
37397             //if(this.hideOnLayout){
37398                 //center.el.setStyle("display", "block");
37399             //}
37400             center.updateBox(this.safeBox(centerBox));
37401         }
37402         this.el.repaint();
37403         this.fireEvent("layout", this);
37404     },
37405
37406     // private
37407     safeBox : function(box){
37408         box.width = Math.max(0, box.width);
37409         box.height = Math.max(0, box.height);
37410         return box;
37411     },
37412
37413     /**
37414      * Adds a ContentPanel (or subclass) to this layout.
37415      * @param {String} target The target region key (north, south, east, west or center).
37416      * @param {Roo.ContentPanel} panel The panel to add
37417      * @return {Roo.ContentPanel} The added panel
37418      */
37419     add : function(target, panel){
37420          
37421         target = target.toLowerCase();
37422         return this.regions[target].add(panel);
37423     },
37424
37425     /**
37426      * Remove a ContentPanel (or subclass) to this layout.
37427      * @param {String} target The target region key (north, south, east, west or center).
37428      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37429      * @return {Roo.ContentPanel} The removed panel
37430      */
37431     remove : function(target, panel){
37432         target = target.toLowerCase();
37433         return this.regions[target].remove(panel);
37434     },
37435
37436     /**
37437      * Searches all regions for a panel with the specified id
37438      * @param {String} panelId
37439      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37440      */
37441     findPanel : function(panelId){
37442         var rs = this.regions;
37443         for(var target in rs){
37444             if(typeof rs[target] != "function"){
37445                 var p = rs[target].getPanel(panelId);
37446                 if(p){
37447                     return p;
37448                 }
37449             }
37450         }
37451         return null;
37452     },
37453
37454     /**
37455      * Searches all regions for a panel with the specified id and activates (shows) it.
37456      * @param {String/ContentPanel} panelId The panels id or the panel itself
37457      * @return {Roo.ContentPanel} The shown panel or null
37458      */
37459     showPanel : function(panelId) {
37460       var rs = this.regions;
37461       for(var target in rs){
37462          var r = rs[target];
37463          if(typeof r != "function"){
37464             if(r.hasPanel(panelId)){
37465                return r.showPanel(panelId);
37466             }
37467          }
37468       }
37469       return null;
37470    },
37471
37472    /**
37473      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37474      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37475      */
37476    /*
37477     restoreState : function(provider){
37478         if(!provider){
37479             provider = Roo.state.Manager;
37480         }
37481         var sm = new Roo.LayoutStateManager();
37482         sm.init(this, provider);
37483     },
37484 */
37485  
37486  
37487     /**
37488      * Adds a xtype elements to the layout.
37489      * <pre><code>
37490
37491 layout.addxtype({
37492        xtype : 'ContentPanel',
37493        region: 'west',
37494        items: [ .... ]
37495    }
37496 );
37497
37498 layout.addxtype({
37499         xtype : 'NestedLayoutPanel',
37500         region: 'west',
37501         layout: {
37502            center: { },
37503            west: { }   
37504         },
37505         items : [ ... list of content panels or nested layout panels.. ]
37506    }
37507 );
37508 </code></pre>
37509      * @param {Object} cfg Xtype definition of item to add.
37510      */
37511     addxtype : function(cfg)
37512     {
37513         // basically accepts a pannel...
37514         // can accept a layout region..!?!?
37515         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37516         
37517         
37518         // theory?  children can only be panels??
37519         
37520         //if (!cfg.xtype.match(/Panel$/)) {
37521         //    return false;
37522         //}
37523         var ret = false;
37524         
37525         if (typeof(cfg.region) == 'undefined') {
37526             Roo.log("Failed to add Panel, region was not set");
37527             Roo.log(cfg);
37528             return false;
37529         }
37530         var region = cfg.region;
37531         delete cfg.region;
37532         
37533           
37534         var xitems = [];
37535         if (cfg.items) {
37536             xitems = cfg.items;
37537             delete cfg.items;
37538         }
37539         var nb = false;
37540         
37541         if ( region == 'center') {
37542             Roo.log("Center: " + cfg.title);
37543         }
37544         
37545         
37546         switch(cfg.xtype) 
37547         {
37548             case 'Content':  // ContentPanel (el, cfg)
37549             case 'Scroll':  // ContentPanel (el, cfg)
37550             case 'View': 
37551                 cfg.autoCreate = cfg.autoCreate || true;
37552                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37553                 //} else {
37554                 //    var el = this.el.createChild();
37555                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37556                 //}
37557                 
37558                 this.add(region, ret);
37559                 break;
37560             
37561             /*
37562             case 'TreePanel': // our new panel!
37563                 cfg.el = this.el.createChild();
37564                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37565                 this.add(region, ret);
37566                 break;
37567             */
37568             
37569             case 'Nest': 
37570                 // create a new Layout (which is  a Border Layout...
37571                 
37572                 var clayout = cfg.layout;
37573                 clayout.el  = this.el.createChild();
37574                 clayout.items   = clayout.items  || [];
37575                 
37576                 delete cfg.layout;
37577                 
37578                 // replace this exitems with the clayout ones..
37579                 xitems = clayout.items;
37580                  
37581                 // force background off if it's in center...
37582                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37583                     cfg.background = false;
37584                 }
37585                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37586                 
37587                 
37588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37589                 //console.log('adding nested layout panel '  + cfg.toSource());
37590                 this.add(region, ret);
37591                 nb = {}; /// find first...
37592                 break;
37593             
37594             case 'Grid':
37595                 
37596                 // needs grid and region
37597                 
37598                 //var el = this.getRegion(region).el.createChild();
37599                 /*
37600                  *var el = this.el.createChild();
37601                 // create the grid first...
37602                 cfg.grid.container = el;
37603                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37604                 */
37605                 
37606                 if (region == 'center' && this.active ) {
37607                     cfg.background = false;
37608                 }
37609                 
37610                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37611                 
37612                 this.add(region, ret);
37613                 /*
37614                 if (cfg.background) {
37615                     // render grid on panel activation (if panel background)
37616                     ret.on('activate', function(gp) {
37617                         if (!gp.grid.rendered) {
37618                     //        gp.grid.render(el);
37619                         }
37620                     });
37621                 } else {
37622                   //  cfg.grid.render(el);
37623                 }
37624                 */
37625                 break;
37626            
37627            
37628             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37629                 // it was the old xcomponent building that caused this before.
37630                 // espeically if border is the top element in the tree.
37631                 ret = this;
37632                 break; 
37633                 
37634                     
37635                 
37636                 
37637                 
37638             default:
37639                 /*
37640                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37641                     
37642                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37643                     this.add(region, ret);
37644                 } else {
37645                 */
37646                     Roo.log(cfg);
37647                     throw "Can not add '" + cfg.xtype + "' to Border";
37648                     return null;
37649              
37650                                 
37651              
37652         }
37653         this.beginUpdate();
37654         // add children..
37655         var region = '';
37656         var abn = {};
37657         Roo.each(xitems, function(i)  {
37658             region = nb && i.region ? i.region : false;
37659             
37660             var add = ret.addxtype(i);
37661            
37662             if (region) {
37663                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37664                 if (!i.background) {
37665                     abn[region] = nb[region] ;
37666                 }
37667             }
37668             
37669         });
37670         this.endUpdate();
37671
37672         // make the last non-background panel active..
37673         //if (nb) { Roo.log(abn); }
37674         if (nb) {
37675             
37676             for(var r in abn) {
37677                 region = this.getRegion(r);
37678                 if (region) {
37679                     // tried using nb[r], but it does not work..
37680                      
37681                     region.showPanel(abn[r]);
37682                    
37683                 }
37684             }
37685         }
37686         return ret;
37687         
37688     },
37689     
37690     
37691 // private
37692     factory : function(cfg)
37693     {
37694         
37695         var validRegions = Roo.bootstrap.layout.Border.regions;
37696
37697         var target = cfg.region;
37698         cfg.mgr = this;
37699         
37700         var r = Roo.bootstrap.layout;
37701         Roo.log(target);
37702         switch(target){
37703             case "north":
37704                 return new r.North(cfg);
37705             case "south":
37706                 return new r.South(cfg);
37707             case "east":
37708                 return new r.East(cfg);
37709             case "west":
37710                 return new r.West(cfg);
37711             case "center":
37712                 return new r.Center(cfg);
37713         }
37714         throw 'Layout region "'+target+'" not supported.';
37715     }
37716     
37717     
37718 });
37719  /*
37720  * Based on:
37721  * Ext JS Library 1.1.1
37722  * Copyright(c) 2006-2007, Ext JS, LLC.
37723  *
37724  * Originally Released Under LGPL - original licence link has changed is not relivant.
37725  *
37726  * Fork - LGPL
37727  * <script type="text/javascript">
37728  */
37729  
37730 /**
37731  * @class Roo.bootstrap.layout.Basic
37732  * @extends Roo.util.Observable
37733  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37734  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37735  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37736  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37737  * @cfg {string}   region  the region that it inhabits..
37738  * @cfg {bool}   skipConfig skip config?
37739  * 
37740
37741  */
37742 Roo.bootstrap.layout.Basic = function(config){
37743     
37744     this.mgr = config.mgr;
37745     
37746     this.position = config.region;
37747     
37748     var skipConfig = config.skipConfig;
37749     
37750     this.events = {
37751         /**
37752          * @scope Roo.BasicLayoutRegion
37753          */
37754         
37755         /**
37756          * @event beforeremove
37757          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37758          * @param {Roo.LayoutRegion} this
37759          * @param {Roo.ContentPanel} panel The panel
37760          * @param {Object} e The cancel event object
37761          */
37762         "beforeremove" : true,
37763         /**
37764          * @event invalidated
37765          * Fires when the layout for this region is changed.
37766          * @param {Roo.LayoutRegion} this
37767          */
37768         "invalidated" : true,
37769         /**
37770          * @event visibilitychange
37771          * Fires when this region is shown or hidden 
37772          * @param {Roo.LayoutRegion} this
37773          * @param {Boolean} visibility true or false
37774          */
37775         "visibilitychange" : true,
37776         /**
37777          * @event paneladded
37778          * Fires when a panel is added. 
37779          * @param {Roo.LayoutRegion} this
37780          * @param {Roo.ContentPanel} panel The panel
37781          */
37782         "paneladded" : true,
37783         /**
37784          * @event panelremoved
37785          * Fires when a panel is removed. 
37786          * @param {Roo.LayoutRegion} this
37787          * @param {Roo.ContentPanel} panel The panel
37788          */
37789         "panelremoved" : true,
37790         /**
37791          * @event beforecollapse
37792          * Fires when this region before collapse.
37793          * @param {Roo.LayoutRegion} this
37794          */
37795         "beforecollapse" : true,
37796         /**
37797          * @event collapsed
37798          * Fires when this region is collapsed.
37799          * @param {Roo.LayoutRegion} this
37800          */
37801         "collapsed" : true,
37802         /**
37803          * @event expanded
37804          * Fires when this region is expanded.
37805          * @param {Roo.LayoutRegion} this
37806          */
37807         "expanded" : true,
37808         /**
37809          * @event slideshow
37810          * Fires when this region is slid into view.
37811          * @param {Roo.LayoutRegion} this
37812          */
37813         "slideshow" : true,
37814         /**
37815          * @event slidehide
37816          * Fires when this region slides out of view. 
37817          * @param {Roo.LayoutRegion} this
37818          */
37819         "slidehide" : true,
37820         /**
37821          * @event panelactivated
37822          * Fires when a panel is activated. 
37823          * @param {Roo.LayoutRegion} this
37824          * @param {Roo.ContentPanel} panel The activated panel
37825          */
37826         "panelactivated" : true,
37827         /**
37828          * @event resized
37829          * Fires when the user resizes this region. 
37830          * @param {Roo.LayoutRegion} this
37831          * @param {Number} newSize The new size (width for east/west, height for north/south)
37832          */
37833         "resized" : true
37834     };
37835     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37836     this.panels = new Roo.util.MixedCollection();
37837     this.panels.getKey = this.getPanelId.createDelegate(this);
37838     this.box = null;
37839     this.activePanel = null;
37840     // ensure listeners are added...
37841     
37842     if (config.listeners || config.events) {
37843         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37844             listeners : config.listeners || {},
37845             events : config.events || {}
37846         });
37847     }
37848     
37849     if(skipConfig !== true){
37850         this.applyConfig(config);
37851     }
37852 };
37853
37854 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37855 {
37856     getPanelId : function(p){
37857         return p.getId();
37858     },
37859     
37860     applyConfig : function(config){
37861         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37862         this.config = config;
37863         
37864     },
37865     
37866     /**
37867      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37868      * the width, for horizontal (north, south) the height.
37869      * @param {Number} newSize The new width or height
37870      */
37871     resizeTo : function(newSize){
37872         var el = this.el ? this.el :
37873                  (this.activePanel ? this.activePanel.getEl() : null);
37874         if(el){
37875             switch(this.position){
37876                 case "east":
37877                 case "west":
37878                     el.setWidth(newSize);
37879                     this.fireEvent("resized", this, newSize);
37880                 break;
37881                 case "north":
37882                 case "south":
37883                     el.setHeight(newSize);
37884                     this.fireEvent("resized", this, newSize);
37885                 break;                
37886             }
37887         }
37888     },
37889     
37890     getBox : function(){
37891         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37892     },
37893     
37894     getMargins : function(){
37895         return this.margins;
37896     },
37897     
37898     updateBox : function(box){
37899         this.box = box;
37900         var el = this.activePanel.getEl();
37901         el.dom.style.left = box.x + "px";
37902         el.dom.style.top = box.y + "px";
37903         this.activePanel.setSize(box.width, box.height);
37904     },
37905     
37906     /**
37907      * Returns the container element for this region.
37908      * @return {Roo.Element}
37909      */
37910     getEl : function(){
37911         return this.activePanel;
37912     },
37913     
37914     /**
37915      * Returns true if this region is currently visible.
37916      * @return {Boolean}
37917      */
37918     isVisible : function(){
37919         return this.activePanel ? true : false;
37920     },
37921     
37922     setActivePanel : function(panel){
37923         panel = this.getPanel(panel);
37924         if(this.activePanel && this.activePanel != panel){
37925             this.activePanel.setActiveState(false);
37926             this.activePanel.getEl().setLeftTop(-10000,-10000);
37927         }
37928         this.activePanel = panel;
37929         panel.setActiveState(true);
37930         if(this.box){
37931             panel.setSize(this.box.width, this.box.height);
37932         }
37933         this.fireEvent("panelactivated", this, panel);
37934         this.fireEvent("invalidated");
37935     },
37936     
37937     /**
37938      * Show the specified panel.
37939      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37940      * @return {Roo.ContentPanel} The shown panel or null
37941      */
37942     showPanel : function(panel){
37943         panel = this.getPanel(panel);
37944         if(panel){
37945             this.setActivePanel(panel);
37946         }
37947         return panel;
37948     },
37949     
37950     /**
37951      * Get the active panel for this region.
37952      * @return {Roo.ContentPanel} The active panel or null
37953      */
37954     getActivePanel : function(){
37955         return this.activePanel;
37956     },
37957     
37958     /**
37959      * Add the passed ContentPanel(s)
37960      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37961      * @return {Roo.ContentPanel} The panel added (if only one was added)
37962      */
37963     add : function(panel){
37964         if(arguments.length > 1){
37965             for(var i = 0, len = arguments.length; i < len; i++) {
37966                 this.add(arguments[i]);
37967             }
37968             return null;
37969         }
37970         if(this.hasPanel(panel)){
37971             this.showPanel(panel);
37972             return panel;
37973         }
37974         var el = panel.getEl();
37975         if(el.dom.parentNode != this.mgr.el.dom){
37976             this.mgr.el.dom.appendChild(el.dom);
37977         }
37978         if(panel.setRegion){
37979             panel.setRegion(this);
37980         }
37981         this.panels.add(panel);
37982         el.setStyle("position", "absolute");
37983         if(!panel.background){
37984             this.setActivePanel(panel);
37985             if(this.config.initialSize && this.panels.getCount()==1){
37986                 this.resizeTo(this.config.initialSize);
37987             }
37988         }
37989         this.fireEvent("paneladded", this, panel);
37990         return panel;
37991     },
37992     
37993     /**
37994      * Returns true if the panel is in this region.
37995      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37996      * @return {Boolean}
37997      */
37998     hasPanel : function(panel){
37999         if(typeof panel == "object"){ // must be panel obj
38000             panel = panel.getId();
38001         }
38002         return this.getPanel(panel) ? true : false;
38003     },
38004     
38005     /**
38006      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38007      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38008      * @param {Boolean} preservePanel Overrides the config preservePanel option
38009      * @return {Roo.ContentPanel} The panel that was removed
38010      */
38011     remove : function(panel, preservePanel){
38012         panel = this.getPanel(panel);
38013         if(!panel){
38014             return null;
38015         }
38016         var e = {};
38017         this.fireEvent("beforeremove", this, panel, e);
38018         if(e.cancel === true){
38019             return null;
38020         }
38021         var panelId = panel.getId();
38022         this.panels.removeKey(panelId);
38023         return panel;
38024     },
38025     
38026     /**
38027      * Returns the panel specified or null if it's not in this region.
38028      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38029      * @return {Roo.ContentPanel}
38030      */
38031     getPanel : function(id){
38032         if(typeof id == "object"){ // must be panel obj
38033             return id;
38034         }
38035         return this.panels.get(id);
38036     },
38037     
38038     /**
38039      * Returns this regions position (north/south/east/west/center).
38040      * @return {String} 
38041      */
38042     getPosition: function(){
38043         return this.position;    
38044     }
38045 });/*
38046  * Based on:
38047  * Ext JS Library 1.1.1
38048  * Copyright(c) 2006-2007, Ext JS, LLC.
38049  *
38050  * Originally Released Under LGPL - original licence link has changed is not relivant.
38051  *
38052  * Fork - LGPL
38053  * <script type="text/javascript">
38054  */
38055  
38056 /**
38057  * @class Roo.bootstrap.layout.Region
38058  * @extends Roo.bootstrap.layout.Basic
38059  * This class represents a region in a layout manager.
38060  
38061  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38062  * @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})
38063  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38064  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38065  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38066  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38067  * @cfg {String}    title           The title for the region (overrides panel titles)
38068  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38069  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38070  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38071  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38072  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38073  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38074  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38075  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38076  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38077  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38078
38079  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38080  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38081  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38082  * @cfg {Number}    width           For East/West panels
38083  * @cfg {Number}    height          For North/South panels
38084  * @cfg {Boolean}   split           To show the splitter
38085  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38086  * 
38087  * @cfg {string}   cls             Extra CSS classes to add to region
38088  * 
38089  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38090  * @cfg {string}   region  the region that it inhabits..
38091  *
38092
38093  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38094  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38095
38096  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38097  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38098  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38099  */
38100 Roo.bootstrap.layout.Region = function(config)
38101 {
38102     this.applyConfig(config);
38103
38104     var mgr = config.mgr;
38105     var pos = config.region;
38106     config.skipConfig = true;
38107     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38108     
38109     if (mgr.el) {
38110         this.onRender(mgr.el);   
38111     }
38112      
38113     this.visible = true;
38114     this.collapsed = false;
38115     this.unrendered_panels = [];
38116 };
38117
38118 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38119
38120     position: '', // set by wrapper (eg. north/south etc..)
38121     unrendered_panels : null,  // unrendered panels.
38122     
38123     tabPosition : false,
38124     
38125     mgr: false, // points to 'Border'
38126     
38127     
38128     createBody : function(){
38129         /** This region's body element 
38130         * @type Roo.Element */
38131         this.bodyEl = this.el.createChild({
38132                 tag: "div",
38133                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38134         });
38135     },
38136
38137     onRender: function(ctr, pos)
38138     {
38139         var dh = Roo.DomHelper;
38140         /** This region's container element 
38141         * @type Roo.Element */
38142         this.el = dh.append(ctr.dom, {
38143                 tag: "div",
38144                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38145             }, true);
38146         /** This region's title element 
38147         * @type Roo.Element */
38148     
38149         this.titleEl = dh.append(this.el.dom,  {
38150                 tag: "div",
38151                 unselectable: "on",
38152                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38153                 children:[
38154                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38155                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38156                 ]
38157             }, true);
38158         
38159         this.titleEl.enableDisplayMode();
38160         /** This region's title text element 
38161         * @type HTMLElement */
38162         this.titleTextEl = this.titleEl.dom.firstChild;
38163         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38164         /*
38165         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38166         this.closeBtn.enableDisplayMode();
38167         this.closeBtn.on("click", this.closeClicked, this);
38168         this.closeBtn.hide();
38169     */
38170         this.createBody(this.config);
38171         if(this.config.hideWhenEmpty){
38172             this.hide();
38173             this.on("paneladded", this.validateVisibility, this);
38174             this.on("panelremoved", this.validateVisibility, this);
38175         }
38176         if(this.autoScroll){
38177             this.bodyEl.setStyle("overflow", "auto");
38178         }else{
38179             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38180         }
38181         //if(c.titlebar !== false){
38182             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38183                 this.titleEl.hide();
38184             }else{
38185                 this.titleEl.show();
38186                 if(this.config.title){
38187                     this.titleTextEl.innerHTML = this.config.title;
38188                 }
38189             }
38190         //}
38191         if(this.config.collapsed){
38192             this.collapse(true);
38193         }
38194         if(this.config.hidden){
38195             this.hide();
38196         }
38197         
38198         if (this.unrendered_panels && this.unrendered_panels.length) {
38199             for (var i =0;i< this.unrendered_panels.length; i++) {
38200                 this.add(this.unrendered_panels[i]);
38201             }
38202             this.unrendered_panels = null;
38203             
38204         }
38205         
38206     },
38207     
38208     applyConfig : function(c)
38209     {
38210         /*
38211          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38212             var dh = Roo.DomHelper;
38213             if(c.titlebar !== false){
38214                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38215                 this.collapseBtn.on("click", this.collapse, this);
38216                 this.collapseBtn.enableDisplayMode();
38217                 /*
38218                 if(c.showPin === true || this.showPin){
38219                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38220                     this.stickBtn.enableDisplayMode();
38221                     this.stickBtn.on("click", this.expand, this);
38222                     this.stickBtn.hide();
38223                 }
38224                 
38225             }
38226             */
38227             /** This region's collapsed element
38228             * @type Roo.Element */
38229             /*
38230              *
38231             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38232                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38233             ]}, true);
38234             
38235             if(c.floatable !== false){
38236                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38237                this.collapsedEl.on("click", this.collapseClick, this);
38238             }
38239
38240             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38241                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38242                    id: "message", unselectable: "on", style:{"float":"left"}});
38243                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38244              }
38245             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38246             this.expandBtn.on("click", this.expand, this);
38247             
38248         }
38249         
38250         if(this.collapseBtn){
38251             this.collapseBtn.setVisible(c.collapsible == true);
38252         }
38253         
38254         this.cmargins = c.cmargins || this.cmargins ||
38255                          (this.position == "west" || this.position == "east" ?
38256                              {top: 0, left: 2, right:2, bottom: 0} :
38257                              {top: 2, left: 0, right:0, bottom: 2});
38258         */
38259         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38260         
38261         
38262         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38263         
38264         this.autoScroll = c.autoScroll || false;
38265         
38266         
38267        
38268         
38269         this.duration = c.duration || .30;
38270         this.slideDuration = c.slideDuration || .45;
38271         this.config = c;
38272        
38273     },
38274     /**
38275      * Returns true if this region is currently visible.
38276      * @return {Boolean}
38277      */
38278     isVisible : function(){
38279         return this.visible;
38280     },
38281
38282     /**
38283      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38284      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38285      */
38286     //setCollapsedTitle : function(title){
38287     //    title = title || "&#160;";
38288      //   if(this.collapsedTitleTextEl){
38289       //      this.collapsedTitleTextEl.innerHTML = title;
38290        // }
38291     //},
38292
38293     getBox : function(){
38294         var b;
38295       //  if(!this.collapsed){
38296             b = this.el.getBox(false, true);
38297        // }else{
38298           //  b = this.collapsedEl.getBox(false, true);
38299         //}
38300         return b;
38301     },
38302
38303     getMargins : function(){
38304         return this.margins;
38305         //return this.collapsed ? this.cmargins : this.margins;
38306     },
38307 /*
38308     highlight : function(){
38309         this.el.addClass("x-layout-panel-dragover");
38310     },
38311
38312     unhighlight : function(){
38313         this.el.removeClass("x-layout-panel-dragover");
38314     },
38315 */
38316     updateBox : function(box)
38317     {
38318         if (!this.bodyEl) {
38319             return; // not rendered yet..
38320         }
38321         
38322         this.box = box;
38323         if(!this.collapsed){
38324             this.el.dom.style.left = box.x + "px";
38325             this.el.dom.style.top = box.y + "px";
38326             this.updateBody(box.width, box.height);
38327         }else{
38328             this.collapsedEl.dom.style.left = box.x + "px";
38329             this.collapsedEl.dom.style.top = box.y + "px";
38330             this.collapsedEl.setSize(box.width, box.height);
38331         }
38332         if(this.tabs){
38333             this.tabs.autoSizeTabs();
38334         }
38335     },
38336
38337     updateBody : function(w, h)
38338     {
38339         if(w !== null){
38340             this.el.setWidth(w);
38341             w -= this.el.getBorderWidth("rl");
38342             if(this.config.adjustments){
38343                 w += this.config.adjustments[0];
38344             }
38345         }
38346         if(h !== null && h > 0){
38347             this.el.setHeight(h);
38348             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38349             h -= this.el.getBorderWidth("tb");
38350             if(this.config.adjustments){
38351                 h += this.config.adjustments[1];
38352             }
38353             this.bodyEl.setHeight(h);
38354             if(this.tabs){
38355                 h = this.tabs.syncHeight(h);
38356             }
38357         }
38358         if(this.panelSize){
38359             w = w !== null ? w : this.panelSize.width;
38360             h = h !== null ? h : this.panelSize.height;
38361         }
38362         if(this.activePanel){
38363             var el = this.activePanel.getEl();
38364             w = w !== null ? w : el.getWidth();
38365             h = h !== null ? h : el.getHeight();
38366             this.panelSize = {width: w, height: h};
38367             this.activePanel.setSize(w, h);
38368         }
38369         if(Roo.isIE && this.tabs){
38370             this.tabs.el.repaint();
38371         }
38372     },
38373
38374     /**
38375      * Returns the container element for this region.
38376      * @return {Roo.Element}
38377      */
38378     getEl : function(){
38379         return this.el;
38380     },
38381
38382     /**
38383      * Hides this region.
38384      */
38385     hide : function(){
38386         //if(!this.collapsed){
38387             this.el.dom.style.left = "-2000px";
38388             this.el.hide();
38389         //}else{
38390          //   this.collapsedEl.dom.style.left = "-2000px";
38391          //   this.collapsedEl.hide();
38392        // }
38393         this.visible = false;
38394         this.fireEvent("visibilitychange", this, false);
38395     },
38396
38397     /**
38398      * Shows this region if it was previously hidden.
38399      */
38400     show : function(){
38401         //if(!this.collapsed){
38402             this.el.show();
38403         //}else{
38404         //    this.collapsedEl.show();
38405        // }
38406         this.visible = true;
38407         this.fireEvent("visibilitychange", this, true);
38408     },
38409 /*
38410     closeClicked : function(){
38411         if(this.activePanel){
38412             this.remove(this.activePanel);
38413         }
38414     },
38415
38416     collapseClick : function(e){
38417         if(this.isSlid){
38418            e.stopPropagation();
38419            this.slideIn();
38420         }else{
38421            e.stopPropagation();
38422            this.slideOut();
38423         }
38424     },
38425 */
38426     /**
38427      * Collapses this region.
38428      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38429      */
38430     /*
38431     collapse : function(skipAnim, skipCheck = false){
38432         if(this.collapsed) {
38433             return;
38434         }
38435         
38436         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38437             
38438             this.collapsed = true;
38439             if(this.split){
38440                 this.split.el.hide();
38441             }
38442             if(this.config.animate && skipAnim !== true){
38443                 this.fireEvent("invalidated", this);
38444                 this.animateCollapse();
38445             }else{
38446                 this.el.setLocation(-20000,-20000);
38447                 this.el.hide();
38448                 this.collapsedEl.show();
38449                 this.fireEvent("collapsed", this);
38450                 this.fireEvent("invalidated", this);
38451             }
38452         }
38453         
38454     },
38455 */
38456     animateCollapse : function(){
38457         // overridden
38458     },
38459
38460     /**
38461      * Expands this region if it was previously collapsed.
38462      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38463      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38464      */
38465     /*
38466     expand : function(e, skipAnim){
38467         if(e) {
38468             e.stopPropagation();
38469         }
38470         if(!this.collapsed || this.el.hasActiveFx()) {
38471             return;
38472         }
38473         if(this.isSlid){
38474             this.afterSlideIn();
38475             skipAnim = true;
38476         }
38477         this.collapsed = false;
38478         if(this.config.animate && skipAnim !== true){
38479             this.animateExpand();
38480         }else{
38481             this.el.show();
38482             if(this.split){
38483                 this.split.el.show();
38484             }
38485             this.collapsedEl.setLocation(-2000,-2000);
38486             this.collapsedEl.hide();
38487             this.fireEvent("invalidated", this);
38488             this.fireEvent("expanded", this);
38489         }
38490     },
38491 */
38492     animateExpand : function(){
38493         // overridden
38494     },
38495
38496     initTabs : function()
38497     {
38498         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38499         
38500         var ts = new Roo.bootstrap.panel.Tabs({
38501             el: this.bodyEl.dom,
38502             region : this,
38503             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38504             disableTooltips: this.config.disableTabTips,
38505             toolbar : this.config.toolbar
38506         });
38507         
38508         if(this.config.hideTabs){
38509             ts.stripWrap.setDisplayed(false);
38510         }
38511         this.tabs = ts;
38512         ts.resizeTabs = this.config.resizeTabs === true;
38513         ts.minTabWidth = this.config.minTabWidth || 40;
38514         ts.maxTabWidth = this.config.maxTabWidth || 250;
38515         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38516         ts.monitorResize = false;
38517         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38518         ts.bodyEl.addClass('roo-layout-tabs-body');
38519         this.panels.each(this.initPanelAsTab, this);
38520     },
38521
38522     initPanelAsTab : function(panel){
38523         var ti = this.tabs.addTab(
38524             panel.getEl().id,
38525             panel.getTitle(),
38526             null,
38527             this.config.closeOnTab && panel.isClosable(),
38528             panel.tpl
38529         );
38530         if(panel.tabTip !== undefined){
38531             ti.setTooltip(panel.tabTip);
38532         }
38533         ti.on("activate", function(){
38534               this.setActivePanel(panel);
38535         }, this);
38536         
38537         if(this.config.closeOnTab){
38538             ti.on("beforeclose", function(t, e){
38539                 e.cancel = true;
38540                 this.remove(panel);
38541             }, this);
38542         }
38543         
38544         panel.tabItem = ti;
38545         
38546         return ti;
38547     },
38548
38549     updatePanelTitle : function(panel, title)
38550     {
38551         if(this.activePanel == panel){
38552             this.updateTitle(title);
38553         }
38554         if(this.tabs){
38555             var ti = this.tabs.getTab(panel.getEl().id);
38556             ti.setText(title);
38557             if(panel.tabTip !== undefined){
38558                 ti.setTooltip(panel.tabTip);
38559             }
38560         }
38561     },
38562
38563     updateTitle : function(title){
38564         if(this.titleTextEl && !this.config.title){
38565             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38566         }
38567     },
38568
38569     setActivePanel : function(panel)
38570     {
38571         panel = this.getPanel(panel);
38572         if(this.activePanel && this.activePanel != panel){
38573             if(this.activePanel.setActiveState(false) === false){
38574                 return;
38575             }
38576         }
38577         this.activePanel = panel;
38578         panel.setActiveState(true);
38579         if(this.panelSize){
38580             panel.setSize(this.panelSize.width, this.panelSize.height);
38581         }
38582         if(this.closeBtn){
38583             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38584         }
38585         this.updateTitle(panel.getTitle());
38586         if(this.tabs){
38587             this.fireEvent("invalidated", this);
38588         }
38589         this.fireEvent("panelactivated", this, panel);
38590     },
38591
38592     /**
38593      * Shows the specified panel.
38594      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38595      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38596      */
38597     showPanel : function(panel)
38598     {
38599         panel = this.getPanel(panel);
38600         if(panel){
38601             if(this.tabs){
38602                 var tab = this.tabs.getTab(panel.getEl().id);
38603                 if(tab.isHidden()){
38604                     this.tabs.unhideTab(tab.id);
38605                 }
38606                 tab.activate();
38607             }else{
38608                 this.setActivePanel(panel);
38609             }
38610         }
38611         return panel;
38612     },
38613
38614     /**
38615      * Get the active panel for this region.
38616      * @return {Roo.ContentPanel} The active panel or null
38617      */
38618     getActivePanel : function(){
38619         return this.activePanel;
38620     },
38621
38622     validateVisibility : function(){
38623         if(this.panels.getCount() < 1){
38624             this.updateTitle("&#160;");
38625             this.closeBtn.hide();
38626             this.hide();
38627         }else{
38628             if(!this.isVisible()){
38629                 this.show();
38630             }
38631         }
38632     },
38633
38634     /**
38635      * Adds the passed ContentPanel(s) to this region.
38636      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38637      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38638      */
38639     add : function(panel)
38640     {
38641         if(arguments.length > 1){
38642             for(var i = 0, len = arguments.length; i < len; i++) {
38643                 this.add(arguments[i]);
38644             }
38645             return null;
38646         }
38647         
38648         // if we have not been rendered yet, then we can not really do much of this..
38649         if (!this.bodyEl) {
38650             this.unrendered_panels.push(panel);
38651             return panel;
38652         }
38653         
38654         
38655         
38656         
38657         if(this.hasPanel(panel)){
38658             this.showPanel(panel);
38659             return panel;
38660         }
38661         panel.setRegion(this);
38662         this.panels.add(panel);
38663        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38664             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38665             // and hide them... ???
38666             this.bodyEl.dom.appendChild(panel.getEl().dom);
38667             if(panel.background !== true){
38668                 this.setActivePanel(panel);
38669             }
38670             this.fireEvent("paneladded", this, panel);
38671             return panel;
38672         }
38673         */
38674         if(!this.tabs){
38675             this.initTabs();
38676         }else{
38677             this.initPanelAsTab(panel);
38678         }
38679         
38680         
38681         if(panel.background !== true){
38682             this.tabs.activate(panel.getEl().id);
38683         }
38684         this.fireEvent("paneladded", this, panel);
38685         return panel;
38686     },
38687
38688     /**
38689      * Hides the tab for the specified panel.
38690      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38691      */
38692     hidePanel : function(panel){
38693         if(this.tabs && (panel = this.getPanel(panel))){
38694             this.tabs.hideTab(panel.getEl().id);
38695         }
38696     },
38697
38698     /**
38699      * Unhides the tab for a previously hidden panel.
38700      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38701      */
38702     unhidePanel : function(panel){
38703         if(this.tabs && (panel = this.getPanel(panel))){
38704             this.tabs.unhideTab(panel.getEl().id);
38705         }
38706     },
38707
38708     clearPanels : function(){
38709         while(this.panels.getCount() > 0){
38710              this.remove(this.panels.first());
38711         }
38712     },
38713
38714     /**
38715      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38717      * @param {Boolean} preservePanel Overrides the config preservePanel option
38718      * @return {Roo.ContentPanel} The panel that was removed
38719      */
38720     remove : function(panel, preservePanel)
38721     {
38722         panel = this.getPanel(panel);
38723         if(!panel){
38724             return null;
38725         }
38726         var e = {};
38727         this.fireEvent("beforeremove", this, panel, e);
38728         if(e.cancel === true){
38729             return null;
38730         }
38731         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38732         var panelId = panel.getId();
38733         this.panels.removeKey(panelId);
38734         if(preservePanel){
38735             document.body.appendChild(panel.getEl().dom);
38736         }
38737         if(this.tabs){
38738             this.tabs.removeTab(panel.getEl().id);
38739         }else if (!preservePanel){
38740             this.bodyEl.dom.removeChild(panel.getEl().dom);
38741         }
38742         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38743             var p = this.panels.first();
38744             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38745             tempEl.appendChild(p.getEl().dom);
38746             this.bodyEl.update("");
38747             this.bodyEl.dom.appendChild(p.getEl().dom);
38748             tempEl = null;
38749             this.updateTitle(p.getTitle());
38750             this.tabs = null;
38751             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38752             this.setActivePanel(p);
38753         }
38754         panel.setRegion(null);
38755         if(this.activePanel == panel){
38756             this.activePanel = null;
38757         }
38758         if(this.config.autoDestroy !== false && preservePanel !== true){
38759             try{panel.destroy();}catch(e){}
38760         }
38761         this.fireEvent("panelremoved", this, panel);
38762         return panel;
38763     },
38764
38765     /**
38766      * Returns the TabPanel component used by this region
38767      * @return {Roo.TabPanel}
38768      */
38769     getTabs : function(){
38770         return this.tabs;
38771     },
38772
38773     createTool : function(parentEl, className){
38774         var btn = Roo.DomHelper.append(parentEl, {
38775             tag: "div",
38776             cls: "x-layout-tools-button",
38777             children: [ {
38778                 tag: "div",
38779                 cls: "roo-layout-tools-button-inner " + className,
38780                 html: "&#160;"
38781             }]
38782         }, true);
38783         btn.addClassOnOver("roo-layout-tools-button-over");
38784         return btn;
38785     }
38786 });/*
38787  * Based on:
38788  * Ext JS Library 1.1.1
38789  * Copyright(c) 2006-2007, Ext JS, LLC.
38790  *
38791  * Originally Released Under LGPL - original licence link has changed is not relivant.
38792  *
38793  * Fork - LGPL
38794  * <script type="text/javascript">
38795  */
38796  
38797
38798
38799 /**
38800  * @class Roo.SplitLayoutRegion
38801  * @extends Roo.LayoutRegion
38802  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38803  */
38804 Roo.bootstrap.layout.Split = function(config){
38805     this.cursor = config.cursor;
38806     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38807 };
38808
38809 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38810 {
38811     splitTip : "Drag to resize.",
38812     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38813     useSplitTips : false,
38814
38815     applyConfig : function(config){
38816         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38817     },
38818     
38819     onRender : function(ctr,pos) {
38820         
38821         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38822         if(!this.config.split){
38823             return;
38824         }
38825         if(!this.split){
38826             
38827             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38828                             tag: "div",
38829                             id: this.el.id + "-split",
38830                             cls: "roo-layout-split roo-layout-split-"+this.position,
38831                             html: "&#160;"
38832             });
38833             /** The SplitBar for this region 
38834             * @type Roo.SplitBar */
38835             // does not exist yet...
38836             Roo.log([this.position, this.orientation]);
38837             
38838             this.split = new Roo.bootstrap.SplitBar({
38839                 dragElement : splitEl,
38840                 resizingElement: this.el,
38841                 orientation : this.orientation
38842             });
38843             
38844             this.split.on("moved", this.onSplitMove, this);
38845             this.split.useShim = this.config.useShim === true;
38846             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38847             if(this.useSplitTips){
38848                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38849             }
38850             //if(config.collapsible){
38851             //    this.split.el.on("dblclick", this.collapse,  this);
38852             //}
38853         }
38854         if(typeof this.config.minSize != "undefined"){
38855             this.split.minSize = this.config.minSize;
38856         }
38857         if(typeof this.config.maxSize != "undefined"){
38858             this.split.maxSize = this.config.maxSize;
38859         }
38860         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38861             this.hideSplitter();
38862         }
38863         
38864     },
38865
38866     getHMaxSize : function(){
38867          var cmax = this.config.maxSize || 10000;
38868          var center = this.mgr.getRegion("center");
38869          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38870     },
38871
38872     getVMaxSize : function(){
38873          var cmax = this.config.maxSize || 10000;
38874          var center = this.mgr.getRegion("center");
38875          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38876     },
38877
38878     onSplitMove : function(split, newSize){
38879         this.fireEvent("resized", this, newSize);
38880     },
38881     
38882     /** 
38883      * Returns the {@link Roo.SplitBar} for this region.
38884      * @return {Roo.SplitBar}
38885      */
38886     getSplitBar : function(){
38887         return this.split;
38888     },
38889     
38890     hide : function(){
38891         this.hideSplitter();
38892         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38893     },
38894
38895     hideSplitter : function(){
38896         if(this.split){
38897             this.split.el.setLocation(-2000,-2000);
38898             this.split.el.hide();
38899         }
38900     },
38901
38902     show : function(){
38903         if(this.split){
38904             this.split.el.show();
38905         }
38906         Roo.bootstrap.layout.Split.superclass.show.call(this);
38907     },
38908     
38909     beforeSlide: function(){
38910         if(Roo.isGecko){// firefox overflow auto bug workaround
38911             this.bodyEl.clip();
38912             if(this.tabs) {
38913                 this.tabs.bodyEl.clip();
38914             }
38915             if(this.activePanel){
38916                 this.activePanel.getEl().clip();
38917                 
38918                 if(this.activePanel.beforeSlide){
38919                     this.activePanel.beforeSlide();
38920                 }
38921             }
38922         }
38923     },
38924     
38925     afterSlide : function(){
38926         if(Roo.isGecko){// firefox overflow auto bug workaround
38927             this.bodyEl.unclip();
38928             if(this.tabs) {
38929                 this.tabs.bodyEl.unclip();
38930             }
38931             if(this.activePanel){
38932                 this.activePanel.getEl().unclip();
38933                 if(this.activePanel.afterSlide){
38934                     this.activePanel.afterSlide();
38935                 }
38936             }
38937         }
38938     },
38939
38940     initAutoHide : function(){
38941         if(this.autoHide !== false){
38942             if(!this.autoHideHd){
38943                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38944                 this.autoHideHd = {
38945                     "mouseout": function(e){
38946                         if(!e.within(this.el, true)){
38947                             st.delay(500);
38948                         }
38949                     },
38950                     "mouseover" : function(e){
38951                         st.cancel();
38952                     },
38953                     scope : this
38954                 };
38955             }
38956             this.el.on(this.autoHideHd);
38957         }
38958     },
38959
38960     clearAutoHide : function(){
38961         if(this.autoHide !== false){
38962             this.el.un("mouseout", this.autoHideHd.mouseout);
38963             this.el.un("mouseover", this.autoHideHd.mouseover);
38964         }
38965     },
38966
38967     clearMonitor : function(){
38968         Roo.get(document).un("click", this.slideInIf, this);
38969     },
38970
38971     // these names are backwards but not changed for compat
38972     slideOut : function(){
38973         if(this.isSlid || this.el.hasActiveFx()){
38974             return;
38975         }
38976         this.isSlid = true;
38977         if(this.collapseBtn){
38978             this.collapseBtn.hide();
38979         }
38980         this.closeBtnState = this.closeBtn.getStyle('display');
38981         this.closeBtn.hide();
38982         if(this.stickBtn){
38983             this.stickBtn.show();
38984         }
38985         this.el.show();
38986         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38987         this.beforeSlide();
38988         this.el.setStyle("z-index", 10001);
38989         this.el.slideIn(this.getSlideAnchor(), {
38990             callback: function(){
38991                 this.afterSlide();
38992                 this.initAutoHide();
38993                 Roo.get(document).on("click", this.slideInIf, this);
38994                 this.fireEvent("slideshow", this);
38995             },
38996             scope: this,
38997             block: true
38998         });
38999     },
39000
39001     afterSlideIn : function(){
39002         this.clearAutoHide();
39003         this.isSlid = false;
39004         this.clearMonitor();
39005         this.el.setStyle("z-index", "");
39006         if(this.collapseBtn){
39007             this.collapseBtn.show();
39008         }
39009         this.closeBtn.setStyle('display', this.closeBtnState);
39010         if(this.stickBtn){
39011             this.stickBtn.hide();
39012         }
39013         this.fireEvent("slidehide", this);
39014     },
39015
39016     slideIn : function(cb){
39017         if(!this.isSlid || this.el.hasActiveFx()){
39018             Roo.callback(cb);
39019             return;
39020         }
39021         this.isSlid = false;
39022         this.beforeSlide();
39023         this.el.slideOut(this.getSlideAnchor(), {
39024             callback: function(){
39025                 this.el.setLeftTop(-10000, -10000);
39026                 this.afterSlide();
39027                 this.afterSlideIn();
39028                 Roo.callback(cb);
39029             },
39030             scope: this,
39031             block: true
39032         });
39033     },
39034     
39035     slideInIf : function(e){
39036         if(!e.within(this.el)){
39037             this.slideIn();
39038         }
39039     },
39040
39041     animateCollapse : function(){
39042         this.beforeSlide();
39043         this.el.setStyle("z-index", 20000);
39044         var anchor = this.getSlideAnchor();
39045         this.el.slideOut(anchor, {
39046             callback : function(){
39047                 this.el.setStyle("z-index", "");
39048                 this.collapsedEl.slideIn(anchor, {duration:.3});
39049                 this.afterSlide();
39050                 this.el.setLocation(-10000,-10000);
39051                 this.el.hide();
39052                 this.fireEvent("collapsed", this);
39053             },
39054             scope: this,
39055             block: true
39056         });
39057     },
39058
39059     animateExpand : function(){
39060         this.beforeSlide();
39061         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39062         this.el.setStyle("z-index", 20000);
39063         this.collapsedEl.hide({
39064             duration:.1
39065         });
39066         this.el.slideIn(this.getSlideAnchor(), {
39067             callback : function(){
39068                 this.el.setStyle("z-index", "");
39069                 this.afterSlide();
39070                 if(this.split){
39071                     this.split.el.show();
39072                 }
39073                 this.fireEvent("invalidated", this);
39074                 this.fireEvent("expanded", this);
39075             },
39076             scope: this,
39077             block: true
39078         });
39079     },
39080
39081     anchors : {
39082         "west" : "left",
39083         "east" : "right",
39084         "north" : "top",
39085         "south" : "bottom"
39086     },
39087
39088     sanchors : {
39089         "west" : "l",
39090         "east" : "r",
39091         "north" : "t",
39092         "south" : "b"
39093     },
39094
39095     canchors : {
39096         "west" : "tl-tr",
39097         "east" : "tr-tl",
39098         "north" : "tl-bl",
39099         "south" : "bl-tl"
39100     },
39101
39102     getAnchor : function(){
39103         return this.anchors[this.position];
39104     },
39105
39106     getCollapseAnchor : function(){
39107         return this.canchors[this.position];
39108     },
39109
39110     getSlideAnchor : function(){
39111         return this.sanchors[this.position];
39112     },
39113
39114     getAlignAdj : function(){
39115         var cm = this.cmargins;
39116         switch(this.position){
39117             case "west":
39118                 return [0, 0];
39119             break;
39120             case "east":
39121                 return [0, 0];
39122             break;
39123             case "north":
39124                 return [0, 0];
39125             break;
39126             case "south":
39127                 return [0, 0];
39128             break;
39129         }
39130     },
39131
39132     getExpandAdj : function(){
39133         var c = this.collapsedEl, cm = this.cmargins;
39134         switch(this.position){
39135             case "west":
39136                 return [-(cm.right+c.getWidth()+cm.left), 0];
39137             break;
39138             case "east":
39139                 return [cm.right+c.getWidth()+cm.left, 0];
39140             break;
39141             case "north":
39142                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39143             break;
39144             case "south":
39145                 return [0, cm.top+cm.bottom+c.getHeight()];
39146             break;
39147         }
39148     }
39149 });/*
39150  * Based on:
39151  * Ext JS Library 1.1.1
39152  * Copyright(c) 2006-2007, Ext JS, LLC.
39153  *
39154  * Originally Released Under LGPL - original licence link has changed is not relivant.
39155  *
39156  * Fork - LGPL
39157  * <script type="text/javascript">
39158  */
39159 /*
39160  * These classes are private internal classes
39161  */
39162 Roo.bootstrap.layout.Center = function(config){
39163     config.region = "center";
39164     Roo.bootstrap.layout.Region.call(this, config);
39165     this.visible = true;
39166     this.minWidth = config.minWidth || 20;
39167     this.minHeight = config.minHeight || 20;
39168 };
39169
39170 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39171     hide : function(){
39172         // center panel can't be hidden
39173     },
39174     
39175     show : function(){
39176         // center panel can't be hidden
39177     },
39178     
39179     getMinWidth: function(){
39180         return this.minWidth;
39181     },
39182     
39183     getMinHeight: function(){
39184         return this.minHeight;
39185     }
39186 });
39187
39188
39189
39190
39191  
39192
39193
39194
39195
39196
39197
39198 Roo.bootstrap.layout.North = function(config)
39199 {
39200     config.region = 'north';
39201     config.cursor = 'n-resize';
39202     
39203     Roo.bootstrap.layout.Split.call(this, config);
39204     
39205     
39206     if(this.split){
39207         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39208         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39209         this.split.el.addClass("roo-layout-split-v");
39210     }
39211     var size = config.initialSize || config.height;
39212     if(typeof size != "undefined"){
39213         this.el.setHeight(size);
39214     }
39215 };
39216 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39217 {
39218     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39219     
39220     
39221     
39222     getBox : function(){
39223         if(this.collapsed){
39224             return this.collapsedEl.getBox();
39225         }
39226         var box = this.el.getBox();
39227         if(this.split){
39228             box.height += this.split.el.getHeight();
39229         }
39230         return box;
39231     },
39232     
39233     updateBox : function(box){
39234         if(this.split && !this.collapsed){
39235             box.height -= this.split.el.getHeight();
39236             this.split.el.setLeft(box.x);
39237             this.split.el.setTop(box.y+box.height);
39238             this.split.el.setWidth(box.width);
39239         }
39240         if(this.collapsed){
39241             this.updateBody(box.width, null);
39242         }
39243         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39244     }
39245 });
39246
39247
39248
39249
39250
39251 Roo.bootstrap.layout.South = function(config){
39252     config.region = 'south';
39253     config.cursor = 's-resize';
39254     Roo.bootstrap.layout.Split.call(this, config);
39255     if(this.split){
39256         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39257         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39258         this.split.el.addClass("roo-layout-split-v");
39259     }
39260     var size = config.initialSize || config.height;
39261     if(typeof size != "undefined"){
39262         this.el.setHeight(size);
39263     }
39264 };
39265
39266 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39267     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39268     getBox : function(){
39269         if(this.collapsed){
39270             return this.collapsedEl.getBox();
39271         }
39272         var box = this.el.getBox();
39273         if(this.split){
39274             var sh = this.split.el.getHeight();
39275             box.height += sh;
39276             box.y -= sh;
39277         }
39278         return box;
39279     },
39280     
39281     updateBox : function(box){
39282         if(this.split && !this.collapsed){
39283             var sh = this.split.el.getHeight();
39284             box.height -= sh;
39285             box.y += sh;
39286             this.split.el.setLeft(box.x);
39287             this.split.el.setTop(box.y-sh);
39288             this.split.el.setWidth(box.width);
39289         }
39290         if(this.collapsed){
39291             this.updateBody(box.width, null);
39292         }
39293         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39294     }
39295 });
39296
39297 Roo.bootstrap.layout.East = function(config){
39298     config.region = "east";
39299     config.cursor = "e-resize";
39300     Roo.bootstrap.layout.Split.call(this, config);
39301     if(this.split){
39302         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39303         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39304         this.split.el.addClass("roo-layout-split-h");
39305     }
39306     var size = config.initialSize || config.width;
39307     if(typeof size != "undefined"){
39308         this.el.setWidth(size);
39309     }
39310 };
39311 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39312     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39313     getBox : function(){
39314         if(this.collapsed){
39315             return this.collapsedEl.getBox();
39316         }
39317         var box = this.el.getBox();
39318         if(this.split){
39319             var sw = this.split.el.getWidth();
39320             box.width += sw;
39321             box.x -= sw;
39322         }
39323         return box;
39324     },
39325
39326     updateBox : function(box){
39327         if(this.split && !this.collapsed){
39328             var sw = this.split.el.getWidth();
39329             box.width -= sw;
39330             this.split.el.setLeft(box.x);
39331             this.split.el.setTop(box.y);
39332             this.split.el.setHeight(box.height);
39333             box.x += sw;
39334         }
39335         if(this.collapsed){
39336             this.updateBody(null, box.height);
39337         }
39338         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39339     }
39340 });
39341
39342 Roo.bootstrap.layout.West = function(config){
39343     config.region = "west";
39344     config.cursor = "w-resize";
39345     
39346     Roo.bootstrap.layout.Split.call(this, config);
39347     if(this.split){
39348         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39349         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39350         this.split.el.addClass("roo-layout-split-h");
39351     }
39352     
39353 };
39354 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39355     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39356     
39357     onRender: function(ctr, pos)
39358     {
39359         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39360         var size = this.config.initialSize || this.config.width;
39361         if(typeof size != "undefined"){
39362             this.el.setWidth(size);
39363         }
39364     },
39365     
39366     getBox : function(){
39367         if(this.collapsed){
39368             return this.collapsedEl.getBox();
39369         }
39370         var box = this.el.getBox();
39371         if(this.split){
39372             box.width += this.split.el.getWidth();
39373         }
39374         return box;
39375     },
39376     
39377     updateBox : function(box){
39378         if(this.split && !this.collapsed){
39379             var sw = this.split.el.getWidth();
39380             box.width -= sw;
39381             this.split.el.setLeft(box.x+box.width);
39382             this.split.el.setTop(box.y);
39383             this.split.el.setHeight(box.height);
39384         }
39385         if(this.collapsed){
39386             this.updateBody(null, box.height);
39387         }
39388         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39389     }
39390 });Roo.namespace("Roo.bootstrap.panel");/*
39391  * Based on:
39392  * Ext JS Library 1.1.1
39393  * Copyright(c) 2006-2007, Ext JS, LLC.
39394  *
39395  * Originally Released Under LGPL - original licence link has changed is not relivant.
39396  *
39397  * Fork - LGPL
39398  * <script type="text/javascript">
39399  */
39400 /**
39401  * @class Roo.ContentPanel
39402  * @extends Roo.util.Observable
39403  * A basic ContentPanel element.
39404  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39405  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39406  * @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
39407  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39408  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39409  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39410  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39411  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39412  * @cfg {String} title          The title for this panel
39413  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39414  * @cfg {String} url            Calls {@link #setUrl} with this value
39415  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39416  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39417  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39418  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39419  * @cfg {Boolean} badges render the badges
39420  * @cfg {String} cls  extra classes to use  
39421  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39422
39423  * @constructor
39424  * Create a new ContentPanel.
39425  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39426  * @param {String/Object} config A string to set only the title or a config object
39427  * @param {String} content (optional) Set the HTML content for this panel
39428  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39429  */
39430 Roo.bootstrap.panel.Content = function( config){
39431     
39432     this.tpl = config.tpl || false;
39433     
39434     var el = config.el;
39435     var content = config.content;
39436
39437     if(config.autoCreate){ // xtype is available if this is called from factory
39438         el = Roo.id();
39439     }
39440     this.el = Roo.get(el);
39441     if(!this.el && config && config.autoCreate){
39442         if(typeof config.autoCreate == "object"){
39443             if(!config.autoCreate.id){
39444                 config.autoCreate.id = config.id||el;
39445             }
39446             this.el = Roo.DomHelper.append(document.body,
39447                         config.autoCreate, true);
39448         }else{
39449             var elcfg =  {
39450                 tag: "div",
39451                 cls: (config.cls || '') +
39452                     (config.background ? ' bg-' + config.background : '') +
39453                     " roo-layout-inactive-content",
39454                 id: config.id||el
39455             };
39456             if (config.html) {
39457                 elcfg.html = config.html;
39458                 
39459             }
39460                         
39461             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39462         }
39463     } 
39464     this.closable = false;
39465     this.loaded = false;
39466     this.active = false;
39467    
39468       
39469     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39470         
39471         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39472         
39473         this.wrapEl = this.el; //this.el.wrap();
39474         var ti = [];
39475         if (config.toolbar.items) {
39476             ti = config.toolbar.items ;
39477             delete config.toolbar.items ;
39478         }
39479         
39480         var nitems = [];
39481         this.toolbar.render(this.wrapEl, 'before');
39482         for(var i =0;i < ti.length;i++) {
39483           //  Roo.log(['add child', items[i]]);
39484             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39485         }
39486         this.toolbar.items = nitems;
39487         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39488         delete config.toolbar;
39489         
39490     }
39491     /*
39492     // xtype created footer. - not sure if will work as we normally have to render first..
39493     if (this.footer && !this.footer.el && this.footer.xtype) {
39494         if (!this.wrapEl) {
39495             this.wrapEl = this.el.wrap();
39496         }
39497     
39498         this.footer.container = this.wrapEl.createChild();
39499          
39500         this.footer = Roo.factory(this.footer, Roo);
39501         
39502     }
39503     */
39504     
39505      if(typeof config == "string"){
39506         this.title = config;
39507     }else{
39508         Roo.apply(this, config);
39509     }
39510     
39511     if(this.resizeEl){
39512         this.resizeEl = Roo.get(this.resizeEl, true);
39513     }else{
39514         this.resizeEl = this.el;
39515     }
39516     // handle view.xtype
39517     
39518  
39519     
39520     
39521     this.addEvents({
39522         /**
39523          * @event activate
39524          * Fires when this panel is activated. 
39525          * @param {Roo.ContentPanel} this
39526          */
39527         "activate" : true,
39528         /**
39529          * @event deactivate
39530          * Fires when this panel is activated. 
39531          * @param {Roo.ContentPanel} this
39532          */
39533         "deactivate" : true,
39534
39535         /**
39536          * @event resize
39537          * Fires when this panel is resized if fitToFrame is true.
39538          * @param {Roo.ContentPanel} this
39539          * @param {Number} width The width after any component adjustments
39540          * @param {Number} height The height after any component adjustments
39541          */
39542         "resize" : true,
39543         
39544          /**
39545          * @event render
39546          * Fires when this tab is created
39547          * @param {Roo.ContentPanel} this
39548          */
39549         "render" : true
39550         
39551         
39552         
39553     });
39554     
39555
39556     
39557     
39558     if(this.autoScroll){
39559         this.resizeEl.setStyle("overflow", "auto");
39560     } else {
39561         // fix randome scrolling
39562         //this.el.on('scroll', function() {
39563         //    Roo.log('fix random scolling');
39564         //    this.scrollTo('top',0); 
39565         //});
39566     }
39567     content = content || this.content;
39568     if(content){
39569         this.setContent(content);
39570     }
39571     if(config && config.url){
39572         this.setUrl(this.url, this.params, this.loadOnce);
39573     }
39574     
39575     
39576     
39577     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39578     
39579     if (this.view && typeof(this.view.xtype) != 'undefined') {
39580         this.view.el = this.el.appendChild(document.createElement("div"));
39581         this.view = Roo.factory(this.view); 
39582         this.view.render  &&  this.view.render(false, '');  
39583     }
39584     
39585     
39586     this.fireEvent('render', this);
39587 };
39588
39589 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39590     
39591     cls : '',
39592     background : '',
39593     
39594     tabTip : '',
39595     
39596     setRegion : function(region){
39597         this.region = region;
39598         this.setActiveClass(region && !this.background);
39599     },
39600     
39601     
39602     setActiveClass: function(state)
39603     {
39604         if(state){
39605            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39606            this.el.setStyle('position','relative');
39607         }else{
39608            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39609            this.el.setStyle('position', 'absolute');
39610         } 
39611     },
39612     
39613     /**
39614      * Returns the toolbar for this Panel if one was configured. 
39615      * @return {Roo.Toolbar} 
39616      */
39617     getToolbar : function(){
39618         return this.toolbar;
39619     },
39620     
39621     setActiveState : function(active)
39622     {
39623         this.active = active;
39624         this.setActiveClass(active);
39625         if(!active){
39626             if(this.fireEvent("deactivate", this) === false){
39627                 return false;
39628             }
39629             return true;
39630         }
39631         this.fireEvent("activate", this);
39632         return true;
39633     },
39634     /**
39635      * Updates this panel's element
39636      * @param {String} content The new content
39637      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39638     */
39639     setContent : function(content, loadScripts){
39640         this.el.update(content, loadScripts);
39641     },
39642
39643     ignoreResize : function(w, h){
39644         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39645             return true;
39646         }else{
39647             this.lastSize = {width: w, height: h};
39648             return false;
39649         }
39650     },
39651     /**
39652      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39653      * @return {Roo.UpdateManager} The UpdateManager
39654      */
39655     getUpdateManager : function(){
39656         return this.el.getUpdateManager();
39657     },
39658      /**
39659      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39660      * @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:
39661 <pre><code>
39662 panel.load({
39663     url: "your-url.php",
39664     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39665     callback: yourFunction,
39666     scope: yourObject, //(optional scope)
39667     discardUrl: false,
39668     nocache: false,
39669     text: "Loading...",
39670     timeout: 30,
39671     scripts: false
39672 });
39673 </code></pre>
39674      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39675      * 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.
39676      * @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}
39677      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39678      * @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.
39679      * @return {Roo.ContentPanel} this
39680      */
39681     load : function(){
39682         var um = this.el.getUpdateManager();
39683         um.update.apply(um, arguments);
39684         return this;
39685     },
39686
39687
39688     /**
39689      * 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.
39690      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39691      * @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)
39692      * @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)
39693      * @return {Roo.UpdateManager} The UpdateManager
39694      */
39695     setUrl : function(url, params, loadOnce){
39696         if(this.refreshDelegate){
39697             this.removeListener("activate", this.refreshDelegate);
39698         }
39699         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39700         this.on("activate", this.refreshDelegate);
39701         return this.el.getUpdateManager();
39702     },
39703     
39704     _handleRefresh : function(url, params, loadOnce){
39705         if(!loadOnce || !this.loaded){
39706             var updater = this.el.getUpdateManager();
39707             updater.update(url, params, this._setLoaded.createDelegate(this));
39708         }
39709     },
39710     
39711     _setLoaded : function(){
39712         this.loaded = true;
39713     }, 
39714     
39715     /**
39716      * Returns this panel's id
39717      * @return {String} 
39718      */
39719     getId : function(){
39720         return this.el.id;
39721     },
39722     
39723     /** 
39724      * Returns this panel's element - used by regiosn to add.
39725      * @return {Roo.Element} 
39726      */
39727     getEl : function(){
39728         return this.wrapEl || this.el;
39729     },
39730     
39731    
39732     
39733     adjustForComponents : function(width, height)
39734     {
39735         //Roo.log('adjustForComponents ');
39736         if(this.resizeEl != this.el){
39737             width -= this.el.getFrameWidth('lr');
39738             height -= this.el.getFrameWidth('tb');
39739         }
39740         if(this.toolbar){
39741             var te = this.toolbar.getEl();
39742             te.setWidth(width);
39743             height -= te.getHeight();
39744         }
39745         if(this.footer){
39746             var te = this.footer.getEl();
39747             te.setWidth(width);
39748             height -= te.getHeight();
39749         }
39750         
39751         
39752         if(this.adjustments){
39753             width += this.adjustments[0];
39754             height += this.adjustments[1];
39755         }
39756         return {"width": width, "height": height};
39757     },
39758     
39759     setSize : function(width, height){
39760         if(this.fitToFrame && !this.ignoreResize(width, height)){
39761             if(this.fitContainer && this.resizeEl != this.el){
39762                 this.el.setSize(width, height);
39763             }
39764             var size = this.adjustForComponents(width, height);
39765             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39766             this.fireEvent('resize', this, size.width, size.height);
39767         }
39768     },
39769     
39770     /**
39771      * Returns this panel's title
39772      * @return {String} 
39773      */
39774     getTitle : function(){
39775         
39776         if (typeof(this.title) != 'object') {
39777             return this.title;
39778         }
39779         
39780         var t = '';
39781         for (var k in this.title) {
39782             if (!this.title.hasOwnProperty(k)) {
39783                 continue;
39784             }
39785             
39786             if (k.indexOf('-') >= 0) {
39787                 var s = k.split('-');
39788                 for (var i = 0; i<s.length; i++) {
39789                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39790                 }
39791             } else {
39792                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39793             }
39794         }
39795         return t;
39796     },
39797     
39798     /**
39799      * Set this panel's title
39800      * @param {String} title
39801      */
39802     setTitle : function(title){
39803         this.title = title;
39804         if(this.region){
39805             this.region.updatePanelTitle(this, title);
39806         }
39807     },
39808     
39809     /**
39810      * Returns true is this panel was configured to be closable
39811      * @return {Boolean} 
39812      */
39813     isClosable : function(){
39814         return this.closable;
39815     },
39816     
39817     beforeSlide : function(){
39818         this.el.clip();
39819         this.resizeEl.clip();
39820     },
39821     
39822     afterSlide : function(){
39823         this.el.unclip();
39824         this.resizeEl.unclip();
39825     },
39826     
39827     /**
39828      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39829      *   Will fail silently if the {@link #setUrl} method has not been called.
39830      *   This does not activate the panel, just updates its content.
39831      */
39832     refresh : function(){
39833         if(this.refreshDelegate){
39834            this.loaded = false;
39835            this.refreshDelegate();
39836         }
39837     },
39838     
39839     /**
39840      * Destroys this panel
39841      */
39842     destroy : function(){
39843         this.el.removeAllListeners();
39844         var tempEl = document.createElement("span");
39845         tempEl.appendChild(this.el.dom);
39846         tempEl.innerHTML = "";
39847         this.el.remove();
39848         this.el = null;
39849     },
39850     
39851     /**
39852      * form - if the content panel contains a form - this is a reference to it.
39853      * @type {Roo.form.Form}
39854      */
39855     form : false,
39856     /**
39857      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39858      *    This contains a reference to it.
39859      * @type {Roo.View}
39860      */
39861     view : false,
39862     
39863       /**
39864      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39865      * <pre><code>
39866
39867 layout.addxtype({
39868        xtype : 'Form',
39869        items: [ .... ]
39870    }
39871 );
39872
39873 </code></pre>
39874      * @param {Object} cfg Xtype definition of item to add.
39875      */
39876     
39877     
39878     getChildContainer: function () {
39879         return this.getEl();
39880     }
39881     
39882     
39883     /*
39884         var  ret = new Roo.factory(cfg);
39885         return ret;
39886         
39887         
39888         // add form..
39889         if (cfg.xtype.match(/^Form$/)) {
39890             
39891             var el;
39892             //if (this.footer) {
39893             //    el = this.footer.container.insertSibling(false, 'before');
39894             //} else {
39895                 el = this.el.createChild();
39896             //}
39897
39898             this.form = new  Roo.form.Form(cfg);
39899             
39900             
39901             if ( this.form.allItems.length) {
39902                 this.form.render(el.dom);
39903             }
39904             return this.form;
39905         }
39906         // should only have one of theses..
39907         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39908             // views.. should not be just added - used named prop 'view''
39909             
39910             cfg.el = this.el.appendChild(document.createElement("div"));
39911             // factory?
39912             
39913             var ret = new Roo.factory(cfg);
39914              
39915              ret.render && ret.render(false, ''); // render blank..
39916             this.view = ret;
39917             return ret;
39918         }
39919         return false;
39920     }
39921     \*/
39922 });
39923  
39924 /**
39925  * @class Roo.bootstrap.panel.Grid
39926  * @extends Roo.bootstrap.panel.Content
39927  * @constructor
39928  * Create a new GridPanel.
39929  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39930  * @param {Object} config A the config object
39931   
39932  */
39933
39934
39935
39936 Roo.bootstrap.panel.Grid = function(config)
39937 {
39938     
39939       
39940     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39941         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39942
39943     config.el = this.wrapper;
39944     //this.el = this.wrapper;
39945     
39946       if (config.container) {
39947         // ctor'ed from a Border/panel.grid
39948         
39949         
39950         this.wrapper.setStyle("overflow", "hidden");
39951         this.wrapper.addClass('roo-grid-container');
39952
39953     }
39954     
39955     
39956     if(config.toolbar){
39957         var tool_el = this.wrapper.createChild();    
39958         this.toolbar = Roo.factory(config.toolbar);
39959         var ti = [];
39960         if (config.toolbar.items) {
39961             ti = config.toolbar.items ;
39962             delete config.toolbar.items ;
39963         }
39964         
39965         var nitems = [];
39966         this.toolbar.render(tool_el);
39967         for(var i =0;i < ti.length;i++) {
39968           //  Roo.log(['add child', items[i]]);
39969             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39970         }
39971         this.toolbar.items = nitems;
39972         
39973         delete config.toolbar;
39974     }
39975     
39976     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39977     config.grid.scrollBody = true;;
39978     config.grid.monitorWindowResize = false; // turn off autosizing
39979     config.grid.autoHeight = false;
39980     config.grid.autoWidth = false;
39981     
39982     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39983     
39984     if (config.background) {
39985         // render grid on panel activation (if panel background)
39986         this.on('activate', function(gp) {
39987             if (!gp.grid.rendered) {
39988                 gp.grid.render(this.wrapper);
39989                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39990             }
39991         });
39992             
39993     } else {
39994         this.grid.render(this.wrapper);
39995         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39996
39997     }
39998     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39999     // ??? needed ??? config.el = this.wrapper;
40000     
40001     
40002     
40003   
40004     // xtype created footer. - not sure if will work as we normally have to render first..
40005     if (this.footer && !this.footer.el && this.footer.xtype) {
40006         
40007         var ctr = this.grid.getView().getFooterPanel(true);
40008         this.footer.dataSource = this.grid.dataSource;
40009         this.footer = Roo.factory(this.footer, Roo);
40010         this.footer.render(ctr);
40011         
40012     }
40013     
40014     
40015     
40016     
40017      
40018 };
40019
40020 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40021     getId : function(){
40022         return this.grid.id;
40023     },
40024     
40025     /**
40026      * Returns the grid for this panel
40027      * @return {Roo.bootstrap.Table} 
40028      */
40029     getGrid : function(){
40030         return this.grid;    
40031     },
40032     
40033     setSize : function(width, height){
40034         if(!this.ignoreResize(width, height)){
40035             var grid = this.grid;
40036             var size = this.adjustForComponents(width, height);
40037             // tfoot is not a footer?
40038           
40039             
40040             var gridel = grid.getGridEl();
40041             gridel.setSize(size.width, size.height);
40042             
40043             var tbd = grid.getGridEl().select('tbody', true).first();
40044             var thd = grid.getGridEl().select('thead',true).first();
40045             var tbf= grid.getGridEl().select('tfoot', true).first();
40046
40047             if (tbf) {
40048                 size.height -= thd.getHeight();
40049             }
40050             if (thd) {
40051                 size.height -= thd.getHeight();
40052             }
40053             
40054             tbd.setSize(size.width, size.height );
40055             // this is for the account management tab -seems to work there.
40056             var thd = grid.getGridEl().select('thead',true).first();
40057             //if (tbd) {
40058             //    tbd.setSize(size.width, size.height - thd.getHeight());
40059             //}
40060              
40061             grid.autoSize();
40062         }
40063     },
40064      
40065     
40066     
40067     beforeSlide : function(){
40068         this.grid.getView().scroller.clip();
40069     },
40070     
40071     afterSlide : function(){
40072         this.grid.getView().scroller.unclip();
40073     },
40074     
40075     destroy : function(){
40076         this.grid.destroy();
40077         delete this.grid;
40078         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40079     }
40080 });
40081
40082 /**
40083  * @class Roo.bootstrap.panel.Nest
40084  * @extends Roo.bootstrap.panel.Content
40085  * @constructor
40086  * Create a new Panel, that can contain a layout.Border.
40087  * 
40088  * 
40089  * @param {Roo.BorderLayout} layout The layout for this panel
40090  * @param {String/Object} config A string to set only the title or a config object
40091  */
40092 Roo.bootstrap.panel.Nest = function(config)
40093 {
40094     // construct with only one argument..
40095     /* FIXME - implement nicer consturctors
40096     if (layout.layout) {
40097         config = layout;
40098         layout = config.layout;
40099         delete config.layout;
40100     }
40101     if (layout.xtype && !layout.getEl) {
40102         // then layout needs constructing..
40103         layout = Roo.factory(layout, Roo);
40104     }
40105     */
40106     
40107     config.el =  config.layout.getEl();
40108     
40109     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40110     
40111     config.layout.monitorWindowResize = false; // turn off autosizing
40112     this.layout = config.layout;
40113     this.layout.getEl().addClass("roo-layout-nested-layout");
40114     this.layout.parent = this;
40115     
40116     
40117     
40118     
40119 };
40120
40121 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40122
40123     setSize : function(width, height){
40124         if(!this.ignoreResize(width, height)){
40125             var size = this.adjustForComponents(width, height);
40126             var el = this.layout.getEl();
40127             if (size.height < 1) {
40128                 el.setWidth(size.width);   
40129             } else {
40130                 el.setSize(size.width, size.height);
40131             }
40132             var touch = el.dom.offsetWidth;
40133             this.layout.layout();
40134             // ie requires a double layout on the first pass
40135             if(Roo.isIE && !this.initialized){
40136                 this.initialized = true;
40137                 this.layout.layout();
40138             }
40139         }
40140     },
40141     
40142     // activate all subpanels if not currently active..
40143     
40144     setActiveState : function(active){
40145         this.active = active;
40146         this.setActiveClass(active);
40147         
40148         if(!active){
40149             this.fireEvent("deactivate", this);
40150             return;
40151         }
40152         
40153         this.fireEvent("activate", this);
40154         // not sure if this should happen before or after..
40155         if (!this.layout) {
40156             return; // should not happen..
40157         }
40158         var reg = false;
40159         for (var r in this.layout.regions) {
40160             reg = this.layout.getRegion(r);
40161             if (reg.getActivePanel()) {
40162                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40163                 reg.setActivePanel(reg.getActivePanel());
40164                 continue;
40165             }
40166             if (!reg.panels.length) {
40167                 continue;
40168             }
40169             reg.showPanel(reg.getPanel(0));
40170         }
40171         
40172         
40173         
40174         
40175     },
40176     
40177     /**
40178      * Returns the nested BorderLayout for this panel
40179      * @return {Roo.BorderLayout} 
40180      */
40181     getLayout : function(){
40182         return this.layout;
40183     },
40184     
40185      /**
40186      * Adds a xtype elements to the layout of the nested panel
40187      * <pre><code>
40188
40189 panel.addxtype({
40190        xtype : 'ContentPanel',
40191        region: 'west',
40192        items: [ .... ]
40193    }
40194 );
40195
40196 panel.addxtype({
40197         xtype : 'NestedLayoutPanel',
40198         region: 'west',
40199         layout: {
40200            center: { },
40201            west: { }   
40202         },
40203         items : [ ... list of content panels or nested layout panels.. ]
40204    }
40205 );
40206 </code></pre>
40207      * @param {Object} cfg Xtype definition of item to add.
40208      */
40209     addxtype : function(cfg) {
40210         return this.layout.addxtype(cfg);
40211     
40212     }
40213 });/*
40214  * Based on:
40215  * Ext JS Library 1.1.1
40216  * Copyright(c) 2006-2007, Ext JS, LLC.
40217  *
40218  * Originally Released Under LGPL - original licence link has changed is not relivant.
40219  *
40220  * Fork - LGPL
40221  * <script type="text/javascript">
40222  */
40223 /**
40224  * @class Roo.TabPanel
40225  * @extends Roo.util.Observable
40226  * A lightweight tab container.
40227  * <br><br>
40228  * Usage:
40229  * <pre><code>
40230 // basic tabs 1, built from existing content
40231 var tabs = new Roo.TabPanel("tabs1");
40232 tabs.addTab("script", "View Script");
40233 tabs.addTab("markup", "View Markup");
40234 tabs.activate("script");
40235
40236 // more advanced tabs, built from javascript
40237 var jtabs = new Roo.TabPanel("jtabs");
40238 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40239
40240 // set up the UpdateManager
40241 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40242 var updater = tab2.getUpdateManager();
40243 updater.setDefaultUrl("ajax1.htm");
40244 tab2.on('activate', updater.refresh, updater, true);
40245
40246 // Use setUrl for Ajax loading
40247 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40248 tab3.setUrl("ajax2.htm", null, true);
40249
40250 // Disabled tab
40251 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40252 tab4.disable();
40253
40254 jtabs.activate("jtabs-1");
40255  * </code></pre>
40256  * @constructor
40257  * Create a new TabPanel.
40258  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40259  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40260  */
40261 Roo.bootstrap.panel.Tabs = function(config){
40262     /**
40263     * The container element for this TabPanel.
40264     * @type Roo.Element
40265     */
40266     this.el = Roo.get(config.el);
40267     delete config.el;
40268     if(config){
40269         if(typeof config == "boolean"){
40270             this.tabPosition = config ? "bottom" : "top";
40271         }else{
40272             Roo.apply(this, config);
40273         }
40274     }
40275     
40276     if(this.tabPosition == "bottom"){
40277         // if tabs are at the bottom = create the body first.
40278         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40279         this.el.addClass("roo-tabs-bottom");
40280     }
40281     // next create the tabs holders
40282     
40283     if (this.tabPosition == "west"){
40284         
40285         var reg = this.region; // fake it..
40286         while (reg) {
40287             if (!reg.mgr.parent) {
40288                 break;
40289             }
40290             reg = reg.mgr.parent.region;
40291         }
40292         Roo.log("got nest?");
40293         Roo.log(reg);
40294         if (reg.mgr.getRegion('west')) {
40295             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40296             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40297             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40298             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40299             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40300         
40301             
40302         }
40303         
40304         
40305     } else {
40306      
40307         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40308         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40309         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40310         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40311     }
40312     
40313     
40314     if(Roo.isIE){
40315         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40316     }
40317     
40318     // finally - if tabs are at the top, then create the body last..
40319     if(this.tabPosition != "bottom"){
40320         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40321          * @type Roo.Element
40322          */
40323         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40324         this.el.addClass("roo-tabs-top");
40325     }
40326     this.items = [];
40327
40328     this.bodyEl.setStyle("position", "relative");
40329
40330     this.active = null;
40331     this.activateDelegate = this.activate.createDelegate(this);
40332
40333     this.addEvents({
40334         /**
40335          * @event tabchange
40336          * Fires when the active tab changes
40337          * @param {Roo.TabPanel} this
40338          * @param {Roo.TabPanelItem} activePanel The new active tab
40339          */
40340         "tabchange": true,
40341         /**
40342          * @event beforetabchange
40343          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40344          * @param {Roo.TabPanel} this
40345          * @param {Object} e Set cancel to true on this object to cancel the tab change
40346          * @param {Roo.TabPanelItem} tab The tab being changed to
40347          */
40348         "beforetabchange" : true
40349     });
40350
40351     Roo.EventManager.onWindowResize(this.onResize, this);
40352     this.cpad = this.el.getPadding("lr");
40353     this.hiddenCount = 0;
40354
40355
40356     // toolbar on the tabbar support...
40357     if (this.toolbar) {
40358         alert("no toolbar support yet");
40359         this.toolbar  = false;
40360         /*
40361         var tcfg = this.toolbar;
40362         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40363         this.toolbar = new Roo.Toolbar(tcfg);
40364         if (Roo.isSafari) {
40365             var tbl = tcfg.container.child('table', true);
40366             tbl.setAttribute('width', '100%');
40367         }
40368         */
40369         
40370     }
40371    
40372
40373
40374     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40375 };
40376
40377 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40378     /*
40379      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40380      */
40381     tabPosition : "top",
40382     /*
40383      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40384      */
40385     currentTabWidth : 0,
40386     /*
40387      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40388      */
40389     minTabWidth : 40,
40390     /*
40391      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40392      */
40393     maxTabWidth : 250,
40394     /*
40395      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40396      */
40397     preferredTabWidth : 175,
40398     /*
40399      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40400      */
40401     resizeTabs : false,
40402     /*
40403      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40404      */
40405     monitorResize : true,
40406     /*
40407      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40408      */
40409     toolbar : false,  // set by caller..
40410     
40411     region : false, /// set by caller
40412     
40413     disableTooltips : true, // not used yet...
40414
40415     /**
40416      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40417      * @param {String} id The id of the div to use <b>or create</b>
40418      * @param {String} text The text for the tab
40419      * @param {String} content (optional) Content to put in the TabPanelItem body
40420      * @param {Boolean} closable (optional) True to create a close icon on the tab
40421      * @return {Roo.TabPanelItem} The created TabPanelItem
40422      */
40423     addTab : function(id, text, content, closable, tpl)
40424     {
40425         var item = new Roo.bootstrap.panel.TabItem({
40426             panel: this,
40427             id : id,
40428             text : text,
40429             closable : closable,
40430             tpl : tpl
40431         });
40432         this.addTabItem(item);
40433         if(content){
40434             item.setContent(content);
40435         }
40436         return item;
40437     },
40438
40439     /**
40440      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40441      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40442      * @return {Roo.TabPanelItem}
40443      */
40444     getTab : function(id){
40445         return this.items[id];
40446     },
40447
40448     /**
40449      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40450      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40451      */
40452     hideTab : function(id){
40453         var t = this.items[id];
40454         if(!t.isHidden()){
40455            t.setHidden(true);
40456            this.hiddenCount++;
40457            this.autoSizeTabs();
40458         }
40459     },
40460
40461     /**
40462      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40463      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40464      */
40465     unhideTab : function(id){
40466         var t = this.items[id];
40467         if(t.isHidden()){
40468            t.setHidden(false);
40469            this.hiddenCount--;
40470            this.autoSizeTabs();
40471         }
40472     },
40473
40474     /**
40475      * Adds an existing {@link Roo.TabPanelItem}.
40476      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40477      */
40478     addTabItem : function(item)
40479     {
40480         this.items[item.id] = item;
40481         this.items.push(item);
40482         this.autoSizeTabs();
40483       //  if(this.resizeTabs){
40484     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40485   //         this.autoSizeTabs();
40486 //        }else{
40487 //            item.autoSize();
40488        // }
40489     },
40490
40491     /**
40492      * Removes a {@link Roo.TabPanelItem}.
40493      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40494      */
40495     removeTab : function(id){
40496         var items = this.items;
40497         var tab = items[id];
40498         if(!tab) { return; }
40499         var index = items.indexOf(tab);
40500         if(this.active == tab && items.length > 1){
40501             var newTab = this.getNextAvailable(index);
40502             if(newTab) {
40503                 newTab.activate();
40504             }
40505         }
40506         this.stripEl.dom.removeChild(tab.pnode.dom);
40507         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40508             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40509         }
40510         items.splice(index, 1);
40511         delete this.items[tab.id];
40512         tab.fireEvent("close", tab);
40513         tab.purgeListeners();
40514         this.autoSizeTabs();
40515     },
40516
40517     getNextAvailable : function(start){
40518         var items = this.items;
40519         var index = start;
40520         // look for a next tab that will slide over to
40521         // replace the one being removed
40522         while(index < items.length){
40523             var item = items[++index];
40524             if(item && !item.isHidden()){
40525                 return item;
40526             }
40527         }
40528         // if one isn't found select the previous tab (on the left)
40529         index = start;
40530         while(index >= 0){
40531             var item = items[--index];
40532             if(item && !item.isHidden()){
40533                 return item;
40534             }
40535         }
40536         return null;
40537     },
40538
40539     /**
40540      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40541      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40542      */
40543     disableTab : function(id){
40544         var tab = this.items[id];
40545         if(tab && this.active != tab){
40546             tab.disable();
40547         }
40548     },
40549
40550     /**
40551      * Enables a {@link Roo.TabPanelItem} that is disabled.
40552      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40553      */
40554     enableTab : function(id){
40555         var tab = this.items[id];
40556         tab.enable();
40557     },
40558
40559     /**
40560      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40561      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40562      * @return {Roo.TabPanelItem} The TabPanelItem.
40563      */
40564     activate : function(id)
40565     {
40566         //Roo.log('activite:'  + id);
40567         
40568         var tab = this.items[id];
40569         if(!tab){
40570             return null;
40571         }
40572         if(tab == this.active || tab.disabled){
40573             return tab;
40574         }
40575         var e = {};
40576         this.fireEvent("beforetabchange", this, e, tab);
40577         if(e.cancel !== true && !tab.disabled){
40578             if(this.active){
40579                 this.active.hide();
40580             }
40581             this.active = this.items[id];
40582             this.active.show();
40583             this.fireEvent("tabchange", this, this.active);
40584         }
40585         return tab;
40586     },
40587
40588     /**
40589      * Gets the active {@link Roo.TabPanelItem}.
40590      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40591      */
40592     getActiveTab : function(){
40593         return this.active;
40594     },
40595
40596     /**
40597      * Updates the tab body element to fit the height of the container element
40598      * for overflow scrolling
40599      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40600      */
40601     syncHeight : function(targetHeight){
40602         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40603         var bm = this.bodyEl.getMargins();
40604         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40605         this.bodyEl.setHeight(newHeight);
40606         return newHeight;
40607     },
40608
40609     onResize : function(){
40610         if(this.monitorResize){
40611             this.autoSizeTabs();
40612         }
40613     },
40614
40615     /**
40616      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40617      */
40618     beginUpdate : function(){
40619         this.updating = true;
40620     },
40621
40622     /**
40623      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40624      */
40625     endUpdate : function(){
40626         this.updating = false;
40627         this.autoSizeTabs();
40628     },
40629
40630     /**
40631      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40632      */
40633     autoSizeTabs : function()
40634     {
40635         var count = this.items.length;
40636         var vcount = count - this.hiddenCount;
40637         
40638         if (vcount < 2) {
40639             this.stripEl.hide();
40640         } else {
40641             this.stripEl.show();
40642         }
40643         
40644         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40645             return;
40646         }
40647         
40648         
40649         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40650         var availWidth = Math.floor(w / vcount);
40651         var b = this.stripBody;
40652         if(b.getWidth() > w){
40653             var tabs = this.items;
40654             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40655             if(availWidth < this.minTabWidth){
40656                 /*if(!this.sleft){    // incomplete scrolling code
40657                     this.createScrollButtons();
40658                 }
40659                 this.showScroll();
40660                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40661             }
40662         }else{
40663             if(this.currentTabWidth < this.preferredTabWidth){
40664                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40665             }
40666         }
40667     },
40668
40669     /**
40670      * Returns the number of tabs in this TabPanel.
40671      * @return {Number}
40672      */
40673      getCount : function(){
40674          return this.items.length;
40675      },
40676
40677     /**
40678      * Resizes all the tabs to the passed width
40679      * @param {Number} The new width
40680      */
40681     setTabWidth : function(width){
40682         this.currentTabWidth = width;
40683         for(var i = 0, len = this.items.length; i < len; i++) {
40684                 if(!this.items[i].isHidden()) {
40685                 this.items[i].setWidth(width);
40686             }
40687         }
40688     },
40689
40690     /**
40691      * Destroys this TabPanel
40692      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40693      */
40694     destroy : function(removeEl){
40695         Roo.EventManager.removeResizeListener(this.onResize, this);
40696         for(var i = 0, len = this.items.length; i < len; i++){
40697             this.items[i].purgeListeners();
40698         }
40699         if(removeEl === true){
40700             this.el.update("");
40701             this.el.remove();
40702         }
40703     },
40704     
40705     createStrip : function(container)
40706     {
40707         var strip = document.createElement("nav");
40708         strip.className = Roo.bootstrap.version == 4 ?
40709             "navbar-light bg-light" : 
40710             "navbar navbar-default"; //"x-tabs-wrap";
40711         container.appendChild(strip);
40712         return strip;
40713     },
40714     
40715     createStripList : function(strip)
40716     {
40717         // div wrapper for retard IE
40718         // returns the "tr" element.
40719         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40720         //'<div class="x-tabs-strip-wrap">'+
40721           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40722           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40723         return strip.firstChild; //.firstChild.firstChild.firstChild;
40724     },
40725     createBody : function(container)
40726     {
40727         var body = document.createElement("div");
40728         Roo.id(body, "tab-body");
40729         //Roo.fly(body).addClass("x-tabs-body");
40730         Roo.fly(body).addClass("tab-content");
40731         container.appendChild(body);
40732         return body;
40733     },
40734     createItemBody :function(bodyEl, id){
40735         var body = Roo.getDom(id);
40736         if(!body){
40737             body = document.createElement("div");
40738             body.id = id;
40739         }
40740         //Roo.fly(body).addClass("x-tabs-item-body");
40741         Roo.fly(body).addClass("tab-pane");
40742          bodyEl.insertBefore(body, bodyEl.firstChild);
40743         return body;
40744     },
40745     /** @private */
40746     createStripElements :  function(stripEl, text, closable, tpl)
40747     {
40748         var td = document.createElement("li"); // was td..
40749         td.className = 'nav-item';
40750         
40751         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40752         
40753         
40754         stripEl.appendChild(td);
40755         /*if(closable){
40756             td.className = "x-tabs-closable";
40757             if(!this.closeTpl){
40758                 this.closeTpl = new Roo.Template(
40759                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40760                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40761                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40762                 );
40763             }
40764             var el = this.closeTpl.overwrite(td, {"text": text});
40765             var close = el.getElementsByTagName("div")[0];
40766             var inner = el.getElementsByTagName("em")[0];
40767             return {"el": el, "close": close, "inner": inner};
40768         } else {
40769         */
40770         // not sure what this is..
40771 //            if(!this.tabTpl){
40772                 //this.tabTpl = new Roo.Template(
40773                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40774                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40775                 //);
40776 //                this.tabTpl = new Roo.Template(
40777 //                   '<a href="#">' +
40778 //                   '<span unselectable="on"' +
40779 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40780 //                            ' >{text}</span></a>'
40781 //                );
40782 //                
40783 //            }
40784
40785
40786             var template = tpl || this.tabTpl || false;
40787             
40788             if(!template){
40789                 template =  new Roo.Template(
40790                         Roo.bootstrap.version == 4 ? 
40791                             (
40792                                 '<a class="nav-link" href="#" unselectable="on"' +
40793                                      (this.disableTooltips ? '' : ' title="{text}"') +
40794                                      ' >{text}</a>'
40795                             ) : (
40796                                 '<a class="nav-link" href="#">' +
40797                                 '<span unselectable="on"' +
40798                                          (this.disableTooltips ? '' : ' title="{text}"') +
40799                                     ' >{text}</span></a>'
40800                             )
40801                 );
40802             }
40803             
40804             switch (typeof(template)) {
40805                 case 'object' :
40806                     break;
40807                 case 'string' :
40808                     template = new Roo.Template(template);
40809                     break;
40810                 default :
40811                     break;
40812             }
40813             
40814             var el = template.overwrite(td, {"text": text});
40815             
40816             var inner = el.getElementsByTagName("span")[0];
40817             
40818             return {"el": el, "inner": inner};
40819             
40820     }
40821         
40822     
40823 });
40824
40825 /**
40826  * @class Roo.TabPanelItem
40827  * @extends Roo.util.Observable
40828  * Represents an individual item (tab plus body) in a TabPanel.
40829  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40830  * @param {String} id The id of this TabPanelItem
40831  * @param {String} text The text for the tab of this TabPanelItem
40832  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40833  */
40834 Roo.bootstrap.panel.TabItem = function(config){
40835     /**
40836      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40837      * @type Roo.TabPanel
40838      */
40839     this.tabPanel = config.panel;
40840     /**
40841      * The id for this TabPanelItem
40842      * @type String
40843      */
40844     this.id = config.id;
40845     /** @private */
40846     this.disabled = false;
40847     /** @private */
40848     this.text = config.text;
40849     /** @private */
40850     this.loaded = false;
40851     this.closable = config.closable;
40852
40853     /**
40854      * The body element for this TabPanelItem.
40855      * @type Roo.Element
40856      */
40857     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40858     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40859     this.bodyEl.setStyle("display", "block");
40860     this.bodyEl.setStyle("zoom", "1");
40861     //this.hideAction();
40862
40863     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40864     /** @private */
40865     this.el = Roo.get(els.el);
40866     this.inner = Roo.get(els.inner, true);
40867      this.textEl = Roo.bootstrap.version == 4 ?
40868         this.el : Roo.get(this.el.dom.firstChild, true);
40869
40870     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40871     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40872
40873     
40874 //    this.el.on("mousedown", this.onTabMouseDown, this);
40875     this.el.on("click", this.onTabClick, this);
40876     /** @private */
40877     if(config.closable){
40878         var c = Roo.get(els.close, true);
40879         c.dom.title = this.closeText;
40880         c.addClassOnOver("close-over");
40881         c.on("click", this.closeClick, this);
40882      }
40883
40884     this.addEvents({
40885          /**
40886          * @event activate
40887          * Fires when this tab becomes the active tab.
40888          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40889          * @param {Roo.TabPanelItem} this
40890          */
40891         "activate": true,
40892         /**
40893          * @event beforeclose
40894          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40895          * @param {Roo.TabPanelItem} this
40896          * @param {Object} e Set cancel to true on this object to cancel the close.
40897          */
40898         "beforeclose": true,
40899         /**
40900          * @event close
40901          * Fires when this tab is closed.
40902          * @param {Roo.TabPanelItem} this
40903          */
40904          "close": true,
40905         /**
40906          * @event deactivate
40907          * Fires when this tab is no longer the active tab.
40908          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40909          * @param {Roo.TabPanelItem} this
40910          */
40911          "deactivate" : true
40912     });
40913     this.hidden = false;
40914
40915     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40916 };
40917
40918 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40919            {
40920     purgeListeners : function(){
40921        Roo.util.Observable.prototype.purgeListeners.call(this);
40922        this.el.removeAllListeners();
40923     },
40924     /**
40925      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40926      */
40927     show : function(){
40928         this.status_node.addClass("active");
40929         this.showAction();
40930         if(Roo.isOpera){
40931             this.tabPanel.stripWrap.repaint();
40932         }
40933         this.fireEvent("activate", this.tabPanel, this);
40934     },
40935
40936     /**
40937      * Returns true if this tab is the active tab.
40938      * @return {Boolean}
40939      */
40940     isActive : function(){
40941         return this.tabPanel.getActiveTab() == this;
40942     },
40943
40944     /**
40945      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40946      */
40947     hide : function(){
40948         this.status_node.removeClass("active");
40949         this.hideAction();
40950         this.fireEvent("deactivate", this.tabPanel, this);
40951     },
40952
40953     hideAction : function(){
40954         this.bodyEl.hide();
40955         this.bodyEl.setStyle("position", "absolute");
40956         this.bodyEl.setLeft("-20000px");
40957         this.bodyEl.setTop("-20000px");
40958     },
40959
40960     showAction : function(){
40961         this.bodyEl.setStyle("position", "relative");
40962         this.bodyEl.setTop("");
40963         this.bodyEl.setLeft("");
40964         this.bodyEl.show();
40965     },
40966
40967     /**
40968      * Set the tooltip for the tab.
40969      * @param {String} tooltip The tab's tooltip
40970      */
40971     setTooltip : function(text){
40972         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40973             this.textEl.dom.qtip = text;
40974             this.textEl.dom.removeAttribute('title');
40975         }else{
40976             this.textEl.dom.title = text;
40977         }
40978     },
40979
40980     onTabClick : function(e){
40981         e.preventDefault();
40982         this.tabPanel.activate(this.id);
40983     },
40984
40985     onTabMouseDown : function(e){
40986         e.preventDefault();
40987         this.tabPanel.activate(this.id);
40988     },
40989 /*
40990     getWidth : function(){
40991         return this.inner.getWidth();
40992     },
40993
40994     setWidth : function(width){
40995         var iwidth = width - this.linode.getPadding("lr");
40996         this.inner.setWidth(iwidth);
40997         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40998         this.linode.setWidth(width);
40999     },
41000 */
41001     /**
41002      * Show or hide the tab
41003      * @param {Boolean} hidden True to hide or false to show.
41004      */
41005     setHidden : function(hidden){
41006         this.hidden = hidden;
41007         this.linode.setStyle("display", hidden ? "none" : "");
41008     },
41009
41010     /**
41011      * Returns true if this tab is "hidden"
41012      * @return {Boolean}
41013      */
41014     isHidden : function(){
41015         return this.hidden;
41016     },
41017
41018     /**
41019      * Returns the text for this tab
41020      * @return {String}
41021      */
41022     getText : function(){
41023         return this.text;
41024     },
41025     /*
41026     autoSize : function(){
41027         //this.el.beginMeasure();
41028         this.textEl.setWidth(1);
41029         /*
41030          *  #2804 [new] Tabs in Roojs
41031          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41032          */
41033         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41034         //this.el.endMeasure();
41035     //},
41036
41037     /**
41038      * Sets the text for the tab (Note: this also sets the tooltip text)
41039      * @param {String} text The tab's text and tooltip
41040      */
41041     setText : function(text){
41042         this.text = text;
41043         this.textEl.update(text);
41044         this.setTooltip(text);
41045         //if(!this.tabPanel.resizeTabs){
41046         //    this.autoSize();
41047         //}
41048     },
41049     /**
41050      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41051      */
41052     activate : function(){
41053         this.tabPanel.activate(this.id);
41054     },
41055
41056     /**
41057      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41058      */
41059     disable : function(){
41060         if(this.tabPanel.active != this){
41061             this.disabled = true;
41062             this.status_node.addClass("disabled");
41063         }
41064     },
41065
41066     /**
41067      * Enables this TabPanelItem if it was previously disabled.
41068      */
41069     enable : function(){
41070         this.disabled = false;
41071         this.status_node.removeClass("disabled");
41072     },
41073
41074     /**
41075      * Sets the content for this TabPanelItem.
41076      * @param {String} content The content
41077      * @param {Boolean} loadScripts true to look for and load scripts
41078      */
41079     setContent : function(content, loadScripts){
41080         this.bodyEl.update(content, loadScripts);
41081     },
41082
41083     /**
41084      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41085      * @return {Roo.UpdateManager} The UpdateManager
41086      */
41087     getUpdateManager : function(){
41088         return this.bodyEl.getUpdateManager();
41089     },
41090
41091     /**
41092      * Set a URL to be used to load the content for this TabPanelItem.
41093      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41094      * @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)
41095      * @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)
41096      * @return {Roo.UpdateManager} The UpdateManager
41097      */
41098     setUrl : function(url, params, loadOnce){
41099         if(this.refreshDelegate){
41100             this.un('activate', this.refreshDelegate);
41101         }
41102         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41103         this.on("activate", this.refreshDelegate);
41104         return this.bodyEl.getUpdateManager();
41105     },
41106
41107     /** @private */
41108     _handleRefresh : function(url, params, loadOnce){
41109         if(!loadOnce || !this.loaded){
41110             var updater = this.bodyEl.getUpdateManager();
41111             updater.update(url, params, this._setLoaded.createDelegate(this));
41112         }
41113     },
41114
41115     /**
41116      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41117      *   Will fail silently if the setUrl method has not been called.
41118      *   This does not activate the panel, just updates its content.
41119      */
41120     refresh : function(){
41121         if(this.refreshDelegate){
41122            this.loaded = false;
41123            this.refreshDelegate();
41124         }
41125     },
41126
41127     /** @private */
41128     _setLoaded : function(){
41129         this.loaded = true;
41130     },
41131
41132     /** @private */
41133     closeClick : function(e){
41134         var o = {};
41135         e.stopEvent();
41136         this.fireEvent("beforeclose", this, o);
41137         if(o.cancel !== true){
41138             this.tabPanel.removeTab(this.id);
41139         }
41140     },
41141     /**
41142      * The text displayed in the tooltip for the close icon.
41143      * @type String
41144      */
41145     closeText : "Close this tab"
41146 });
41147 /**
41148 *    This script refer to:
41149 *    Title: International Telephone Input
41150 *    Author: Jack O'Connor
41151 *    Code version:  v12.1.12
41152 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41153 **/
41154
41155 Roo.bootstrap.PhoneInputData = function() {
41156     var d = [
41157       [
41158         "Afghanistan (‫افغانستان‬‎)",
41159         "af",
41160         "93"
41161       ],
41162       [
41163         "Albania (Shqipëri)",
41164         "al",
41165         "355"
41166       ],
41167       [
41168         "Algeria (‫الجزائر‬‎)",
41169         "dz",
41170         "213"
41171       ],
41172       [
41173         "American Samoa",
41174         "as",
41175         "1684"
41176       ],
41177       [
41178         "Andorra",
41179         "ad",
41180         "376"
41181       ],
41182       [
41183         "Angola",
41184         "ao",
41185         "244"
41186       ],
41187       [
41188         "Anguilla",
41189         "ai",
41190         "1264"
41191       ],
41192       [
41193         "Antigua and Barbuda",
41194         "ag",
41195         "1268"
41196       ],
41197       [
41198         "Argentina",
41199         "ar",
41200         "54"
41201       ],
41202       [
41203         "Armenia (Հայաստան)",
41204         "am",
41205         "374"
41206       ],
41207       [
41208         "Aruba",
41209         "aw",
41210         "297"
41211       ],
41212       [
41213         "Australia",
41214         "au",
41215         "61",
41216         0
41217       ],
41218       [
41219         "Austria (Österreich)",
41220         "at",
41221         "43"
41222       ],
41223       [
41224         "Azerbaijan (Azərbaycan)",
41225         "az",
41226         "994"
41227       ],
41228       [
41229         "Bahamas",
41230         "bs",
41231         "1242"
41232       ],
41233       [
41234         "Bahrain (‫البحرين‬‎)",
41235         "bh",
41236         "973"
41237       ],
41238       [
41239         "Bangladesh (বাংলাদেশ)",
41240         "bd",
41241         "880"
41242       ],
41243       [
41244         "Barbados",
41245         "bb",
41246         "1246"
41247       ],
41248       [
41249         "Belarus (Беларусь)",
41250         "by",
41251         "375"
41252       ],
41253       [
41254         "Belgium (België)",
41255         "be",
41256         "32"
41257       ],
41258       [
41259         "Belize",
41260         "bz",
41261         "501"
41262       ],
41263       [
41264         "Benin (Bénin)",
41265         "bj",
41266         "229"
41267       ],
41268       [
41269         "Bermuda",
41270         "bm",
41271         "1441"
41272       ],
41273       [
41274         "Bhutan (འབྲུག)",
41275         "bt",
41276         "975"
41277       ],
41278       [
41279         "Bolivia",
41280         "bo",
41281         "591"
41282       ],
41283       [
41284         "Bosnia and Herzegovina (Босна и Херцеговина)",
41285         "ba",
41286         "387"
41287       ],
41288       [
41289         "Botswana",
41290         "bw",
41291         "267"
41292       ],
41293       [
41294         "Brazil (Brasil)",
41295         "br",
41296         "55"
41297       ],
41298       [
41299         "British Indian Ocean Territory",
41300         "io",
41301         "246"
41302       ],
41303       [
41304         "British Virgin Islands",
41305         "vg",
41306         "1284"
41307       ],
41308       [
41309         "Brunei",
41310         "bn",
41311         "673"
41312       ],
41313       [
41314         "Bulgaria (България)",
41315         "bg",
41316         "359"
41317       ],
41318       [
41319         "Burkina Faso",
41320         "bf",
41321         "226"
41322       ],
41323       [
41324         "Burundi (Uburundi)",
41325         "bi",
41326         "257"
41327       ],
41328       [
41329         "Cambodia (កម្ពុជា)",
41330         "kh",
41331         "855"
41332       ],
41333       [
41334         "Cameroon (Cameroun)",
41335         "cm",
41336         "237"
41337       ],
41338       [
41339         "Canada",
41340         "ca",
41341         "1",
41342         1,
41343         ["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"]
41344       ],
41345       [
41346         "Cape Verde (Kabu Verdi)",
41347         "cv",
41348         "238"
41349       ],
41350       [
41351         "Caribbean Netherlands",
41352         "bq",
41353         "599",
41354         1
41355       ],
41356       [
41357         "Cayman Islands",
41358         "ky",
41359         "1345"
41360       ],
41361       [
41362         "Central African Republic (République centrafricaine)",
41363         "cf",
41364         "236"
41365       ],
41366       [
41367         "Chad (Tchad)",
41368         "td",
41369         "235"
41370       ],
41371       [
41372         "Chile",
41373         "cl",
41374         "56"
41375       ],
41376       [
41377         "China (中国)",
41378         "cn",
41379         "86"
41380       ],
41381       [
41382         "Christmas Island",
41383         "cx",
41384         "61",
41385         2
41386       ],
41387       [
41388         "Cocos (Keeling) Islands",
41389         "cc",
41390         "61",
41391         1
41392       ],
41393       [
41394         "Colombia",
41395         "co",
41396         "57"
41397       ],
41398       [
41399         "Comoros (‫جزر القمر‬‎)",
41400         "km",
41401         "269"
41402       ],
41403       [
41404         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41405         "cd",
41406         "243"
41407       ],
41408       [
41409         "Congo (Republic) (Congo-Brazzaville)",
41410         "cg",
41411         "242"
41412       ],
41413       [
41414         "Cook Islands",
41415         "ck",
41416         "682"
41417       ],
41418       [
41419         "Costa Rica",
41420         "cr",
41421         "506"
41422       ],
41423       [
41424         "Côte d’Ivoire",
41425         "ci",
41426         "225"
41427       ],
41428       [
41429         "Croatia (Hrvatska)",
41430         "hr",
41431         "385"
41432       ],
41433       [
41434         "Cuba",
41435         "cu",
41436         "53"
41437       ],
41438       [
41439         "Curaçao",
41440         "cw",
41441         "599",
41442         0
41443       ],
41444       [
41445         "Cyprus (Κύπρος)",
41446         "cy",
41447         "357"
41448       ],
41449       [
41450         "Czech Republic (Česká republika)",
41451         "cz",
41452         "420"
41453       ],
41454       [
41455         "Denmark (Danmark)",
41456         "dk",
41457         "45"
41458       ],
41459       [
41460         "Djibouti",
41461         "dj",
41462         "253"
41463       ],
41464       [
41465         "Dominica",
41466         "dm",
41467         "1767"
41468       ],
41469       [
41470         "Dominican Republic (República Dominicana)",
41471         "do",
41472         "1",
41473         2,
41474         ["809", "829", "849"]
41475       ],
41476       [
41477         "Ecuador",
41478         "ec",
41479         "593"
41480       ],
41481       [
41482         "Egypt (‫مصر‬‎)",
41483         "eg",
41484         "20"
41485       ],
41486       [
41487         "El Salvador",
41488         "sv",
41489         "503"
41490       ],
41491       [
41492         "Equatorial Guinea (Guinea Ecuatorial)",
41493         "gq",
41494         "240"
41495       ],
41496       [
41497         "Eritrea",
41498         "er",
41499         "291"
41500       ],
41501       [
41502         "Estonia (Eesti)",
41503         "ee",
41504         "372"
41505       ],
41506       [
41507         "Ethiopia",
41508         "et",
41509         "251"
41510       ],
41511       [
41512         "Falkland Islands (Islas Malvinas)",
41513         "fk",
41514         "500"
41515       ],
41516       [
41517         "Faroe Islands (Føroyar)",
41518         "fo",
41519         "298"
41520       ],
41521       [
41522         "Fiji",
41523         "fj",
41524         "679"
41525       ],
41526       [
41527         "Finland (Suomi)",
41528         "fi",
41529         "358",
41530         0
41531       ],
41532       [
41533         "France",
41534         "fr",
41535         "33"
41536       ],
41537       [
41538         "French Guiana (Guyane française)",
41539         "gf",
41540         "594"
41541       ],
41542       [
41543         "French Polynesia (Polynésie française)",
41544         "pf",
41545         "689"
41546       ],
41547       [
41548         "Gabon",
41549         "ga",
41550         "241"
41551       ],
41552       [
41553         "Gambia",
41554         "gm",
41555         "220"
41556       ],
41557       [
41558         "Georgia (საქართველო)",
41559         "ge",
41560         "995"
41561       ],
41562       [
41563         "Germany (Deutschland)",
41564         "de",
41565         "49"
41566       ],
41567       [
41568         "Ghana (Gaana)",
41569         "gh",
41570         "233"
41571       ],
41572       [
41573         "Gibraltar",
41574         "gi",
41575         "350"
41576       ],
41577       [
41578         "Greece (Ελλάδα)",
41579         "gr",
41580         "30"
41581       ],
41582       [
41583         "Greenland (Kalaallit Nunaat)",
41584         "gl",
41585         "299"
41586       ],
41587       [
41588         "Grenada",
41589         "gd",
41590         "1473"
41591       ],
41592       [
41593         "Guadeloupe",
41594         "gp",
41595         "590",
41596         0
41597       ],
41598       [
41599         "Guam",
41600         "gu",
41601         "1671"
41602       ],
41603       [
41604         "Guatemala",
41605         "gt",
41606         "502"
41607       ],
41608       [
41609         "Guernsey",
41610         "gg",
41611         "44",
41612         1
41613       ],
41614       [
41615         "Guinea (Guinée)",
41616         "gn",
41617         "224"
41618       ],
41619       [
41620         "Guinea-Bissau (Guiné Bissau)",
41621         "gw",
41622         "245"
41623       ],
41624       [
41625         "Guyana",
41626         "gy",
41627         "592"
41628       ],
41629       [
41630         "Haiti",
41631         "ht",
41632         "509"
41633       ],
41634       [
41635         "Honduras",
41636         "hn",
41637         "504"
41638       ],
41639       [
41640         "Hong Kong (香港)",
41641         "hk",
41642         "852"
41643       ],
41644       [
41645         "Hungary (Magyarország)",
41646         "hu",
41647         "36"
41648       ],
41649       [
41650         "Iceland (Ísland)",
41651         "is",
41652         "354"
41653       ],
41654       [
41655         "India (भारत)",
41656         "in",
41657         "91"
41658       ],
41659       [
41660         "Indonesia",
41661         "id",
41662         "62"
41663       ],
41664       [
41665         "Iran (‫ایران‬‎)",
41666         "ir",
41667         "98"
41668       ],
41669       [
41670         "Iraq (‫العراق‬‎)",
41671         "iq",
41672         "964"
41673       ],
41674       [
41675         "Ireland",
41676         "ie",
41677         "353"
41678       ],
41679       [
41680         "Isle of Man",
41681         "im",
41682         "44",
41683         2
41684       ],
41685       [
41686         "Israel (‫ישראל‬‎)",
41687         "il",
41688         "972"
41689       ],
41690       [
41691         "Italy (Italia)",
41692         "it",
41693         "39",
41694         0
41695       ],
41696       [
41697         "Jamaica",
41698         "jm",
41699         "1876"
41700       ],
41701       [
41702         "Japan (日本)",
41703         "jp",
41704         "81"
41705       ],
41706       [
41707         "Jersey",
41708         "je",
41709         "44",
41710         3
41711       ],
41712       [
41713         "Jordan (‫الأردن‬‎)",
41714         "jo",
41715         "962"
41716       ],
41717       [
41718         "Kazakhstan (Казахстан)",
41719         "kz",
41720         "7",
41721         1
41722       ],
41723       [
41724         "Kenya",
41725         "ke",
41726         "254"
41727       ],
41728       [
41729         "Kiribati",
41730         "ki",
41731         "686"
41732       ],
41733       [
41734         "Kosovo",
41735         "xk",
41736         "383"
41737       ],
41738       [
41739         "Kuwait (‫الكويت‬‎)",
41740         "kw",
41741         "965"
41742       ],
41743       [
41744         "Kyrgyzstan (Кыргызстан)",
41745         "kg",
41746         "996"
41747       ],
41748       [
41749         "Laos (ລາວ)",
41750         "la",
41751         "856"
41752       ],
41753       [
41754         "Latvia (Latvija)",
41755         "lv",
41756         "371"
41757       ],
41758       [
41759         "Lebanon (‫لبنان‬‎)",
41760         "lb",
41761         "961"
41762       ],
41763       [
41764         "Lesotho",
41765         "ls",
41766         "266"
41767       ],
41768       [
41769         "Liberia",
41770         "lr",
41771         "231"
41772       ],
41773       [
41774         "Libya (‫ليبيا‬‎)",
41775         "ly",
41776         "218"
41777       ],
41778       [
41779         "Liechtenstein",
41780         "li",
41781         "423"
41782       ],
41783       [
41784         "Lithuania (Lietuva)",
41785         "lt",
41786         "370"
41787       ],
41788       [
41789         "Luxembourg",
41790         "lu",
41791         "352"
41792       ],
41793       [
41794         "Macau (澳門)",
41795         "mo",
41796         "853"
41797       ],
41798       [
41799         "Macedonia (FYROM) (Македонија)",
41800         "mk",
41801         "389"
41802       ],
41803       [
41804         "Madagascar (Madagasikara)",
41805         "mg",
41806         "261"
41807       ],
41808       [
41809         "Malawi",
41810         "mw",
41811         "265"
41812       ],
41813       [
41814         "Malaysia",
41815         "my",
41816         "60"
41817       ],
41818       [
41819         "Maldives",
41820         "mv",
41821         "960"
41822       ],
41823       [
41824         "Mali",
41825         "ml",
41826         "223"
41827       ],
41828       [
41829         "Malta",
41830         "mt",
41831         "356"
41832       ],
41833       [
41834         "Marshall Islands",
41835         "mh",
41836         "692"
41837       ],
41838       [
41839         "Martinique",
41840         "mq",
41841         "596"
41842       ],
41843       [
41844         "Mauritania (‫موريتانيا‬‎)",
41845         "mr",
41846         "222"
41847       ],
41848       [
41849         "Mauritius (Moris)",
41850         "mu",
41851         "230"
41852       ],
41853       [
41854         "Mayotte",
41855         "yt",
41856         "262",
41857         1
41858       ],
41859       [
41860         "Mexico (México)",
41861         "mx",
41862         "52"
41863       ],
41864       [
41865         "Micronesia",
41866         "fm",
41867         "691"
41868       ],
41869       [
41870         "Moldova (Republica Moldova)",
41871         "md",
41872         "373"
41873       ],
41874       [
41875         "Monaco",
41876         "mc",
41877         "377"
41878       ],
41879       [
41880         "Mongolia (Монгол)",
41881         "mn",
41882         "976"
41883       ],
41884       [
41885         "Montenegro (Crna Gora)",
41886         "me",
41887         "382"
41888       ],
41889       [
41890         "Montserrat",
41891         "ms",
41892         "1664"
41893       ],
41894       [
41895         "Morocco (‫المغرب‬‎)",
41896         "ma",
41897         "212",
41898         0
41899       ],
41900       [
41901         "Mozambique (Moçambique)",
41902         "mz",
41903         "258"
41904       ],
41905       [
41906         "Myanmar (Burma) (မြန်မာ)",
41907         "mm",
41908         "95"
41909       ],
41910       [
41911         "Namibia (Namibië)",
41912         "na",
41913         "264"
41914       ],
41915       [
41916         "Nauru",
41917         "nr",
41918         "674"
41919       ],
41920       [
41921         "Nepal (नेपाल)",
41922         "np",
41923         "977"
41924       ],
41925       [
41926         "Netherlands (Nederland)",
41927         "nl",
41928         "31"
41929       ],
41930       [
41931         "New Caledonia (Nouvelle-Calédonie)",
41932         "nc",
41933         "687"
41934       ],
41935       [
41936         "New Zealand",
41937         "nz",
41938         "64"
41939       ],
41940       [
41941         "Nicaragua",
41942         "ni",
41943         "505"
41944       ],
41945       [
41946         "Niger (Nijar)",
41947         "ne",
41948         "227"
41949       ],
41950       [
41951         "Nigeria",
41952         "ng",
41953         "234"
41954       ],
41955       [
41956         "Niue",
41957         "nu",
41958         "683"
41959       ],
41960       [
41961         "Norfolk Island",
41962         "nf",
41963         "672"
41964       ],
41965       [
41966         "North Korea (조선 민주주의 인민 공화국)",
41967         "kp",
41968         "850"
41969       ],
41970       [
41971         "Northern Mariana Islands",
41972         "mp",
41973         "1670"
41974       ],
41975       [
41976         "Norway (Norge)",
41977         "no",
41978         "47",
41979         0
41980       ],
41981       [
41982         "Oman (‫عُمان‬‎)",
41983         "om",
41984         "968"
41985       ],
41986       [
41987         "Pakistan (‫پاکستان‬‎)",
41988         "pk",
41989         "92"
41990       ],
41991       [
41992         "Palau",
41993         "pw",
41994         "680"
41995       ],
41996       [
41997         "Palestine (‫فلسطين‬‎)",
41998         "ps",
41999         "970"
42000       ],
42001       [
42002         "Panama (Panamá)",
42003         "pa",
42004         "507"
42005       ],
42006       [
42007         "Papua New Guinea",
42008         "pg",
42009         "675"
42010       ],
42011       [
42012         "Paraguay",
42013         "py",
42014         "595"
42015       ],
42016       [
42017         "Peru (Perú)",
42018         "pe",
42019         "51"
42020       ],
42021       [
42022         "Philippines",
42023         "ph",
42024         "63"
42025       ],
42026       [
42027         "Poland (Polska)",
42028         "pl",
42029         "48"
42030       ],
42031       [
42032         "Portugal",
42033         "pt",
42034         "351"
42035       ],
42036       [
42037         "Puerto Rico",
42038         "pr",
42039         "1",
42040         3,
42041         ["787", "939"]
42042       ],
42043       [
42044         "Qatar (‫قطر‬‎)",
42045         "qa",
42046         "974"
42047       ],
42048       [
42049         "Réunion (La Réunion)",
42050         "re",
42051         "262",
42052         0
42053       ],
42054       [
42055         "Romania (România)",
42056         "ro",
42057         "40"
42058       ],
42059       [
42060         "Russia (Россия)",
42061         "ru",
42062         "7",
42063         0
42064       ],
42065       [
42066         "Rwanda",
42067         "rw",
42068         "250"
42069       ],
42070       [
42071         "Saint Barthélemy",
42072         "bl",
42073         "590",
42074         1
42075       ],
42076       [
42077         "Saint Helena",
42078         "sh",
42079         "290"
42080       ],
42081       [
42082         "Saint Kitts and Nevis",
42083         "kn",
42084         "1869"
42085       ],
42086       [
42087         "Saint Lucia",
42088         "lc",
42089         "1758"
42090       ],
42091       [
42092         "Saint Martin (Saint-Martin (partie française))",
42093         "mf",
42094         "590",
42095         2
42096       ],
42097       [
42098         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42099         "pm",
42100         "508"
42101       ],
42102       [
42103         "Saint Vincent and the Grenadines",
42104         "vc",
42105         "1784"
42106       ],
42107       [
42108         "Samoa",
42109         "ws",
42110         "685"
42111       ],
42112       [
42113         "San Marino",
42114         "sm",
42115         "378"
42116       ],
42117       [
42118         "São Tomé and Príncipe (São Tomé e Príncipe)",
42119         "st",
42120         "239"
42121       ],
42122       [
42123         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42124         "sa",
42125         "966"
42126       ],
42127       [
42128         "Senegal (Sénégal)",
42129         "sn",
42130         "221"
42131       ],
42132       [
42133         "Serbia (Србија)",
42134         "rs",
42135         "381"
42136       ],
42137       [
42138         "Seychelles",
42139         "sc",
42140         "248"
42141       ],
42142       [
42143         "Sierra Leone",
42144         "sl",
42145         "232"
42146       ],
42147       [
42148         "Singapore",
42149         "sg",
42150         "65"
42151       ],
42152       [
42153         "Sint Maarten",
42154         "sx",
42155         "1721"
42156       ],
42157       [
42158         "Slovakia (Slovensko)",
42159         "sk",
42160         "421"
42161       ],
42162       [
42163         "Slovenia (Slovenija)",
42164         "si",
42165         "386"
42166       ],
42167       [
42168         "Solomon Islands",
42169         "sb",
42170         "677"
42171       ],
42172       [
42173         "Somalia (Soomaaliya)",
42174         "so",
42175         "252"
42176       ],
42177       [
42178         "South Africa",
42179         "za",
42180         "27"
42181       ],
42182       [
42183         "South Korea (대한민국)",
42184         "kr",
42185         "82"
42186       ],
42187       [
42188         "South Sudan (‫جنوب السودان‬‎)",
42189         "ss",
42190         "211"
42191       ],
42192       [
42193         "Spain (España)",
42194         "es",
42195         "34"
42196       ],
42197       [
42198         "Sri Lanka (ශ්‍රී ලංකාව)",
42199         "lk",
42200         "94"
42201       ],
42202       [
42203         "Sudan (‫السودان‬‎)",
42204         "sd",
42205         "249"
42206       ],
42207       [
42208         "Suriname",
42209         "sr",
42210         "597"
42211       ],
42212       [
42213         "Svalbard and Jan Mayen",
42214         "sj",
42215         "47",
42216         1
42217       ],
42218       [
42219         "Swaziland",
42220         "sz",
42221         "268"
42222       ],
42223       [
42224         "Sweden (Sverige)",
42225         "se",
42226         "46"
42227       ],
42228       [
42229         "Switzerland (Schweiz)",
42230         "ch",
42231         "41"
42232       ],
42233       [
42234         "Syria (‫سوريا‬‎)",
42235         "sy",
42236         "963"
42237       ],
42238       [
42239         "Taiwan (台灣)",
42240         "tw",
42241         "886"
42242       ],
42243       [
42244         "Tajikistan",
42245         "tj",
42246         "992"
42247       ],
42248       [
42249         "Tanzania",
42250         "tz",
42251         "255"
42252       ],
42253       [
42254         "Thailand (ไทย)",
42255         "th",
42256         "66"
42257       ],
42258       [
42259         "Timor-Leste",
42260         "tl",
42261         "670"
42262       ],
42263       [
42264         "Togo",
42265         "tg",
42266         "228"
42267       ],
42268       [
42269         "Tokelau",
42270         "tk",
42271         "690"
42272       ],
42273       [
42274         "Tonga",
42275         "to",
42276         "676"
42277       ],
42278       [
42279         "Trinidad and Tobago",
42280         "tt",
42281         "1868"
42282       ],
42283       [
42284         "Tunisia (‫تونس‬‎)",
42285         "tn",
42286         "216"
42287       ],
42288       [
42289         "Turkey (Türkiye)",
42290         "tr",
42291         "90"
42292       ],
42293       [
42294         "Turkmenistan",
42295         "tm",
42296         "993"
42297       ],
42298       [
42299         "Turks and Caicos Islands",
42300         "tc",
42301         "1649"
42302       ],
42303       [
42304         "Tuvalu",
42305         "tv",
42306         "688"
42307       ],
42308       [
42309         "U.S. Virgin Islands",
42310         "vi",
42311         "1340"
42312       ],
42313       [
42314         "Uganda",
42315         "ug",
42316         "256"
42317       ],
42318       [
42319         "Ukraine (Україна)",
42320         "ua",
42321         "380"
42322       ],
42323       [
42324         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42325         "ae",
42326         "971"
42327       ],
42328       [
42329         "United Kingdom",
42330         "gb",
42331         "44",
42332         0
42333       ],
42334       [
42335         "United States",
42336         "us",
42337         "1",
42338         0
42339       ],
42340       [
42341         "Uruguay",
42342         "uy",
42343         "598"
42344       ],
42345       [
42346         "Uzbekistan (Oʻzbekiston)",
42347         "uz",
42348         "998"
42349       ],
42350       [
42351         "Vanuatu",
42352         "vu",
42353         "678"
42354       ],
42355       [
42356         "Vatican City (Città del Vaticano)",
42357         "va",
42358         "39",
42359         1
42360       ],
42361       [
42362         "Venezuela",
42363         "ve",
42364         "58"
42365       ],
42366       [
42367         "Vietnam (Việt Nam)",
42368         "vn",
42369         "84"
42370       ],
42371       [
42372         "Wallis and Futuna (Wallis-et-Futuna)",
42373         "wf",
42374         "681"
42375       ],
42376       [
42377         "Western Sahara (‫الصحراء الغربية‬‎)",
42378         "eh",
42379         "212",
42380         1
42381       ],
42382       [
42383         "Yemen (‫اليمن‬‎)",
42384         "ye",
42385         "967"
42386       ],
42387       [
42388         "Zambia",
42389         "zm",
42390         "260"
42391       ],
42392       [
42393         "Zimbabwe",
42394         "zw",
42395         "263"
42396       ],
42397       [
42398         "Åland Islands",
42399         "ax",
42400         "358",
42401         1
42402       ]
42403   ];
42404   
42405   return d;
42406 }/**
42407 *    This script refer to:
42408 *    Title: International Telephone Input
42409 *    Author: Jack O'Connor
42410 *    Code version:  v12.1.12
42411 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42412 **/
42413
42414 /**
42415  * @class Roo.bootstrap.PhoneInput
42416  * @extends Roo.bootstrap.TriggerField
42417  * An input with International dial-code selection
42418  
42419  * @cfg {String} defaultDialCode default '+852'
42420  * @cfg {Array} preferedCountries default []
42421   
42422  * @constructor
42423  * Create a new PhoneInput.
42424  * @param {Object} config Configuration options
42425  */
42426
42427 Roo.bootstrap.PhoneInput = function(config) {
42428     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42429 };
42430
42431 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42432         
42433         listWidth: undefined,
42434         
42435         selectedClass: 'active',
42436         
42437         invalidClass : "has-warning",
42438         
42439         validClass: 'has-success',
42440         
42441         allowed: '0123456789',
42442         
42443         max_length: 15,
42444         
42445         /**
42446          * @cfg {String} defaultDialCode The default dial code when initializing the input
42447          */
42448         defaultDialCode: '+852',
42449         
42450         /**
42451          * @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
42452          */
42453         preferedCountries: false,
42454         
42455         getAutoCreate : function()
42456         {
42457             var data = Roo.bootstrap.PhoneInputData();
42458             var align = this.labelAlign || this.parentLabelAlign();
42459             var id = Roo.id();
42460             
42461             this.allCountries = [];
42462             this.dialCodeMapping = [];
42463             
42464             for (var i = 0; i < data.length; i++) {
42465               var c = data[i];
42466               this.allCountries[i] = {
42467                 name: c[0],
42468                 iso2: c[1],
42469                 dialCode: c[2],
42470                 priority: c[3] || 0,
42471                 areaCodes: c[4] || null
42472               };
42473               this.dialCodeMapping[c[2]] = {
42474                   name: c[0],
42475                   iso2: c[1],
42476                   priority: c[3] || 0,
42477                   areaCodes: c[4] || null
42478               };
42479             }
42480             
42481             var cfg = {
42482                 cls: 'form-group',
42483                 cn: []
42484             };
42485             
42486             var input =  {
42487                 tag: 'input',
42488                 id : id,
42489                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42490                 maxlength: this.max_length,
42491                 cls : 'form-control tel-input',
42492                 autocomplete: 'new-password'
42493             };
42494             
42495             var hiddenInput = {
42496                 tag: 'input',
42497                 type: 'hidden',
42498                 cls: 'hidden-tel-input'
42499             };
42500             
42501             if (this.name) {
42502                 hiddenInput.name = this.name;
42503             }
42504             
42505             if (this.disabled) {
42506                 input.disabled = true;
42507             }
42508             
42509             var flag_container = {
42510                 tag: 'div',
42511                 cls: 'flag-box',
42512                 cn: [
42513                     {
42514                         tag: 'div',
42515                         cls: 'flag'
42516                     },
42517                     {
42518                         tag: 'div',
42519                         cls: 'caret'
42520                     }
42521                 ]
42522             };
42523             
42524             var box = {
42525                 tag: 'div',
42526                 cls: this.hasFeedback ? 'has-feedback' : '',
42527                 cn: [
42528                     hiddenInput,
42529                     input,
42530                     {
42531                         tag: 'input',
42532                         cls: 'dial-code-holder',
42533                         disabled: true
42534                     }
42535                 ]
42536             };
42537             
42538             var container = {
42539                 cls: 'roo-select2-container input-group',
42540                 cn: [
42541                     flag_container,
42542                     box
42543                 ]
42544             };
42545             
42546             if (this.fieldLabel.length) {
42547                 var indicator = {
42548                     tag: 'i',
42549                     tooltip: 'This field is required'
42550                 };
42551                 
42552                 var label = {
42553                     tag: 'label',
42554                     'for':  id,
42555                     cls: 'control-label',
42556                     cn: []
42557                 };
42558                 
42559                 var label_text = {
42560                     tag: 'span',
42561                     html: this.fieldLabel
42562                 };
42563                 
42564                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42565                 label.cn = [
42566                     indicator,
42567                     label_text
42568                 ];
42569                 
42570                 if(this.indicatorpos == 'right') {
42571                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42572                     label.cn = [
42573                         label_text,
42574                         indicator
42575                     ];
42576                 }
42577                 
42578                 if(align == 'left') {
42579                     container = {
42580                         tag: 'div',
42581                         cn: [
42582                             container
42583                         ]
42584                     };
42585                     
42586                     if(this.labelWidth > 12){
42587                         label.style = "width: " + this.labelWidth + 'px';
42588                     }
42589                     if(this.labelWidth < 13 && this.labelmd == 0){
42590                         this.labelmd = this.labelWidth;
42591                     }
42592                     if(this.labellg > 0){
42593                         label.cls += ' col-lg-' + this.labellg;
42594                         input.cls += ' col-lg-' + (12 - this.labellg);
42595                     }
42596                     if(this.labelmd > 0){
42597                         label.cls += ' col-md-' + this.labelmd;
42598                         container.cls += ' col-md-' + (12 - this.labelmd);
42599                     }
42600                     if(this.labelsm > 0){
42601                         label.cls += ' col-sm-' + this.labelsm;
42602                         container.cls += ' col-sm-' + (12 - this.labelsm);
42603                     }
42604                     if(this.labelxs > 0){
42605                         label.cls += ' col-xs-' + this.labelxs;
42606                         container.cls += ' col-xs-' + (12 - this.labelxs);
42607                     }
42608                 }
42609             }
42610             
42611             cfg.cn = [
42612                 label,
42613                 container
42614             ];
42615             
42616             var settings = this;
42617             
42618             ['xs','sm','md','lg'].map(function(size){
42619                 if (settings[size]) {
42620                     cfg.cls += ' col-' + size + '-' + settings[size];
42621                 }
42622             });
42623             
42624             this.store = new Roo.data.Store({
42625                 proxy : new Roo.data.MemoryProxy({}),
42626                 reader : new Roo.data.JsonReader({
42627                     fields : [
42628                         {
42629                             'name' : 'name',
42630                             'type' : 'string'
42631                         },
42632                         {
42633                             'name' : 'iso2',
42634                             'type' : 'string'
42635                         },
42636                         {
42637                             'name' : 'dialCode',
42638                             'type' : 'string'
42639                         },
42640                         {
42641                             'name' : 'priority',
42642                             'type' : 'string'
42643                         },
42644                         {
42645                             'name' : 'areaCodes',
42646                             'type' : 'string'
42647                         }
42648                     ]
42649                 })
42650             });
42651             
42652             if(!this.preferedCountries) {
42653                 this.preferedCountries = [
42654                     'hk',
42655                     'gb',
42656                     'us'
42657                 ];
42658             }
42659             
42660             var p = this.preferedCountries.reverse();
42661             
42662             if(p) {
42663                 for (var i = 0; i < p.length; i++) {
42664                     for (var j = 0; j < this.allCountries.length; j++) {
42665                         if(this.allCountries[j].iso2 == p[i]) {
42666                             var t = this.allCountries[j];
42667                             this.allCountries.splice(j,1);
42668                             this.allCountries.unshift(t);
42669                         }
42670                     } 
42671                 }
42672             }
42673             
42674             this.store.proxy.data = {
42675                 success: true,
42676                 data: this.allCountries
42677             };
42678             
42679             return cfg;
42680         },
42681         
42682         initEvents : function()
42683         {
42684             this.createList();
42685             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42686             
42687             this.indicator = this.indicatorEl();
42688             this.flag = this.flagEl();
42689             this.dialCodeHolder = this.dialCodeHolderEl();
42690             
42691             this.trigger = this.el.select('div.flag-box',true).first();
42692             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42693             
42694             var _this = this;
42695             
42696             (function(){
42697                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42698                 _this.list.setWidth(lw);
42699             }).defer(100);
42700             
42701             this.list.on('mouseover', this.onViewOver, this);
42702             this.list.on('mousemove', this.onViewMove, this);
42703             this.inputEl().on("keyup", this.onKeyUp, this);
42704             this.inputEl().on("keypress", this.onKeyPress, this);
42705             
42706             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42707
42708             this.view = new Roo.View(this.list, this.tpl, {
42709                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42710             });
42711             
42712             this.view.on('click', this.onViewClick, this);
42713             this.setValue(this.defaultDialCode);
42714         },
42715         
42716         onTriggerClick : function(e)
42717         {
42718             Roo.log('trigger click');
42719             if(this.disabled){
42720                 return;
42721             }
42722             
42723             if(this.isExpanded()){
42724                 this.collapse();
42725                 this.hasFocus = false;
42726             }else {
42727                 this.store.load({});
42728                 this.hasFocus = true;
42729                 this.expand();
42730             }
42731         },
42732         
42733         isExpanded : function()
42734         {
42735             return this.list.isVisible();
42736         },
42737         
42738         collapse : function()
42739         {
42740             if(!this.isExpanded()){
42741                 return;
42742             }
42743             this.list.hide();
42744             Roo.get(document).un('mousedown', this.collapseIf, this);
42745             Roo.get(document).un('mousewheel', this.collapseIf, this);
42746             this.fireEvent('collapse', this);
42747             this.validate();
42748         },
42749         
42750         expand : function()
42751         {
42752             Roo.log('expand');
42753
42754             if(this.isExpanded() || !this.hasFocus){
42755                 return;
42756             }
42757             
42758             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42759             this.list.setWidth(lw);
42760             
42761             this.list.show();
42762             this.restrictHeight();
42763             
42764             Roo.get(document).on('mousedown', this.collapseIf, this);
42765             Roo.get(document).on('mousewheel', this.collapseIf, this);
42766             
42767             this.fireEvent('expand', this);
42768         },
42769         
42770         restrictHeight : function()
42771         {
42772             this.list.alignTo(this.inputEl(), this.listAlign);
42773             this.list.alignTo(this.inputEl(), this.listAlign);
42774         },
42775         
42776         onViewOver : function(e, t)
42777         {
42778             if(this.inKeyMode){
42779                 return;
42780             }
42781             var item = this.view.findItemFromChild(t);
42782             
42783             if(item){
42784                 var index = this.view.indexOf(item);
42785                 this.select(index, false);
42786             }
42787         },
42788
42789         // private
42790         onViewClick : function(view, doFocus, el, e)
42791         {
42792             var index = this.view.getSelectedIndexes()[0];
42793             
42794             var r = this.store.getAt(index);
42795             
42796             if(r){
42797                 this.onSelect(r, index);
42798             }
42799             if(doFocus !== false && !this.blockFocus){
42800                 this.inputEl().focus();
42801             }
42802         },
42803         
42804         onViewMove : function(e, t)
42805         {
42806             this.inKeyMode = false;
42807         },
42808         
42809         select : function(index, scrollIntoView)
42810         {
42811             this.selectedIndex = index;
42812             this.view.select(index);
42813             if(scrollIntoView !== false){
42814                 var el = this.view.getNode(index);
42815                 if(el){
42816                     this.list.scrollChildIntoView(el, false);
42817                 }
42818             }
42819         },
42820         
42821         createList : function()
42822         {
42823             this.list = Roo.get(document.body).createChild({
42824                 tag: 'ul',
42825                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42826                 style: 'display:none'
42827             });
42828             
42829             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42830         },
42831         
42832         collapseIf : function(e)
42833         {
42834             var in_combo  = e.within(this.el);
42835             var in_list =  e.within(this.list);
42836             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42837             
42838             if (in_combo || in_list || is_list) {
42839                 return;
42840             }
42841             this.collapse();
42842         },
42843         
42844         onSelect : function(record, index)
42845         {
42846             if(this.fireEvent('beforeselect', this, record, index) !== false){
42847                 
42848                 this.setFlagClass(record.data.iso2);
42849                 this.setDialCode(record.data.dialCode);
42850                 this.hasFocus = false;
42851                 this.collapse();
42852                 this.fireEvent('select', this, record, index);
42853             }
42854         },
42855         
42856         flagEl : function()
42857         {
42858             var flag = this.el.select('div.flag',true).first();
42859             if(!flag){
42860                 return false;
42861             }
42862             return flag;
42863         },
42864         
42865         dialCodeHolderEl : function()
42866         {
42867             var d = this.el.select('input.dial-code-holder',true).first();
42868             if(!d){
42869                 return false;
42870             }
42871             return d;
42872         },
42873         
42874         setDialCode : function(v)
42875         {
42876             this.dialCodeHolder.dom.value = '+'+v;
42877         },
42878         
42879         setFlagClass : function(n)
42880         {
42881             this.flag.dom.className = 'flag '+n;
42882         },
42883         
42884         getValue : function()
42885         {
42886             var v = this.inputEl().getValue();
42887             if(this.dialCodeHolder) {
42888                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42889             }
42890             return v;
42891         },
42892         
42893         setValue : function(v)
42894         {
42895             var d = this.getDialCode(v);
42896             
42897             //invalid dial code
42898             if(v.length == 0 || !d || d.length == 0) {
42899                 if(this.rendered){
42900                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42901                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42902                 }
42903                 return;
42904             }
42905             
42906             //valid dial code
42907             this.setFlagClass(this.dialCodeMapping[d].iso2);
42908             this.setDialCode(d);
42909             this.inputEl().dom.value = v.replace('+'+d,'');
42910             this.hiddenEl().dom.value = this.getValue();
42911             
42912             this.validate();
42913         },
42914         
42915         getDialCode : function(v)
42916         {
42917             v = v ||  '';
42918             
42919             if (v.length == 0) {
42920                 return this.dialCodeHolder.dom.value;
42921             }
42922             
42923             var dialCode = "";
42924             if (v.charAt(0) != "+") {
42925                 return false;
42926             }
42927             var numericChars = "";
42928             for (var i = 1; i < v.length; i++) {
42929               var c = v.charAt(i);
42930               if (!isNaN(c)) {
42931                 numericChars += c;
42932                 if (this.dialCodeMapping[numericChars]) {
42933                   dialCode = v.substr(1, i);
42934                 }
42935                 if (numericChars.length == 4) {
42936                   break;
42937                 }
42938               }
42939             }
42940             return dialCode;
42941         },
42942         
42943         reset : function()
42944         {
42945             this.setValue(this.defaultDialCode);
42946             this.validate();
42947         },
42948         
42949         hiddenEl : function()
42950         {
42951             return this.el.select('input.hidden-tel-input',true).first();
42952         },
42953         
42954         // after setting val
42955         onKeyUp : function(e){
42956             this.setValue(this.getValue());
42957         },
42958         
42959         onKeyPress : function(e){
42960             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42961                 e.stopEvent();
42962             }
42963         }
42964         
42965 });
42966 /**
42967  * @class Roo.bootstrap.MoneyField
42968  * @extends Roo.bootstrap.ComboBox
42969  * Bootstrap MoneyField class
42970  * 
42971  * @constructor
42972  * Create a new MoneyField.
42973  * @param {Object} config Configuration options
42974  */
42975
42976 Roo.bootstrap.MoneyField = function(config) {
42977     
42978     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42979     
42980 };
42981
42982 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42983     
42984     /**
42985      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42986      */
42987     allowDecimals : true,
42988     /**
42989      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42990      */
42991     decimalSeparator : ".",
42992     /**
42993      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42994      */
42995     decimalPrecision : 0,
42996     /**
42997      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42998      */
42999     allowNegative : true,
43000     /**
43001      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43002      */
43003     allowZero: true,
43004     /**
43005      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43006      */
43007     minValue : Number.NEGATIVE_INFINITY,
43008     /**
43009      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43010      */
43011     maxValue : Number.MAX_VALUE,
43012     /**
43013      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43014      */
43015     minText : "The minimum value for this field is {0}",
43016     /**
43017      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43018      */
43019     maxText : "The maximum value for this field is {0}",
43020     /**
43021      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43022      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43023      */
43024     nanText : "{0} is not a valid number",
43025     /**
43026      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43027      */
43028     castInt : true,
43029     /**
43030      * @cfg {String} defaults currency of the MoneyField
43031      * value should be in lkey
43032      */
43033     defaultCurrency : false,
43034     /**
43035      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43036      */
43037     thousandsDelimiter : false,
43038     /**
43039      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43040      */
43041     max_length: false,
43042     
43043     inputlg : 9,
43044     inputmd : 9,
43045     inputsm : 9,
43046     inputxs : 6,
43047     
43048     store : false,
43049     
43050     getAutoCreate : function()
43051     {
43052         var align = this.labelAlign || this.parentLabelAlign();
43053         
43054         var id = Roo.id();
43055
43056         var cfg = {
43057             cls: 'form-group',
43058             cn: []
43059         };
43060
43061         var input =  {
43062             tag: 'input',
43063             id : id,
43064             cls : 'form-control roo-money-amount-input',
43065             autocomplete: 'new-password'
43066         };
43067         
43068         var hiddenInput = {
43069             tag: 'input',
43070             type: 'hidden',
43071             id: Roo.id(),
43072             cls: 'hidden-number-input'
43073         };
43074         
43075         if(this.max_length) {
43076             input.maxlength = this.max_length; 
43077         }
43078         
43079         if (this.name) {
43080             hiddenInput.name = this.name;
43081         }
43082
43083         if (this.disabled) {
43084             input.disabled = true;
43085         }
43086
43087         var clg = 12 - this.inputlg;
43088         var cmd = 12 - this.inputmd;
43089         var csm = 12 - this.inputsm;
43090         var cxs = 12 - this.inputxs;
43091         
43092         var container = {
43093             tag : 'div',
43094             cls : 'row roo-money-field',
43095             cn : [
43096                 {
43097                     tag : 'div',
43098                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43099                     cn : [
43100                         {
43101                             tag : 'div',
43102                             cls: 'roo-select2-container input-group',
43103                             cn: [
43104                                 {
43105                                     tag : 'input',
43106                                     cls : 'form-control roo-money-currency-input',
43107                                     autocomplete: 'new-password',
43108                                     readOnly : 1,
43109                                     name : this.currencyName
43110                                 },
43111                                 {
43112                                     tag :'span',
43113                                     cls : 'input-group-addon',
43114                                     cn : [
43115                                         {
43116                                             tag: 'span',
43117                                             cls: 'caret'
43118                                         }
43119                                     ]
43120                                 }
43121                             ]
43122                         }
43123                     ]
43124                 },
43125                 {
43126                     tag : 'div',
43127                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43128                     cn : [
43129                         {
43130                             tag: 'div',
43131                             cls: this.hasFeedback ? 'has-feedback' : '',
43132                             cn: [
43133                                 input
43134                             ]
43135                         }
43136                     ]
43137                 }
43138             ]
43139             
43140         };
43141         
43142         if (this.fieldLabel.length) {
43143             var indicator = {
43144                 tag: 'i',
43145                 tooltip: 'This field is required'
43146             };
43147
43148             var label = {
43149                 tag: 'label',
43150                 'for':  id,
43151                 cls: 'control-label',
43152                 cn: []
43153             };
43154
43155             var label_text = {
43156                 tag: 'span',
43157                 html: this.fieldLabel
43158             };
43159
43160             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43161             label.cn = [
43162                 indicator,
43163                 label_text
43164             ];
43165
43166             if(this.indicatorpos == 'right') {
43167                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43168                 label.cn = [
43169                     label_text,
43170                     indicator
43171                 ];
43172             }
43173
43174             if(align == 'left') {
43175                 container = {
43176                     tag: 'div',
43177                     cn: [
43178                         container
43179                     ]
43180                 };
43181
43182                 if(this.labelWidth > 12){
43183                     label.style = "width: " + this.labelWidth + 'px';
43184                 }
43185                 if(this.labelWidth < 13 && this.labelmd == 0){
43186                     this.labelmd = this.labelWidth;
43187                 }
43188                 if(this.labellg > 0){
43189                     label.cls += ' col-lg-' + this.labellg;
43190                     input.cls += ' col-lg-' + (12 - this.labellg);
43191                 }
43192                 if(this.labelmd > 0){
43193                     label.cls += ' col-md-' + this.labelmd;
43194                     container.cls += ' col-md-' + (12 - this.labelmd);
43195                 }
43196                 if(this.labelsm > 0){
43197                     label.cls += ' col-sm-' + this.labelsm;
43198                     container.cls += ' col-sm-' + (12 - this.labelsm);
43199                 }
43200                 if(this.labelxs > 0){
43201                     label.cls += ' col-xs-' + this.labelxs;
43202                     container.cls += ' col-xs-' + (12 - this.labelxs);
43203                 }
43204             }
43205         }
43206
43207         cfg.cn = [
43208             label,
43209             container,
43210             hiddenInput
43211         ];
43212         
43213         var settings = this;
43214
43215         ['xs','sm','md','lg'].map(function(size){
43216             if (settings[size]) {
43217                 cfg.cls += ' col-' + size + '-' + settings[size];
43218             }
43219         });
43220         
43221         return cfg;
43222     },
43223     
43224     initEvents : function()
43225     {
43226         this.indicator = this.indicatorEl();
43227         
43228         this.initCurrencyEvent();
43229         
43230         this.initNumberEvent();
43231     },
43232     
43233     initCurrencyEvent : function()
43234     {
43235         if (!this.store) {
43236             throw "can not find store for combo";
43237         }
43238         
43239         this.store = Roo.factory(this.store, Roo.data);
43240         this.store.parent = this;
43241         
43242         this.createList();
43243         
43244         this.triggerEl = this.el.select('.input-group-addon', true).first();
43245         
43246         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43247         
43248         var _this = this;
43249         
43250         (function(){
43251             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43252             _this.list.setWidth(lw);
43253         }).defer(100);
43254         
43255         this.list.on('mouseover', this.onViewOver, this);
43256         this.list.on('mousemove', this.onViewMove, this);
43257         this.list.on('scroll', this.onViewScroll, this);
43258         
43259         if(!this.tpl){
43260             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43261         }
43262         
43263         this.view = new Roo.View(this.list, this.tpl, {
43264             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43265         });
43266         
43267         this.view.on('click', this.onViewClick, this);
43268         
43269         this.store.on('beforeload', this.onBeforeLoad, this);
43270         this.store.on('load', this.onLoad, this);
43271         this.store.on('loadexception', this.onLoadException, this);
43272         
43273         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43274             "up" : function(e){
43275                 this.inKeyMode = true;
43276                 this.selectPrev();
43277             },
43278
43279             "down" : function(e){
43280                 if(!this.isExpanded()){
43281                     this.onTriggerClick();
43282                 }else{
43283                     this.inKeyMode = true;
43284                     this.selectNext();
43285                 }
43286             },
43287
43288             "enter" : function(e){
43289                 this.collapse();
43290                 
43291                 if(this.fireEvent("specialkey", this, e)){
43292                     this.onViewClick(false);
43293                 }
43294                 
43295                 return true;
43296             },
43297
43298             "esc" : function(e){
43299                 this.collapse();
43300             },
43301
43302             "tab" : function(e){
43303                 this.collapse();
43304                 
43305                 if(this.fireEvent("specialkey", this, e)){
43306                     this.onViewClick(false);
43307                 }
43308                 
43309                 return true;
43310             },
43311
43312             scope : this,
43313
43314             doRelay : function(foo, bar, hname){
43315                 if(hname == 'down' || this.scope.isExpanded()){
43316                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43317                 }
43318                 return true;
43319             },
43320
43321             forceKeyDown: true
43322         });
43323         
43324         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43325         
43326     },
43327     
43328     initNumberEvent : function(e)
43329     {
43330         this.inputEl().on("keydown" , this.fireKey,  this);
43331         this.inputEl().on("focus", this.onFocus,  this);
43332         this.inputEl().on("blur", this.onBlur,  this);
43333         
43334         this.inputEl().relayEvent('keyup', this);
43335         
43336         if(this.indicator){
43337             this.indicator.addClass('invisible');
43338         }
43339  
43340         this.originalValue = this.getValue();
43341         
43342         if(this.validationEvent == 'keyup'){
43343             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43344             this.inputEl().on('keyup', this.filterValidation, this);
43345         }
43346         else if(this.validationEvent !== false){
43347             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43348         }
43349         
43350         if(this.selectOnFocus){
43351             this.on("focus", this.preFocus, this);
43352             
43353         }
43354         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43355             this.inputEl().on("keypress", this.filterKeys, this);
43356         } else {
43357             this.inputEl().relayEvent('keypress', this);
43358         }
43359         
43360         var allowed = "0123456789";
43361         
43362         if(this.allowDecimals){
43363             allowed += this.decimalSeparator;
43364         }
43365         
43366         if(this.allowNegative){
43367             allowed += "-";
43368         }
43369         
43370         if(this.thousandsDelimiter) {
43371             allowed += ",";
43372         }
43373         
43374         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43375         
43376         var keyPress = function(e){
43377             
43378             var k = e.getKey();
43379             
43380             var c = e.getCharCode();
43381             
43382             if(
43383                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43384                     allowed.indexOf(String.fromCharCode(c)) === -1
43385             ){
43386                 e.stopEvent();
43387                 return;
43388             }
43389             
43390             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43391                 return;
43392             }
43393             
43394             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43395                 e.stopEvent();
43396             }
43397         };
43398         
43399         this.inputEl().on("keypress", keyPress, this);
43400         
43401     },
43402     
43403     onTriggerClick : function(e)
43404     {   
43405         if(this.disabled){
43406             return;
43407         }
43408         
43409         this.page = 0;
43410         this.loadNext = false;
43411         
43412         if(this.isExpanded()){
43413             this.collapse();
43414             return;
43415         }
43416         
43417         this.hasFocus = true;
43418         
43419         if(this.triggerAction == 'all') {
43420             this.doQuery(this.allQuery, true);
43421             return;
43422         }
43423         
43424         this.doQuery(this.getRawValue());
43425     },
43426     
43427     getCurrency : function()
43428     {   
43429         var v = this.currencyEl().getValue();
43430         
43431         return v;
43432     },
43433     
43434     restrictHeight : function()
43435     {
43436         this.list.alignTo(this.currencyEl(), this.listAlign);
43437         this.list.alignTo(this.currencyEl(), this.listAlign);
43438     },
43439     
43440     onViewClick : function(view, doFocus, el, e)
43441     {
43442         var index = this.view.getSelectedIndexes()[0];
43443         
43444         var r = this.store.getAt(index);
43445         
43446         if(r){
43447             this.onSelect(r, index);
43448         }
43449     },
43450     
43451     onSelect : function(record, index){
43452         
43453         if(this.fireEvent('beforeselect', this, record, index) !== false){
43454         
43455             this.setFromCurrencyData(index > -1 ? record.data : false);
43456             
43457             this.collapse();
43458             
43459             this.fireEvent('select', this, record, index);
43460         }
43461     },
43462     
43463     setFromCurrencyData : function(o)
43464     {
43465         var currency = '';
43466         
43467         this.lastCurrency = o;
43468         
43469         if (this.currencyField) {
43470             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43471         } else {
43472             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43473         }
43474         
43475         this.lastSelectionText = currency;
43476         
43477         //setting default currency
43478         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43479             this.setCurrency(this.defaultCurrency);
43480             return;
43481         }
43482         
43483         this.setCurrency(currency);
43484     },
43485     
43486     setFromData : function(o)
43487     {
43488         var c = {};
43489         
43490         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43491         
43492         this.setFromCurrencyData(c);
43493         
43494         var value = '';
43495         
43496         if (this.name) {
43497             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43498         } else {
43499             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43500         }
43501         
43502         this.setValue(value);
43503         
43504     },
43505     
43506     setCurrency : function(v)
43507     {   
43508         this.currencyValue = v;
43509         
43510         if(this.rendered){
43511             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43512             this.validate();
43513         }
43514     },
43515     
43516     setValue : function(v)
43517     {
43518         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43519         
43520         this.value = v;
43521         
43522         if(this.rendered){
43523             
43524             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43525             
43526             this.inputEl().dom.value = (v == '') ? '' :
43527                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43528             
43529             if(!this.allowZero && v === '0') {
43530                 this.hiddenEl().dom.value = '';
43531                 this.inputEl().dom.value = '';
43532             }
43533             
43534             this.validate();
43535         }
43536     },
43537     
43538     getRawValue : function()
43539     {
43540         var v = this.inputEl().getValue();
43541         
43542         return v;
43543     },
43544     
43545     getValue : function()
43546     {
43547         return this.fixPrecision(this.parseValue(this.getRawValue()));
43548     },
43549     
43550     parseValue : function(value)
43551     {
43552         if(this.thousandsDelimiter) {
43553             value += "";
43554             r = new RegExp(",", "g");
43555             value = value.replace(r, "");
43556         }
43557         
43558         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43559         return isNaN(value) ? '' : value;
43560         
43561     },
43562     
43563     fixPrecision : function(value)
43564     {
43565         if(this.thousandsDelimiter) {
43566             value += "";
43567             r = new RegExp(",", "g");
43568             value = value.replace(r, "");
43569         }
43570         
43571         var nan = isNaN(value);
43572         
43573         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43574             return nan ? '' : value;
43575         }
43576         return parseFloat(value).toFixed(this.decimalPrecision);
43577     },
43578     
43579     decimalPrecisionFcn : function(v)
43580     {
43581         return Math.floor(v);
43582     },
43583     
43584     validateValue : function(value)
43585     {
43586         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43587             return false;
43588         }
43589         
43590         var num = this.parseValue(value);
43591         
43592         if(isNaN(num)){
43593             this.markInvalid(String.format(this.nanText, value));
43594             return false;
43595         }
43596         
43597         if(num < this.minValue){
43598             this.markInvalid(String.format(this.minText, this.minValue));
43599             return false;
43600         }
43601         
43602         if(num > this.maxValue){
43603             this.markInvalid(String.format(this.maxText, this.maxValue));
43604             return false;
43605         }
43606         
43607         return true;
43608     },
43609     
43610     validate : function()
43611     {
43612         if(this.disabled || this.allowBlank){
43613             this.markValid();
43614             return true;
43615         }
43616         
43617         var currency = this.getCurrency();
43618         
43619         if(this.validateValue(this.getRawValue()) && currency.length){
43620             this.markValid();
43621             return true;
43622         }
43623         
43624         this.markInvalid();
43625         return false;
43626     },
43627     
43628     getName: function()
43629     {
43630         return this.name;
43631     },
43632     
43633     beforeBlur : function()
43634     {
43635         if(!this.castInt){
43636             return;
43637         }
43638         
43639         var v = this.parseValue(this.getRawValue());
43640         
43641         if(v || v == 0){
43642             this.setValue(v);
43643         }
43644     },
43645     
43646     onBlur : function()
43647     {
43648         this.beforeBlur();
43649         
43650         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43651             //this.el.removeClass(this.focusClass);
43652         }
43653         
43654         this.hasFocus = false;
43655         
43656         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43657             this.validate();
43658         }
43659         
43660         var v = this.getValue();
43661         
43662         if(String(v) !== String(this.startValue)){
43663             this.fireEvent('change', this, v, this.startValue);
43664         }
43665         
43666         this.fireEvent("blur", this);
43667     },
43668     
43669     inputEl : function()
43670     {
43671         return this.el.select('.roo-money-amount-input', true).first();
43672     },
43673     
43674     currencyEl : function()
43675     {
43676         return this.el.select('.roo-money-currency-input', true).first();
43677     },
43678     
43679     hiddenEl : function()
43680     {
43681         return this.el.select('input.hidden-number-input',true).first();
43682     }
43683     
43684 });/**
43685  * @class Roo.bootstrap.BezierSignature
43686  * @extends Roo.bootstrap.Component
43687  * Bootstrap BezierSignature class
43688  * This script refer to:
43689  *    Title: Signature Pad
43690  *    Author: szimek
43691  *    Availability: https://github.com/szimek/signature_pad
43692  *
43693  * @constructor
43694  * Create a new BezierSignature
43695  * @param {Object} config The config object
43696  */
43697
43698 Roo.bootstrap.BezierSignature = function(config){
43699     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43700     this.addEvents({
43701         "resize" : true
43702     });
43703 };
43704
43705 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43706 {
43707      
43708     curve_data: [],
43709     
43710     is_empty: true,
43711     
43712     mouse_btn_down: true,
43713     
43714     /**
43715      * @cfg {int} canvas height
43716      */
43717     canvas_height: '200px',
43718     
43719     /**
43720      * @cfg {float|function} Radius of a single dot.
43721      */ 
43722     dot_size: false,
43723     
43724     /**
43725      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43726      */
43727     min_width: 0.5,
43728     
43729     /**
43730      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43731      */
43732     max_width: 2.5,
43733     
43734     /**
43735      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43736      */
43737     throttle: 16,
43738     
43739     /**
43740      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43741      */
43742     min_distance: 5,
43743     
43744     /**
43745      * @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.
43746      */
43747     bg_color: 'rgba(0, 0, 0, 0)',
43748     
43749     /**
43750      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43751      */
43752     dot_color: 'black',
43753     
43754     /**
43755      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43756      */ 
43757     velocity_filter_weight: 0.7,
43758     
43759     /**
43760      * @cfg {function} Callback when stroke begin. 
43761      */
43762     onBegin: false,
43763     
43764     /**
43765      * @cfg {function} Callback when stroke end.
43766      */
43767     onEnd: false,
43768     
43769     getAutoCreate : function()
43770     {
43771         var cls = 'roo-signature column';
43772         
43773         if(this.cls){
43774             cls += ' ' + this.cls;
43775         }
43776         
43777         var col_sizes = [
43778             'lg',
43779             'md',
43780             'sm',
43781             'xs'
43782         ];
43783         
43784         for(var i = 0; i < col_sizes.length; i++) {
43785             if(this[col_sizes[i]]) {
43786                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43787             }
43788         }
43789         
43790         var cfg = {
43791             tag: 'div',
43792             cls: cls,
43793             cn: [
43794                 {
43795                     tag: 'div',
43796                     cls: 'roo-signature-body',
43797                     cn: [
43798                         {
43799                             tag: 'canvas',
43800                             cls: 'roo-signature-body-canvas',
43801                             height: this.canvas_height,
43802                             width: this.canvas_width
43803                         }
43804                     ]
43805                 },
43806                 {
43807                     tag: 'input',
43808                     type: 'file',
43809                     style: 'display: none'
43810                 }
43811             ]
43812         };
43813         
43814         return cfg;
43815     },
43816     
43817     initEvents: function() 
43818     {
43819         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43820         
43821         var canvas = this.canvasEl();
43822         
43823         // mouse && touch event swapping...
43824         canvas.dom.style.touchAction = 'none';
43825         canvas.dom.style.msTouchAction = 'none';
43826         
43827         this.mouse_btn_down = false;
43828         canvas.on('mousedown', this._handleMouseDown, this);
43829         canvas.on('mousemove', this._handleMouseMove, this);
43830         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43831         
43832         if (window.PointerEvent) {
43833             canvas.on('pointerdown', this._handleMouseDown, this);
43834             canvas.on('pointermove', this._handleMouseMove, this);
43835             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43836         }
43837         
43838         if ('ontouchstart' in window) {
43839             canvas.on('touchstart', this._handleTouchStart, this);
43840             canvas.on('touchmove', this._handleTouchMove, this);
43841             canvas.on('touchend', this._handleTouchEnd, this);
43842         }
43843         
43844         Roo.EventManager.onWindowResize(this.resize, this, true);
43845         
43846         // file input event
43847         this.fileEl().on('change', this.uploadImage, this);
43848         
43849         this.clear();
43850         
43851         this.resize();
43852     },
43853     
43854     resize: function(){
43855         
43856         var canvas = this.canvasEl().dom;
43857         var ctx = this.canvasElCtx();
43858         var img_data = false;
43859         
43860         if(canvas.width > 0) {
43861             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43862         }
43863         // setting canvas width will clean img data
43864         canvas.width = 0;
43865         
43866         var style = window.getComputedStyle ? 
43867             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43868             
43869         var padding_left = parseInt(style.paddingLeft) || 0;
43870         var padding_right = parseInt(style.paddingRight) || 0;
43871         
43872         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43873         
43874         if(img_data) {
43875             ctx.putImageData(img_data, 0, 0);
43876         }
43877     },
43878     
43879     _handleMouseDown: function(e)
43880     {
43881         if (e.browserEvent.which === 1) {
43882             this.mouse_btn_down = true;
43883             this.strokeBegin(e);
43884         }
43885     },
43886     
43887     _handleMouseMove: function (e)
43888     {
43889         if (this.mouse_btn_down) {
43890             this.strokeMoveUpdate(e);
43891         }
43892     },
43893     
43894     _handleMouseUp: function (e)
43895     {
43896         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43897             this.mouse_btn_down = false;
43898             this.strokeEnd(e);
43899         }
43900     },
43901     
43902     _handleTouchStart: function (e) {
43903         
43904         e.preventDefault();
43905         if (e.browserEvent.targetTouches.length === 1) {
43906             // var touch = e.browserEvent.changedTouches[0];
43907             // this.strokeBegin(touch);
43908             
43909              this.strokeBegin(e); // assume e catching the correct xy...
43910         }
43911     },
43912     
43913     _handleTouchMove: function (e) {
43914         e.preventDefault();
43915         // var touch = event.targetTouches[0];
43916         // _this._strokeMoveUpdate(touch);
43917         this.strokeMoveUpdate(e);
43918     },
43919     
43920     _handleTouchEnd: function (e) {
43921         var wasCanvasTouched = e.target === this.canvasEl().dom;
43922         if (wasCanvasTouched) {
43923             e.preventDefault();
43924             // var touch = event.changedTouches[0];
43925             // _this._strokeEnd(touch);
43926             this.strokeEnd(e);
43927         }
43928     },
43929     
43930     reset: function () {
43931         this._lastPoints = [];
43932         this._lastVelocity = 0;
43933         this._lastWidth = (this.min_width + this.max_width) / 2;
43934         this.canvasElCtx().fillStyle = this.dot_color;
43935     },
43936     
43937     strokeMoveUpdate: function(e)
43938     {
43939         this.strokeUpdate(e);
43940         
43941         if (this.throttle) {
43942             this.throttleStroke(this.strokeUpdate, this.throttle);
43943         }
43944         else {
43945             this.strokeUpdate(e);
43946         }
43947     },
43948     
43949     strokeBegin: function(e)
43950     {
43951         var newPointGroup = {
43952             color: this.dot_color,
43953             points: []
43954         };
43955         
43956         if (typeof this.onBegin === 'function') {
43957             this.onBegin(e);
43958         }
43959         
43960         this.curve_data.push(newPointGroup);
43961         this.reset();
43962         this.strokeUpdate(e);
43963     },
43964     
43965     strokeUpdate: function(e)
43966     {
43967         var rect = this.canvasEl().dom.getBoundingClientRect();
43968         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43969         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43970         var lastPoints = lastPointGroup.points;
43971         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43972         var isLastPointTooClose = lastPoint
43973             ? point.distanceTo(lastPoint) <= this.min_distance
43974             : false;
43975         var color = lastPointGroup.color;
43976         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43977             var curve = this.addPoint(point);
43978             if (!lastPoint) {
43979                 this.drawDot({color: color, point: point});
43980             }
43981             else if (curve) {
43982                 this.drawCurve({color: color, curve: curve});
43983             }
43984             lastPoints.push({
43985                 time: point.time,
43986                 x: point.x,
43987                 y: point.y
43988             });
43989         }
43990     },
43991     
43992     strokeEnd: function(e)
43993     {
43994         this.strokeUpdate(e);
43995         if (typeof this.onEnd === 'function') {
43996             this.onEnd(e);
43997         }
43998     },
43999     
44000     addPoint:  function (point) {
44001         var _lastPoints = this._lastPoints;
44002         _lastPoints.push(point);
44003         if (_lastPoints.length > 2) {
44004             if (_lastPoints.length === 3) {
44005                 _lastPoints.unshift(_lastPoints[0]);
44006             }
44007             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44008             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44009             _lastPoints.shift();
44010             return curve;
44011         }
44012         return null;
44013     },
44014     
44015     calculateCurveWidths: function (startPoint, endPoint) {
44016         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44017             (1 - this.velocity_filter_weight) * this._lastVelocity;
44018
44019         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44020         var widths = {
44021             end: newWidth,
44022             start: this._lastWidth
44023         };
44024         
44025         this._lastVelocity = velocity;
44026         this._lastWidth = newWidth;
44027         return widths;
44028     },
44029     
44030     drawDot: function (_a) {
44031         var color = _a.color, point = _a.point;
44032         var ctx = this.canvasElCtx();
44033         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44034         ctx.beginPath();
44035         this.drawCurveSegment(point.x, point.y, width);
44036         ctx.closePath();
44037         ctx.fillStyle = color;
44038         ctx.fill();
44039     },
44040     
44041     drawCurve: function (_a) {
44042         var color = _a.color, curve = _a.curve;
44043         var ctx = this.canvasElCtx();
44044         var widthDelta = curve.endWidth - curve.startWidth;
44045         var drawSteps = Math.floor(curve.length()) * 2;
44046         ctx.beginPath();
44047         ctx.fillStyle = color;
44048         for (var i = 0; i < drawSteps; i += 1) {
44049         var t = i / drawSteps;
44050         var tt = t * t;
44051         var ttt = tt * t;
44052         var u = 1 - t;
44053         var uu = u * u;
44054         var uuu = uu * u;
44055         var x = uuu * curve.startPoint.x;
44056         x += 3 * uu * t * curve.control1.x;
44057         x += 3 * u * tt * curve.control2.x;
44058         x += ttt * curve.endPoint.x;
44059         var y = uuu * curve.startPoint.y;
44060         y += 3 * uu * t * curve.control1.y;
44061         y += 3 * u * tt * curve.control2.y;
44062         y += ttt * curve.endPoint.y;
44063         var width = curve.startWidth + ttt * widthDelta;
44064         this.drawCurveSegment(x, y, width);
44065         }
44066         ctx.closePath();
44067         ctx.fill();
44068     },
44069     
44070     drawCurveSegment: function (x, y, width) {
44071         var ctx = this.canvasElCtx();
44072         ctx.moveTo(x, y);
44073         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44074         this.is_empty = false;
44075     },
44076     
44077     clear: function()
44078     {
44079         var ctx = this.canvasElCtx();
44080         var canvas = this.canvasEl().dom;
44081         ctx.fillStyle = this.bg_color;
44082         ctx.clearRect(0, 0, canvas.width, canvas.height);
44083         ctx.fillRect(0, 0, canvas.width, canvas.height);
44084         this.curve_data = [];
44085         this.reset();
44086         this.is_empty = true;
44087     },
44088     
44089     fileEl: function()
44090     {
44091         return  this.el.select('input',true).first();
44092     },
44093     
44094     canvasEl: function()
44095     {
44096         return this.el.select('canvas',true).first();
44097     },
44098     
44099     canvasElCtx: function()
44100     {
44101         return this.el.select('canvas',true).first().dom.getContext('2d');
44102     },
44103     
44104     getImage: function(type)
44105     {
44106         if(this.is_empty) {
44107             return false;
44108         }
44109         
44110         // encryption ?
44111         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44112     },
44113     
44114     drawFromImage: function(img_src)
44115     {
44116         var img = new Image();
44117         
44118         img.onload = function(){
44119             this.canvasElCtx().drawImage(img, 0, 0);
44120         }.bind(this);
44121         
44122         img.src = img_src;
44123         
44124         this.is_empty = false;
44125     },
44126     
44127     selectImage: function()
44128     {
44129         this.fileEl().dom.click();
44130     },
44131     
44132     uploadImage: function(e)
44133     {
44134         var reader = new FileReader();
44135         
44136         reader.onload = function(e){
44137             var img = new Image();
44138             img.onload = function(){
44139                 this.reset();
44140                 this.canvasElCtx().drawImage(img, 0, 0);
44141             }.bind(this);
44142             img.src = e.target.result;
44143         }.bind(this);
44144         
44145         reader.readAsDataURL(e.target.files[0]);
44146     },
44147     
44148     // Bezier Point Constructor
44149     Point: (function () {
44150         function Point(x, y, time) {
44151             this.x = x;
44152             this.y = y;
44153             this.time = time || Date.now();
44154         }
44155         Point.prototype.distanceTo = function (start) {
44156             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44157         };
44158         Point.prototype.equals = function (other) {
44159             return this.x === other.x && this.y === other.y && this.time === other.time;
44160         };
44161         Point.prototype.velocityFrom = function (start) {
44162             return this.time !== start.time
44163             ? this.distanceTo(start) / (this.time - start.time)
44164             : 0;
44165         };
44166         return Point;
44167     }()),
44168     
44169     
44170     // Bezier Constructor
44171     Bezier: (function () {
44172         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44173             this.startPoint = startPoint;
44174             this.control2 = control2;
44175             this.control1 = control1;
44176             this.endPoint = endPoint;
44177             this.startWidth = startWidth;
44178             this.endWidth = endWidth;
44179         }
44180         Bezier.fromPoints = function (points, widths, scope) {
44181             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44182             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44183             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44184         };
44185         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44186             var dx1 = s1.x - s2.x;
44187             var dy1 = s1.y - s2.y;
44188             var dx2 = s2.x - s3.x;
44189             var dy2 = s2.y - s3.y;
44190             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44191             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44192             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44193             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44194             var dxm = m1.x - m2.x;
44195             var dym = m1.y - m2.y;
44196             var k = l2 / (l1 + l2);
44197             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44198             var tx = s2.x - cm.x;
44199             var ty = s2.y - cm.y;
44200             return {
44201                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44202                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44203             };
44204         };
44205         Bezier.prototype.length = function () {
44206             var steps = 10;
44207             var length = 0;
44208             var px;
44209             var py;
44210             for (var i = 0; i <= steps; i += 1) {
44211                 var t = i / steps;
44212                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44213                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44214                 if (i > 0) {
44215                     var xdiff = cx - px;
44216                     var ydiff = cy - py;
44217                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44218                 }
44219                 px = cx;
44220                 py = cy;
44221             }
44222             return length;
44223         };
44224         Bezier.prototype.point = function (t, start, c1, c2, end) {
44225             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44226             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44227             + (3.0 * c2 * (1.0 - t) * t * t)
44228             + (end * t * t * t);
44229         };
44230         return Bezier;
44231     }()),
44232     
44233     throttleStroke: function(fn, wait) {
44234       if (wait === void 0) { wait = 250; }
44235       var previous = 0;
44236       var timeout = null;
44237       var result;
44238       var storedContext;
44239       var storedArgs;
44240       var later = function () {
44241           previous = Date.now();
44242           timeout = null;
44243           result = fn.apply(storedContext, storedArgs);
44244           if (!timeout) {
44245               storedContext = null;
44246               storedArgs = [];
44247           }
44248       };
44249       return function wrapper() {
44250           var args = [];
44251           for (var _i = 0; _i < arguments.length; _i++) {
44252               args[_i] = arguments[_i];
44253           }
44254           var now = Date.now();
44255           var remaining = wait - (now - previous);
44256           storedContext = this;
44257           storedArgs = args;
44258           if (remaining <= 0 || remaining > wait) {
44259               if (timeout) {
44260                   clearTimeout(timeout);
44261                   timeout = null;
44262               }
44263               previous = now;
44264               result = fn.apply(storedContext, storedArgs);
44265               if (!timeout) {
44266                   storedContext = null;
44267                   storedArgs = [];
44268               }
44269           }
44270           else if (!timeout) {
44271               timeout = window.setTimeout(later, remaining);
44272           }
44273           return result;
44274       };
44275   }
44276   
44277 });
44278
44279  
44280
44281