Roo/bootstrap/Popover.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {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
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     initEvents : function()
19751     {
19752         
19753         Roo.bootstrap.Popover.register(this);
19754         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19755         this.el.enableDisplayMode('block');
19756         this.el.hide();
19757         if (this.over === false && !this.parent()) {
19758             return; 
19759         }
19760         if (this.triggers === false) {
19761             return;
19762         }
19763          
19764         // support parent
19765         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19766         var triggers = this.trigger ? this.trigger.split(' ') : [];
19767         Roo.each(triggers, function(trigger) {
19768         
19769             if (trigger == 'click') {
19770                 on_el.on('click', this.toggle, this);
19771             } else if (trigger != 'manual') {
19772                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19773                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19774       
19775                 on_el.on(eventIn  ,this.enter, this);
19776                 on_el.on(eventOut, this.leave, this);
19777             }
19778         }, this);
19779         
19780     },
19781     
19782     
19783     // private
19784     timeout : null,
19785     hoverState : null,
19786     
19787     toggle : function () {
19788         this.hoverState == 'in' ? this.leave() : this.enter();
19789     },
19790     
19791     enter : function () {
19792         
19793         clearTimeout(this.timeout);
19794     
19795         this.hoverState = 'in';
19796     
19797         if (!this.delay || !this.delay.show) {
19798             this.show();
19799             return;
19800         }
19801         var _t = this;
19802         this.timeout = setTimeout(function () {
19803             if (_t.hoverState == 'in') {
19804                 _t.show();
19805             }
19806         }, this.delay.show)
19807     },
19808     
19809     leave : function() {
19810         clearTimeout(this.timeout);
19811     
19812         this.hoverState = 'out';
19813     
19814         if (!this.delay || !this.delay.hide) {
19815             this.hide();
19816             return;
19817         }
19818         var _t = this;
19819         this.timeout = setTimeout(function () {
19820             if (_t.hoverState == 'out') {
19821                 _t.hide();
19822             }
19823         }, this.delay.hide)
19824     },
19825     /**
19826      * Show the popover
19827      * @param {Roo.Element|string|false} - element to align and point to.
19828      */
19829     show : function (on_el)
19830     {
19831         
19832         on_el = on_el || false; // default to false
19833         if (!on_el) {
19834             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19835                 on_el = this.parent().el;
19836             } else if (this.over) {
19837                 Roo.get(this.over);
19838             }
19839             
19840         }
19841         
19842         if (!this.el) {
19843             this.render(document.body);
19844         }
19845         
19846         
19847         this.el.removeClass([
19848             'fade','top','bottom', 'left', 'right','in',
19849             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19850         ]);
19851         
19852         if (!this.title.length) {
19853             this.el.select('.popover-title',true).hide();
19854         }
19855         
19856         
19857         var placement = typeof this.placement == 'function' ?
19858             this.placement.call(this, this.el, on_el) :
19859             this.placement;
19860             
19861         /*
19862         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19863         
19864         // I think  'auto right' - but 
19865         
19866         var autoPlace = autoToken.test(placement);
19867         if (autoPlace) {
19868             placement = placement.replace(autoToken, '') || 'top';
19869         }
19870         */
19871         
19872         
19873         this.el.show();
19874         this.el.dom.style.display='block';
19875         
19876         //this.el.appendTo(on_el);
19877         
19878         var p = this.getPosition();
19879         var box = this.el.getBox();
19880         
19881         
19882         var align = Roo.bootstrap.Popover.alignment[placement];
19883         this.el.addClass(align[2]);
19884
19885 //        Roo.log(align);
19886
19887         if (on_el) {
19888             this.el.alignTo(on_el, align[0],align[1]);
19889         } else {
19890             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19891             var es = this.el.getSize();
19892             var x = Roo.lib.Dom.getViewWidth()/2;
19893             var y = Roo.lib.Dom.getViewHeight()/2;
19894             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19895             
19896         }
19897
19898         
19899         //var arrow = this.el.select('.arrow',true).first();
19900         //arrow.set(align[2], 
19901         
19902         this.el.addClass('in');
19903         
19904         
19905         if (this.el.hasClass('fade')) {
19906             // fade it?
19907         }
19908         
19909         this.hoverState = 'in';
19910         
19911         this.fireEvent('show', this);
19912         
19913     },
19914     hide : function()
19915     {
19916         this.el.setXY([0,0]);
19917         this.el.removeClass('in');
19918         this.el.hide();
19919         this.hoverState = null;
19920         
19921         this.fireEvent('hide', this);
19922     }
19923     
19924 });
19925
19926
19927 Roo.apply(Roo.bootstrap.Popover, {
19928
19929     alignment : {
19930         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19931         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19932         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19933         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19934     },
19935
19936     clickHander : false,
19937     
19938
19939     onMouseDown : function(e)
19940     {
19941         if (!e.getTarget(".roo-popover")) {
19942             this.hideAll();
19943         }
19944          
19945     },
19946     
19947     popups : [],
19948     
19949     register : function(popup)
19950     {
19951         if (!Roo.bootstrap.Popover.clickHandler) {
19952             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19953         }
19954         // hide other popups.
19955         this.hideAll();
19956         this.popups.push(popup);
19957     },
19958     hideAll : function()
19959     {
19960         this.popups.forEach(function(p) {
19961             p.hide();
19962         });
19963     }
19964
19965 });/*
19966  * - LGPL
19967  *
19968  * Progress
19969  * 
19970  */
19971
19972 /**
19973  * @class Roo.bootstrap.Progress
19974  * @extends Roo.bootstrap.Component
19975  * Bootstrap Progress class
19976  * @cfg {Boolean} striped striped of the progress bar
19977  * @cfg {Boolean} active animated of the progress bar
19978  * 
19979  * 
19980  * @constructor
19981  * Create a new Progress
19982  * @param {Object} config The config object
19983  */
19984
19985 Roo.bootstrap.Progress = function(config){
19986     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19987 };
19988
19989 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19990     
19991     striped : false,
19992     active: false,
19993     
19994     getAutoCreate : function(){
19995         var cfg = {
19996             tag: 'div',
19997             cls: 'progress'
19998         };
19999         
20000         
20001         if(this.striped){
20002             cfg.cls += ' progress-striped';
20003         }
20004       
20005         if(this.active){
20006             cfg.cls += ' active';
20007         }
20008         
20009         
20010         return cfg;
20011     }
20012    
20013 });
20014
20015  
20016
20017  /*
20018  * - LGPL
20019  *
20020  * ProgressBar
20021  * 
20022  */
20023
20024 /**
20025  * @class Roo.bootstrap.ProgressBar
20026  * @extends Roo.bootstrap.Component
20027  * Bootstrap ProgressBar class
20028  * @cfg {Number} aria_valuenow aria-value now
20029  * @cfg {Number} aria_valuemin aria-value min
20030  * @cfg {Number} aria_valuemax aria-value max
20031  * @cfg {String} label label for the progress bar
20032  * @cfg {String} panel (success | info | warning | danger )
20033  * @cfg {String} role role of the progress bar
20034  * @cfg {String} sr_only text
20035  * 
20036  * 
20037  * @constructor
20038  * Create a new ProgressBar
20039  * @param {Object} config The config object
20040  */
20041
20042 Roo.bootstrap.ProgressBar = function(config){
20043     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20044 };
20045
20046 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20047     
20048     aria_valuenow : 0,
20049     aria_valuemin : 0,
20050     aria_valuemax : 100,
20051     label : false,
20052     panel : false,
20053     role : false,
20054     sr_only: false,
20055     
20056     getAutoCreate : function()
20057     {
20058         
20059         var cfg = {
20060             tag: 'div',
20061             cls: 'progress-bar',
20062             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20063         };
20064         
20065         if(this.sr_only){
20066             cfg.cn = {
20067                 tag: 'span',
20068                 cls: 'sr-only',
20069                 html: this.sr_only
20070             }
20071         }
20072         
20073         if(this.role){
20074             cfg.role = this.role;
20075         }
20076         
20077         if(this.aria_valuenow){
20078             cfg['aria-valuenow'] = this.aria_valuenow;
20079         }
20080         
20081         if(this.aria_valuemin){
20082             cfg['aria-valuemin'] = this.aria_valuemin;
20083         }
20084         
20085         if(this.aria_valuemax){
20086             cfg['aria-valuemax'] = this.aria_valuemax;
20087         }
20088         
20089         if(this.label && !this.sr_only){
20090             cfg.html = this.label;
20091         }
20092         
20093         if(this.panel){
20094             cfg.cls += ' progress-bar-' + this.panel;
20095         }
20096         
20097         return cfg;
20098     },
20099     
20100     update : function(aria_valuenow)
20101     {
20102         this.aria_valuenow = aria_valuenow;
20103         
20104         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20105     }
20106    
20107 });
20108
20109  
20110
20111  /*
20112  * - LGPL
20113  *
20114  * column
20115  * 
20116  */
20117
20118 /**
20119  * @class Roo.bootstrap.TabGroup
20120  * @extends Roo.bootstrap.Column
20121  * Bootstrap Column class
20122  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20123  * @cfg {Boolean} carousel true to make the group behave like a carousel
20124  * @cfg {Boolean} bullets show bullets for the panels
20125  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20126  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20127  * @cfg {Boolean} showarrow (true|false) show arrow default true
20128  * 
20129  * @constructor
20130  * Create a new TabGroup
20131  * @param {Object} config The config object
20132  */
20133
20134 Roo.bootstrap.TabGroup = function(config){
20135     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20136     if (!this.navId) {
20137         this.navId = Roo.id();
20138     }
20139     this.tabs = [];
20140     Roo.bootstrap.TabGroup.register(this);
20141     
20142 };
20143
20144 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20145     
20146     carousel : false,
20147     transition : false,
20148     bullets : 0,
20149     timer : 0,
20150     autoslide : false,
20151     slideFn : false,
20152     slideOnTouch : false,
20153     showarrow : true,
20154     
20155     getAutoCreate : function()
20156     {
20157         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20158         
20159         cfg.cls += ' tab-content';
20160         
20161         if (this.carousel) {
20162             cfg.cls += ' carousel slide';
20163             
20164             cfg.cn = [{
20165                cls : 'carousel-inner',
20166                cn : []
20167             }];
20168         
20169             if(this.bullets  && !Roo.isTouch){
20170                 
20171                 var bullets = {
20172                     cls : 'carousel-bullets',
20173                     cn : []
20174                 };
20175                
20176                 if(this.bullets_cls){
20177                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20178                 }
20179                 
20180                 bullets.cn.push({
20181                     cls : 'clear'
20182                 });
20183                 
20184                 cfg.cn[0].cn.push(bullets);
20185             }
20186             
20187             if(this.showarrow){
20188                 cfg.cn[0].cn.push({
20189                     tag : 'div',
20190                     class : 'carousel-arrow',
20191                     cn : [
20192                         {
20193                             tag : 'div',
20194                             class : 'carousel-prev',
20195                             cn : [
20196                                 {
20197                                     tag : 'i',
20198                                     class : 'fa fa-chevron-left'
20199                                 }
20200                             ]
20201                         },
20202                         {
20203                             tag : 'div',
20204                             class : 'carousel-next',
20205                             cn : [
20206                                 {
20207                                     tag : 'i',
20208                                     class : 'fa fa-chevron-right'
20209                                 }
20210                             ]
20211                         }
20212                     ]
20213                 });
20214             }
20215             
20216         }
20217         
20218         return cfg;
20219     },
20220     
20221     initEvents:  function()
20222     {
20223 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20224 //            this.el.on("touchstart", this.onTouchStart, this);
20225 //        }
20226         
20227         if(this.autoslide){
20228             var _this = this;
20229             
20230             this.slideFn = window.setInterval(function() {
20231                 _this.showPanelNext();
20232             }, this.timer);
20233         }
20234         
20235         if(this.showarrow){
20236             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20237             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20238         }
20239         
20240         
20241     },
20242     
20243 //    onTouchStart : function(e, el, o)
20244 //    {
20245 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20246 //            return;
20247 //        }
20248 //        
20249 //        this.showPanelNext();
20250 //    },
20251     
20252     
20253     getChildContainer : function()
20254     {
20255         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20256     },
20257     
20258     /**
20259     * register a Navigation item
20260     * @param {Roo.bootstrap.NavItem} the navitem to add
20261     */
20262     register : function(item)
20263     {
20264         this.tabs.push( item);
20265         item.navId = this.navId; // not really needed..
20266         this.addBullet();
20267     
20268     },
20269     
20270     getActivePanel : function()
20271     {
20272         var r = false;
20273         Roo.each(this.tabs, function(t) {
20274             if (t.active) {
20275                 r = t;
20276                 return false;
20277             }
20278             return null;
20279         });
20280         return r;
20281         
20282     },
20283     getPanelByName : function(n)
20284     {
20285         var r = false;
20286         Roo.each(this.tabs, function(t) {
20287             if (t.tabId == n) {
20288                 r = t;
20289                 return false;
20290             }
20291             return null;
20292         });
20293         return r;
20294     },
20295     indexOfPanel : function(p)
20296     {
20297         var r = false;
20298         Roo.each(this.tabs, function(t,i) {
20299             if (t.tabId == p.tabId) {
20300                 r = i;
20301                 return false;
20302             }
20303             return null;
20304         });
20305         return r;
20306     },
20307     /**
20308      * show a specific panel
20309      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20310      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20311      */
20312     showPanel : function (pan)
20313     {
20314         if(this.transition || typeof(pan) == 'undefined'){
20315             Roo.log("waiting for the transitionend");
20316             return false;
20317         }
20318         
20319         if (typeof(pan) == 'number') {
20320             pan = this.tabs[pan];
20321         }
20322         
20323         if (typeof(pan) == 'string') {
20324             pan = this.getPanelByName(pan);
20325         }
20326         
20327         var cur = this.getActivePanel();
20328         
20329         if(!pan || !cur){
20330             Roo.log('pan or acitve pan is undefined');
20331             return false;
20332         }
20333         
20334         if (pan.tabId == this.getActivePanel().tabId) {
20335             return true;
20336         }
20337         
20338         if (false === cur.fireEvent('beforedeactivate')) {
20339             return false;
20340         }
20341         
20342         if(this.bullets > 0 && !Roo.isTouch){
20343             this.setActiveBullet(this.indexOfPanel(pan));
20344         }
20345         
20346         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20347             
20348             //class="carousel-item carousel-item-next carousel-item-left"
20349             
20350             this.transition = true;
20351             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20352             var lr = dir == 'next' ? 'left' : 'right';
20353             pan.el.addClass(dir); // or prev
20354             pan.el.addClass('carousel-item-' + dir); // or prev
20355             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20356             cur.el.addClass(lr); // or right
20357             pan.el.addClass(lr);
20358             cur.el.addClass('carousel-item-' +lr); // or right
20359             pan.el.addClass('carousel-item-' +lr);
20360             
20361             
20362             var _this = this;
20363             cur.el.on('transitionend', function() {
20364                 Roo.log("trans end?");
20365                 
20366                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20367                 pan.setActive(true);
20368                 
20369                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20370                 cur.setActive(false);
20371                 
20372                 _this.transition = false;
20373                 
20374             }, this, { single:  true } );
20375             
20376             return true;
20377         }
20378         
20379         cur.setActive(false);
20380         pan.setActive(true);
20381         
20382         return true;
20383         
20384     },
20385     showPanelNext : function()
20386     {
20387         var i = this.indexOfPanel(this.getActivePanel());
20388         
20389         if (i >= this.tabs.length - 1 && !this.autoslide) {
20390             return;
20391         }
20392         
20393         if (i >= this.tabs.length - 1 && this.autoslide) {
20394             i = -1;
20395         }
20396         
20397         this.showPanel(this.tabs[i+1]);
20398     },
20399     
20400     showPanelPrev : function()
20401     {
20402         var i = this.indexOfPanel(this.getActivePanel());
20403         
20404         if (i  < 1 && !this.autoslide) {
20405             return;
20406         }
20407         
20408         if (i < 1 && this.autoslide) {
20409             i = this.tabs.length;
20410         }
20411         
20412         this.showPanel(this.tabs[i-1]);
20413     },
20414     
20415     
20416     addBullet: function()
20417     {
20418         if(!this.bullets || Roo.isTouch){
20419             return;
20420         }
20421         var ctr = this.el.select('.carousel-bullets',true).first();
20422         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20423         var bullet = ctr.createChild({
20424             cls : 'bullet bullet-' + i
20425         },ctr.dom.lastChild);
20426         
20427         
20428         var _this = this;
20429         
20430         bullet.on('click', (function(e, el, o, ii, t){
20431
20432             e.preventDefault();
20433
20434             this.showPanel(ii);
20435
20436             if(this.autoslide && this.slideFn){
20437                 clearInterval(this.slideFn);
20438                 this.slideFn = window.setInterval(function() {
20439                     _this.showPanelNext();
20440                 }, this.timer);
20441             }
20442
20443         }).createDelegate(this, [i, bullet], true));
20444                 
20445         
20446     },
20447      
20448     setActiveBullet : function(i)
20449     {
20450         if(Roo.isTouch){
20451             return;
20452         }
20453         
20454         Roo.each(this.el.select('.bullet', true).elements, function(el){
20455             el.removeClass('selected');
20456         });
20457
20458         var bullet = this.el.select('.bullet-' + i, true).first();
20459         
20460         if(!bullet){
20461             return;
20462         }
20463         
20464         bullet.addClass('selected');
20465     }
20466     
20467     
20468   
20469 });
20470
20471  
20472
20473  
20474  
20475 Roo.apply(Roo.bootstrap.TabGroup, {
20476     
20477     groups: {},
20478      /**
20479     * register a Navigation Group
20480     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20481     */
20482     register : function(navgrp)
20483     {
20484         this.groups[navgrp.navId] = navgrp;
20485         
20486     },
20487     /**
20488     * fetch a Navigation Group based on the navigation ID
20489     * if one does not exist , it will get created.
20490     * @param {string} the navgroup to add
20491     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20492     */
20493     get: function(navId) {
20494         if (typeof(this.groups[navId]) == 'undefined') {
20495             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20496         }
20497         return this.groups[navId] ;
20498     }
20499     
20500     
20501     
20502 });
20503
20504  /*
20505  * - LGPL
20506  *
20507  * TabPanel
20508  * 
20509  */
20510
20511 /**
20512  * @class Roo.bootstrap.TabPanel
20513  * @extends Roo.bootstrap.Component
20514  * Bootstrap TabPanel class
20515  * @cfg {Boolean} active panel active
20516  * @cfg {String} html panel content
20517  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20518  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20519  * @cfg {String} href click to link..
20520  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20521  * 
20522  * 
20523  * @constructor
20524  * Create a new TabPanel
20525  * @param {Object} config The config object
20526  */
20527
20528 Roo.bootstrap.TabPanel = function(config){
20529     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20530     this.addEvents({
20531         /**
20532              * @event changed
20533              * Fires when the active status changes
20534              * @param {Roo.bootstrap.TabPanel} this
20535              * @param {Boolean} state the new state
20536             
20537          */
20538         'changed': true,
20539         /**
20540              * @event beforedeactivate
20541              * Fires before a tab is de-activated - can be used to do validation on a form.
20542              * @param {Roo.bootstrap.TabPanel} this
20543              * @return {Boolean} false if there is an error
20544             
20545          */
20546         'beforedeactivate': true
20547      });
20548     
20549     this.tabId = this.tabId || Roo.id();
20550   
20551 };
20552
20553 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20554     
20555     active: false,
20556     html: false,
20557     tabId: false,
20558     navId : false,
20559     href : '',
20560     touchSlide : false,
20561     getAutoCreate : function(){
20562         
20563         
20564         var cfg = {
20565             tag: 'div',
20566             // item is needed for carousel - not sure if it has any effect otherwise
20567             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20568             html: this.html || ''
20569         };
20570         
20571         if(this.active){
20572             cfg.cls += ' active';
20573         }
20574         
20575         if(this.tabId){
20576             cfg.tabId = this.tabId;
20577         }
20578         
20579         
20580         
20581         return cfg;
20582     },
20583     
20584     initEvents:  function()
20585     {
20586         var p = this.parent();
20587         
20588         this.navId = this.navId || p.navId;
20589         
20590         if (typeof(this.navId) != 'undefined') {
20591             // not really needed.. but just in case.. parent should be a NavGroup.
20592             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20593             
20594             tg.register(this);
20595             
20596             var i = tg.tabs.length - 1;
20597             
20598             if(this.active && tg.bullets > 0 && i < tg.bullets){
20599                 tg.setActiveBullet(i);
20600             }
20601         }
20602         
20603         this.el.on('click', this.onClick, this);
20604         
20605         if(Roo.isTouch && this.touchSlide){
20606             this.el.on("touchstart", this.onTouchStart, this);
20607             this.el.on("touchmove", this.onTouchMove, this);
20608             this.el.on("touchend", this.onTouchEnd, this);
20609         }
20610         
20611     },
20612     
20613     onRender : function(ct, position)
20614     {
20615         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20616     },
20617     
20618     setActive : function(state)
20619     {
20620         Roo.log("panel - set active " + this.tabId + "=" + state);
20621         
20622         this.active = state;
20623         if (!state) {
20624             this.el.removeClass('active');
20625             
20626         } else  if (!this.el.hasClass('active')) {
20627             this.el.addClass('active');
20628         }
20629         
20630         this.fireEvent('changed', this, state);
20631     },
20632     
20633     onClick : function(e)
20634     {
20635         e.preventDefault();
20636         
20637         if(!this.href.length){
20638             return;
20639         }
20640         
20641         window.location.href = this.href;
20642     },
20643     
20644     startX : 0,
20645     startY : 0,
20646     endX : 0,
20647     endY : 0,
20648     swiping : false,
20649     
20650     onTouchStart : function(e)
20651     {
20652         this.swiping = false;
20653         
20654         this.startX = e.browserEvent.touches[0].clientX;
20655         this.startY = e.browserEvent.touches[0].clientY;
20656     },
20657     
20658     onTouchMove : function(e)
20659     {
20660         this.swiping = true;
20661         
20662         this.endX = e.browserEvent.touches[0].clientX;
20663         this.endY = e.browserEvent.touches[0].clientY;
20664     },
20665     
20666     onTouchEnd : function(e)
20667     {
20668         if(!this.swiping){
20669             this.onClick(e);
20670             return;
20671         }
20672         
20673         var tabGroup = this.parent();
20674         
20675         if(this.endX > this.startX){ // swiping right
20676             tabGroup.showPanelPrev();
20677             return;
20678         }
20679         
20680         if(this.startX > this.endX){ // swiping left
20681             tabGroup.showPanelNext();
20682             return;
20683         }
20684     }
20685     
20686     
20687 });
20688  
20689
20690  
20691
20692  /*
20693  * - LGPL
20694  *
20695  * DateField
20696  * 
20697  */
20698
20699 /**
20700  * @class Roo.bootstrap.DateField
20701  * @extends Roo.bootstrap.Input
20702  * Bootstrap DateField class
20703  * @cfg {Number} weekStart default 0
20704  * @cfg {String} viewMode default empty, (months|years)
20705  * @cfg {String} minViewMode default empty, (months|years)
20706  * @cfg {Number} startDate default -Infinity
20707  * @cfg {Number} endDate default Infinity
20708  * @cfg {Boolean} todayHighlight default false
20709  * @cfg {Boolean} todayBtn default false
20710  * @cfg {Boolean} calendarWeeks default false
20711  * @cfg {Object} daysOfWeekDisabled default empty
20712  * @cfg {Boolean} singleMode default false (true | false)
20713  * 
20714  * @cfg {Boolean} keyboardNavigation default true
20715  * @cfg {String} language default en
20716  * 
20717  * @constructor
20718  * Create a new DateField
20719  * @param {Object} config The config object
20720  */
20721
20722 Roo.bootstrap.DateField = function(config){
20723     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20724      this.addEvents({
20725             /**
20726              * @event show
20727              * Fires when this field show.
20728              * @param {Roo.bootstrap.DateField} this
20729              * @param {Mixed} date The date value
20730              */
20731             show : true,
20732             /**
20733              * @event show
20734              * Fires when this field hide.
20735              * @param {Roo.bootstrap.DateField} this
20736              * @param {Mixed} date The date value
20737              */
20738             hide : true,
20739             /**
20740              * @event select
20741              * Fires when select a date.
20742              * @param {Roo.bootstrap.DateField} this
20743              * @param {Mixed} date The date value
20744              */
20745             select : true,
20746             /**
20747              * @event beforeselect
20748              * Fires when before select a date.
20749              * @param {Roo.bootstrap.DateField} this
20750              * @param {Mixed} date The date value
20751              */
20752             beforeselect : true
20753         });
20754 };
20755
20756 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20757     
20758     /**
20759      * @cfg {String} format
20760      * The default date format string which can be overriden for localization support.  The format must be
20761      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20762      */
20763     format : "m/d/y",
20764     /**
20765      * @cfg {String} altFormats
20766      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20767      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20768      */
20769     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20770     
20771     weekStart : 0,
20772     
20773     viewMode : '',
20774     
20775     minViewMode : '',
20776     
20777     todayHighlight : false,
20778     
20779     todayBtn: false,
20780     
20781     language: 'en',
20782     
20783     keyboardNavigation: true,
20784     
20785     calendarWeeks: false,
20786     
20787     startDate: -Infinity,
20788     
20789     endDate: Infinity,
20790     
20791     daysOfWeekDisabled: [],
20792     
20793     _events: [],
20794     
20795     singleMode : false,
20796     
20797     UTCDate: function()
20798     {
20799         return new Date(Date.UTC.apply(Date, arguments));
20800     },
20801     
20802     UTCToday: function()
20803     {
20804         var today = new Date();
20805         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20806     },
20807     
20808     getDate: function() {
20809             var d = this.getUTCDate();
20810             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20811     },
20812     
20813     getUTCDate: function() {
20814             return this.date;
20815     },
20816     
20817     setDate: function(d) {
20818             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20819     },
20820     
20821     setUTCDate: function(d) {
20822             this.date = d;
20823             this.setValue(this.formatDate(this.date));
20824     },
20825         
20826     onRender: function(ct, position)
20827     {
20828         
20829         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20830         
20831         this.language = this.language || 'en';
20832         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20833         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20834         
20835         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20836         this.format = this.format || 'm/d/y';
20837         this.isInline = false;
20838         this.isInput = true;
20839         this.component = this.el.select('.add-on', true).first() || false;
20840         this.component = (this.component && this.component.length === 0) ? false : this.component;
20841         this.hasInput = this.component && this.inputEl().length;
20842         
20843         if (typeof(this.minViewMode === 'string')) {
20844             switch (this.minViewMode) {
20845                 case 'months':
20846                     this.minViewMode = 1;
20847                     break;
20848                 case 'years':
20849                     this.minViewMode = 2;
20850                     break;
20851                 default:
20852                     this.minViewMode = 0;
20853                     break;
20854             }
20855         }
20856         
20857         if (typeof(this.viewMode === 'string')) {
20858             switch (this.viewMode) {
20859                 case 'months':
20860                     this.viewMode = 1;
20861                     break;
20862                 case 'years':
20863                     this.viewMode = 2;
20864                     break;
20865                 default:
20866                     this.viewMode = 0;
20867                     break;
20868             }
20869         }
20870                 
20871         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20872         
20873 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20874         
20875         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20876         
20877         this.picker().on('mousedown', this.onMousedown, this);
20878         this.picker().on('click', this.onClick, this);
20879         
20880         this.picker().addClass('datepicker-dropdown');
20881         
20882         this.startViewMode = this.viewMode;
20883         
20884         if(this.singleMode){
20885             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20886                 v.setVisibilityMode(Roo.Element.DISPLAY);
20887                 v.hide();
20888             });
20889             
20890             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20891                 v.setStyle('width', '189px');
20892             });
20893         }
20894         
20895         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20896             if(!this.calendarWeeks){
20897                 v.remove();
20898                 return;
20899             }
20900             
20901             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20902             v.attr('colspan', function(i, val){
20903                 return parseInt(val) + 1;
20904             });
20905         });
20906                         
20907         
20908         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20909         
20910         this.setStartDate(this.startDate);
20911         this.setEndDate(this.endDate);
20912         
20913         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20914         
20915         this.fillDow();
20916         this.fillMonths();
20917         this.update();
20918         this.showMode();
20919         
20920         if(this.isInline) {
20921             this.showPopup();
20922         }
20923     },
20924     
20925     picker : function()
20926     {
20927         return this.pickerEl;
20928 //        return this.el.select('.datepicker', true).first();
20929     },
20930     
20931     fillDow: function()
20932     {
20933         var dowCnt = this.weekStart;
20934         
20935         var dow = {
20936             tag: 'tr',
20937             cn: [
20938                 
20939             ]
20940         };
20941         
20942         if(this.calendarWeeks){
20943             dow.cn.push({
20944                 tag: 'th',
20945                 cls: 'cw',
20946                 html: '&nbsp;'
20947             })
20948         }
20949         
20950         while (dowCnt < this.weekStart + 7) {
20951             dow.cn.push({
20952                 tag: 'th',
20953                 cls: 'dow',
20954                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20955             });
20956         }
20957         
20958         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20959     },
20960     
20961     fillMonths: function()
20962     {    
20963         var i = 0;
20964         var months = this.picker().select('>.datepicker-months td', true).first();
20965         
20966         months.dom.innerHTML = '';
20967         
20968         while (i < 12) {
20969             var month = {
20970                 tag: 'span',
20971                 cls: 'month',
20972                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20973             };
20974             
20975             months.createChild(month);
20976         }
20977         
20978     },
20979     
20980     update: function()
20981     {
20982         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;
20983         
20984         if (this.date < this.startDate) {
20985             this.viewDate = new Date(this.startDate);
20986         } else if (this.date > this.endDate) {
20987             this.viewDate = new Date(this.endDate);
20988         } else {
20989             this.viewDate = new Date(this.date);
20990         }
20991         
20992         this.fill();
20993     },
20994     
20995     fill: function() 
20996     {
20997         var d = new Date(this.viewDate),
20998                 year = d.getUTCFullYear(),
20999                 month = d.getUTCMonth(),
21000                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21001                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21002                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21003                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21004                 currentDate = this.date && this.date.valueOf(),
21005                 today = this.UTCToday();
21006         
21007         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21008         
21009 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21010         
21011 //        this.picker.select('>tfoot th.today').
21012 //                                              .text(dates[this.language].today)
21013 //                                              .toggle(this.todayBtn !== false);
21014     
21015         this.updateNavArrows();
21016         this.fillMonths();
21017                                                 
21018         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21019         
21020         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21021          
21022         prevMonth.setUTCDate(day);
21023         
21024         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21025         
21026         var nextMonth = new Date(prevMonth);
21027         
21028         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21029         
21030         nextMonth = nextMonth.valueOf();
21031         
21032         var fillMonths = false;
21033         
21034         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21035         
21036         while(prevMonth.valueOf() <= nextMonth) {
21037             var clsName = '';
21038             
21039             if (prevMonth.getUTCDay() === this.weekStart) {
21040                 if(fillMonths){
21041                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21042                 }
21043                     
21044                 fillMonths = {
21045                     tag: 'tr',
21046                     cn: []
21047                 };
21048                 
21049                 if(this.calendarWeeks){
21050                     // ISO 8601: First week contains first thursday.
21051                     // ISO also states week starts on Monday, but we can be more abstract here.
21052                     var
21053                     // Start of current week: based on weekstart/current date
21054                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21055                     // Thursday of this week
21056                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21057                     // First Thursday of year, year from thursday
21058                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21059                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21060                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21061                     
21062                     fillMonths.cn.push({
21063                         tag: 'td',
21064                         cls: 'cw',
21065                         html: calWeek
21066                     });
21067                 }
21068             }
21069             
21070             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21071                 clsName += ' old';
21072             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21073                 clsName += ' new';
21074             }
21075             if (this.todayHighlight &&
21076                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21077                 prevMonth.getUTCMonth() == today.getMonth() &&
21078                 prevMonth.getUTCDate() == today.getDate()) {
21079                 clsName += ' today';
21080             }
21081             
21082             if (currentDate && prevMonth.valueOf() === currentDate) {
21083                 clsName += ' active';
21084             }
21085             
21086             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21087                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21088                     clsName += ' disabled';
21089             }
21090             
21091             fillMonths.cn.push({
21092                 tag: 'td',
21093                 cls: 'day ' + clsName,
21094                 html: prevMonth.getDate()
21095             });
21096             
21097             prevMonth.setDate(prevMonth.getDate()+1);
21098         }
21099           
21100         var currentYear = this.date && this.date.getUTCFullYear();
21101         var currentMonth = this.date && this.date.getUTCMonth();
21102         
21103         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21104         
21105         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21106             v.removeClass('active');
21107             
21108             if(currentYear === year && k === currentMonth){
21109                 v.addClass('active');
21110             }
21111             
21112             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21113                 v.addClass('disabled');
21114             }
21115             
21116         });
21117         
21118         
21119         year = parseInt(year/10, 10) * 10;
21120         
21121         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21122         
21123         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21124         
21125         year -= 1;
21126         for (var i = -1; i < 11; i++) {
21127             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21128                 tag: 'span',
21129                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21130                 html: year
21131             });
21132             
21133             year += 1;
21134         }
21135     },
21136     
21137     showMode: function(dir) 
21138     {
21139         if (dir) {
21140             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21141         }
21142         
21143         Roo.each(this.picker().select('>div',true).elements, function(v){
21144             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21145             v.hide();
21146         });
21147         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21148     },
21149     
21150     place: function()
21151     {
21152         if(this.isInline) {
21153             return;
21154         }
21155         
21156         this.picker().removeClass(['bottom', 'top']);
21157         
21158         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21159             /*
21160              * place to the top of element!
21161              *
21162              */
21163             
21164             this.picker().addClass('top');
21165             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21166             
21167             return;
21168         }
21169         
21170         this.picker().addClass('bottom');
21171         
21172         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21173     },
21174     
21175     parseDate : function(value)
21176     {
21177         if(!value || value instanceof Date){
21178             return value;
21179         }
21180         var v = Date.parseDate(value, this.format);
21181         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21182             v = Date.parseDate(value, 'Y-m-d');
21183         }
21184         if(!v && this.altFormats){
21185             if(!this.altFormatsArray){
21186                 this.altFormatsArray = this.altFormats.split("|");
21187             }
21188             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21189                 v = Date.parseDate(value, this.altFormatsArray[i]);
21190             }
21191         }
21192         return v;
21193     },
21194     
21195     formatDate : function(date, fmt)
21196     {   
21197         return (!date || !(date instanceof Date)) ?
21198         date : date.dateFormat(fmt || this.format);
21199     },
21200     
21201     onFocus : function()
21202     {
21203         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21204         this.showPopup();
21205     },
21206     
21207     onBlur : function()
21208     {
21209         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21210         
21211         var d = this.inputEl().getValue();
21212         
21213         this.setValue(d);
21214                 
21215         this.hidePopup();
21216     },
21217     
21218     showPopup : function()
21219     {
21220         this.picker().show();
21221         this.update();
21222         this.place();
21223         
21224         this.fireEvent('showpopup', this, this.date);
21225     },
21226     
21227     hidePopup : function()
21228     {
21229         if(this.isInline) {
21230             return;
21231         }
21232         this.picker().hide();
21233         this.viewMode = this.startViewMode;
21234         this.showMode();
21235         
21236         this.fireEvent('hidepopup', this, this.date);
21237         
21238     },
21239     
21240     onMousedown: function(e)
21241     {
21242         e.stopPropagation();
21243         e.preventDefault();
21244     },
21245     
21246     keyup: function(e)
21247     {
21248         Roo.bootstrap.DateField.superclass.keyup.call(this);
21249         this.update();
21250     },
21251
21252     setValue: function(v)
21253     {
21254         if(this.fireEvent('beforeselect', this, v) !== false){
21255             var d = new Date(this.parseDate(v) ).clearTime();
21256         
21257             if(isNaN(d.getTime())){
21258                 this.date = this.viewDate = '';
21259                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21260                 return;
21261             }
21262
21263             v = this.formatDate(d);
21264
21265             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21266
21267             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21268
21269             this.update();
21270
21271             this.fireEvent('select', this, this.date);
21272         }
21273     },
21274     
21275     getValue: function()
21276     {
21277         return this.formatDate(this.date);
21278     },
21279     
21280     fireKey: function(e)
21281     {
21282         if (!this.picker().isVisible()){
21283             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21284                 this.showPopup();
21285             }
21286             return;
21287         }
21288         
21289         var dateChanged = false,
21290         dir, day, month,
21291         newDate, newViewDate;
21292         
21293         switch(e.keyCode){
21294             case 27: // escape
21295                 this.hidePopup();
21296                 e.preventDefault();
21297                 break;
21298             case 37: // left
21299             case 39: // right
21300                 if (!this.keyboardNavigation) {
21301                     break;
21302                 }
21303                 dir = e.keyCode == 37 ? -1 : 1;
21304                 
21305                 if (e.ctrlKey){
21306                     newDate = this.moveYear(this.date, dir);
21307                     newViewDate = this.moveYear(this.viewDate, dir);
21308                 } else if (e.shiftKey){
21309                     newDate = this.moveMonth(this.date, dir);
21310                     newViewDate = this.moveMonth(this.viewDate, dir);
21311                 } else {
21312                     newDate = new Date(this.date);
21313                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21314                     newViewDate = new Date(this.viewDate);
21315                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21316                 }
21317                 if (this.dateWithinRange(newDate)){
21318                     this.date = newDate;
21319                     this.viewDate = newViewDate;
21320                     this.setValue(this.formatDate(this.date));
21321 //                    this.update();
21322                     e.preventDefault();
21323                     dateChanged = true;
21324                 }
21325                 break;
21326             case 38: // up
21327             case 40: // down
21328                 if (!this.keyboardNavigation) {
21329                     break;
21330                 }
21331                 dir = e.keyCode == 38 ? -1 : 1;
21332                 if (e.ctrlKey){
21333                     newDate = this.moveYear(this.date, dir);
21334                     newViewDate = this.moveYear(this.viewDate, dir);
21335                 } else if (e.shiftKey){
21336                     newDate = this.moveMonth(this.date, dir);
21337                     newViewDate = this.moveMonth(this.viewDate, dir);
21338                 } else {
21339                     newDate = new Date(this.date);
21340                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21341                     newViewDate = new Date(this.viewDate);
21342                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21343                 }
21344                 if (this.dateWithinRange(newDate)){
21345                     this.date = newDate;
21346                     this.viewDate = newViewDate;
21347                     this.setValue(this.formatDate(this.date));
21348 //                    this.update();
21349                     e.preventDefault();
21350                     dateChanged = true;
21351                 }
21352                 break;
21353             case 13: // enter
21354                 this.setValue(this.formatDate(this.date));
21355                 this.hidePopup();
21356                 e.preventDefault();
21357                 break;
21358             case 9: // tab
21359                 this.setValue(this.formatDate(this.date));
21360                 this.hidePopup();
21361                 break;
21362             case 16: // shift
21363             case 17: // ctrl
21364             case 18: // alt
21365                 break;
21366             default :
21367                 this.hidePopup();
21368                 
21369         }
21370     },
21371     
21372     
21373     onClick: function(e) 
21374     {
21375         e.stopPropagation();
21376         e.preventDefault();
21377         
21378         var target = e.getTarget();
21379         
21380         if(target.nodeName.toLowerCase() === 'i'){
21381             target = Roo.get(target).dom.parentNode;
21382         }
21383         
21384         var nodeName = target.nodeName;
21385         var className = target.className;
21386         var html = target.innerHTML;
21387         //Roo.log(nodeName);
21388         
21389         switch(nodeName.toLowerCase()) {
21390             case 'th':
21391                 switch(className) {
21392                     case 'switch':
21393                         this.showMode(1);
21394                         break;
21395                     case 'prev':
21396                     case 'next':
21397                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21398                         switch(this.viewMode){
21399                                 case 0:
21400                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21401                                         break;
21402                                 case 1:
21403                                 case 2:
21404                                         this.viewDate = this.moveYear(this.viewDate, dir);
21405                                         break;
21406                         }
21407                         this.fill();
21408                         break;
21409                     case 'today':
21410                         var date = new Date();
21411                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21412 //                        this.fill()
21413                         this.setValue(this.formatDate(this.date));
21414                         
21415                         this.hidePopup();
21416                         break;
21417                 }
21418                 break;
21419             case 'span':
21420                 if (className.indexOf('disabled') < 0) {
21421                     this.viewDate.setUTCDate(1);
21422                     if (className.indexOf('month') > -1) {
21423                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21424                     } else {
21425                         var year = parseInt(html, 10) || 0;
21426                         this.viewDate.setUTCFullYear(year);
21427                         
21428                     }
21429                     
21430                     if(this.singleMode){
21431                         this.setValue(this.formatDate(this.viewDate));
21432                         this.hidePopup();
21433                         return;
21434                     }
21435                     
21436                     this.showMode(-1);
21437                     this.fill();
21438                 }
21439                 break;
21440                 
21441             case 'td':
21442                 //Roo.log(className);
21443                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21444                     var day = parseInt(html, 10) || 1;
21445                     var year = this.viewDate.getUTCFullYear(),
21446                         month = this.viewDate.getUTCMonth();
21447
21448                     if (className.indexOf('old') > -1) {
21449                         if(month === 0 ){
21450                             month = 11;
21451                             year -= 1;
21452                         }else{
21453                             month -= 1;
21454                         }
21455                     } else if (className.indexOf('new') > -1) {
21456                         if (month == 11) {
21457                             month = 0;
21458                             year += 1;
21459                         } else {
21460                             month += 1;
21461                         }
21462                     }
21463                     //Roo.log([year,month,day]);
21464                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21465                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21466 //                    this.fill();
21467                     //Roo.log(this.formatDate(this.date));
21468                     this.setValue(this.formatDate(this.date));
21469                     this.hidePopup();
21470                 }
21471                 break;
21472         }
21473     },
21474     
21475     setStartDate: function(startDate)
21476     {
21477         this.startDate = startDate || -Infinity;
21478         if (this.startDate !== -Infinity) {
21479             this.startDate = this.parseDate(this.startDate);
21480         }
21481         this.update();
21482         this.updateNavArrows();
21483     },
21484
21485     setEndDate: function(endDate)
21486     {
21487         this.endDate = endDate || Infinity;
21488         if (this.endDate !== Infinity) {
21489             this.endDate = this.parseDate(this.endDate);
21490         }
21491         this.update();
21492         this.updateNavArrows();
21493     },
21494     
21495     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21496     {
21497         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21498         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21499             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21500         }
21501         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21502             return parseInt(d, 10);
21503         });
21504         this.update();
21505         this.updateNavArrows();
21506     },
21507     
21508     updateNavArrows: function() 
21509     {
21510         if(this.singleMode){
21511             return;
21512         }
21513         
21514         var d = new Date(this.viewDate),
21515         year = d.getUTCFullYear(),
21516         month = d.getUTCMonth();
21517         
21518         Roo.each(this.picker().select('.prev', true).elements, function(v){
21519             v.show();
21520             switch (this.viewMode) {
21521                 case 0:
21522
21523                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21524                         v.hide();
21525                     }
21526                     break;
21527                 case 1:
21528                 case 2:
21529                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21530                         v.hide();
21531                     }
21532                     break;
21533             }
21534         });
21535         
21536         Roo.each(this.picker().select('.next', true).elements, function(v){
21537             v.show();
21538             switch (this.viewMode) {
21539                 case 0:
21540
21541                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21542                         v.hide();
21543                     }
21544                     break;
21545                 case 1:
21546                 case 2:
21547                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21548                         v.hide();
21549                     }
21550                     break;
21551             }
21552         })
21553     },
21554     
21555     moveMonth: function(date, dir)
21556     {
21557         if (!dir) {
21558             return date;
21559         }
21560         var new_date = new Date(date.valueOf()),
21561         day = new_date.getUTCDate(),
21562         month = new_date.getUTCMonth(),
21563         mag = Math.abs(dir),
21564         new_month, test;
21565         dir = dir > 0 ? 1 : -1;
21566         if (mag == 1){
21567             test = dir == -1
21568             // If going back one month, make sure month is not current month
21569             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21570             ? function(){
21571                 return new_date.getUTCMonth() == month;
21572             }
21573             // If going forward one month, make sure month is as expected
21574             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21575             : function(){
21576                 return new_date.getUTCMonth() != new_month;
21577             };
21578             new_month = month + dir;
21579             new_date.setUTCMonth(new_month);
21580             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21581             if (new_month < 0 || new_month > 11) {
21582                 new_month = (new_month + 12) % 12;
21583             }
21584         } else {
21585             // For magnitudes >1, move one month at a time...
21586             for (var i=0; i<mag; i++) {
21587                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21588                 new_date = this.moveMonth(new_date, dir);
21589             }
21590             // ...then reset the day, keeping it in the new month
21591             new_month = new_date.getUTCMonth();
21592             new_date.setUTCDate(day);
21593             test = function(){
21594                 return new_month != new_date.getUTCMonth();
21595             };
21596         }
21597         // Common date-resetting loop -- if date is beyond end of month, make it
21598         // end of month
21599         while (test()){
21600             new_date.setUTCDate(--day);
21601             new_date.setUTCMonth(new_month);
21602         }
21603         return new_date;
21604     },
21605
21606     moveYear: function(date, dir)
21607     {
21608         return this.moveMonth(date, dir*12);
21609     },
21610
21611     dateWithinRange: function(date)
21612     {
21613         return date >= this.startDate && date <= this.endDate;
21614     },
21615
21616     
21617     remove: function() 
21618     {
21619         this.picker().remove();
21620     },
21621     
21622     validateValue : function(value)
21623     {
21624         if(this.getVisibilityEl().hasClass('hidden')){
21625             return true;
21626         }
21627         
21628         if(value.length < 1)  {
21629             if(this.allowBlank){
21630                 return true;
21631             }
21632             return false;
21633         }
21634         
21635         if(value.length < this.minLength){
21636             return false;
21637         }
21638         if(value.length > this.maxLength){
21639             return false;
21640         }
21641         if(this.vtype){
21642             var vt = Roo.form.VTypes;
21643             if(!vt[this.vtype](value, this)){
21644                 return false;
21645             }
21646         }
21647         if(typeof this.validator == "function"){
21648             var msg = this.validator(value);
21649             if(msg !== true){
21650                 return false;
21651             }
21652         }
21653         
21654         if(this.regex && !this.regex.test(value)){
21655             return false;
21656         }
21657         
21658         if(typeof(this.parseDate(value)) == 'undefined'){
21659             return false;
21660         }
21661         
21662         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21663             return false;
21664         }      
21665         
21666         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21667             return false;
21668         } 
21669         
21670         
21671         return true;
21672     },
21673     
21674     reset : function()
21675     {
21676         this.date = this.viewDate = '';
21677         
21678         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21679     }
21680    
21681 });
21682
21683 Roo.apply(Roo.bootstrap.DateField,  {
21684     
21685     head : {
21686         tag: 'thead',
21687         cn: [
21688         {
21689             tag: 'tr',
21690             cn: [
21691             {
21692                 tag: 'th',
21693                 cls: 'prev',
21694                 html: '<i class="fa fa-arrow-left"/>'
21695             },
21696             {
21697                 tag: 'th',
21698                 cls: 'switch',
21699                 colspan: '5'
21700             },
21701             {
21702                 tag: 'th',
21703                 cls: 'next',
21704                 html: '<i class="fa fa-arrow-right"/>'
21705             }
21706
21707             ]
21708         }
21709         ]
21710     },
21711     
21712     content : {
21713         tag: 'tbody',
21714         cn: [
21715         {
21716             tag: 'tr',
21717             cn: [
21718             {
21719                 tag: 'td',
21720                 colspan: '7'
21721             }
21722             ]
21723         }
21724         ]
21725     },
21726     
21727     footer : {
21728         tag: 'tfoot',
21729         cn: [
21730         {
21731             tag: 'tr',
21732             cn: [
21733             {
21734                 tag: 'th',
21735                 colspan: '7',
21736                 cls: 'today'
21737             }
21738                     
21739             ]
21740         }
21741         ]
21742     },
21743     
21744     dates:{
21745         en: {
21746             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21747             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21748             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21749             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21750             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21751             today: "Today"
21752         }
21753     },
21754     
21755     modes: [
21756     {
21757         clsName: 'days',
21758         navFnc: 'Month',
21759         navStep: 1
21760     },
21761     {
21762         clsName: 'months',
21763         navFnc: 'FullYear',
21764         navStep: 1
21765     },
21766     {
21767         clsName: 'years',
21768         navFnc: 'FullYear',
21769         navStep: 10
21770     }]
21771 });
21772
21773 Roo.apply(Roo.bootstrap.DateField,  {
21774   
21775     template : {
21776         tag: 'div',
21777         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21778         cn: [
21779         {
21780             tag: 'div',
21781             cls: 'datepicker-days',
21782             cn: [
21783             {
21784                 tag: 'table',
21785                 cls: 'table-condensed',
21786                 cn:[
21787                 Roo.bootstrap.DateField.head,
21788                 {
21789                     tag: 'tbody'
21790                 },
21791                 Roo.bootstrap.DateField.footer
21792                 ]
21793             }
21794             ]
21795         },
21796         {
21797             tag: 'div',
21798             cls: 'datepicker-months',
21799             cn: [
21800             {
21801                 tag: 'table',
21802                 cls: 'table-condensed',
21803                 cn:[
21804                 Roo.bootstrap.DateField.head,
21805                 Roo.bootstrap.DateField.content,
21806                 Roo.bootstrap.DateField.footer
21807                 ]
21808             }
21809             ]
21810         },
21811         {
21812             tag: 'div',
21813             cls: 'datepicker-years',
21814             cn: [
21815             {
21816                 tag: 'table',
21817                 cls: 'table-condensed',
21818                 cn:[
21819                 Roo.bootstrap.DateField.head,
21820                 Roo.bootstrap.DateField.content,
21821                 Roo.bootstrap.DateField.footer
21822                 ]
21823             }
21824             ]
21825         }
21826         ]
21827     }
21828 });
21829
21830  
21831
21832  /*
21833  * - LGPL
21834  *
21835  * TimeField
21836  * 
21837  */
21838
21839 /**
21840  * @class Roo.bootstrap.TimeField
21841  * @extends Roo.bootstrap.Input
21842  * Bootstrap DateField class
21843  * 
21844  * 
21845  * @constructor
21846  * Create a new TimeField
21847  * @param {Object} config The config object
21848  */
21849
21850 Roo.bootstrap.TimeField = function(config){
21851     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21852     this.addEvents({
21853             /**
21854              * @event show
21855              * Fires when this field show.
21856              * @param {Roo.bootstrap.DateField} thisthis
21857              * @param {Mixed} date The date value
21858              */
21859             show : true,
21860             /**
21861              * @event show
21862              * Fires when this field hide.
21863              * @param {Roo.bootstrap.DateField} this
21864              * @param {Mixed} date The date value
21865              */
21866             hide : true,
21867             /**
21868              * @event select
21869              * Fires when select a date.
21870              * @param {Roo.bootstrap.DateField} this
21871              * @param {Mixed} date The date value
21872              */
21873             select : true
21874         });
21875 };
21876
21877 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21878     
21879     /**
21880      * @cfg {String} format
21881      * The default time format string which can be overriden for localization support.  The format must be
21882      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21883      */
21884     format : "H:i",
21885        
21886     onRender: function(ct, position)
21887     {
21888         
21889         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21890                 
21891         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21892         
21893         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21894         
21895         this.pop = this.picker().select('>.datepicker-time',true).first();
21896         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21897         
21898         this.picker().on('mousedown', this.onMousedown, this);
21899         this.picker().on('click', this.onClick, this);
21900         
21901         this.picker().addClass('datepicker-dropdown');
21902     
21903         this.fillTime();
21904         this.update();
21905             
21906         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21907         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21908         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21909         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21910         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21911         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21912
21913     },
21914     
21915     fireKey: function(e){
21916         if (!this.picker().isVisible()){
21917             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21918                 this.show();
21919             }
21920             return;
21921         }
21922
21923         e.preventDefault();
21924         
21925         switch(e.keyCode){
21926             case 27: // escape
21927                 this.hide();
21928                 break;
21929             case 37: // left
21930             case 39: // right
21931                 this.onTogglePeriod();
21932                 break;
21933             case 38: // up
21934                 this.onIncrementMinutes();
21935                 break;
21936             case 40: // down
21937                 this.onDecrementMinutes();
21938                 break;
21939             case 13: // enter
21940             case 9: // tab
21941                 this.setTime();
21942                 break;
21943         }
21944     },
21945     
21946     onClick: function(e) {
21947         e.stopPropagation();
21948         e.preventDefault();
21949     },
21950     
21951     picker : function()
21952     {
21953         return this.el.select('.datepicker', true).first();
21954     },
21955     
21956     fillTime: function()
21957     {    
21958         var time = this.pop.select('tbody', true).first();
21959         
21960         time.dom.innerHTML = '';
21961         
21962         time.createChild({
21963             tag: 'tr',
21964             cn: [
21965                 {
21966                     tag: 'td',
21967                     cn: [
21968                         {
21969                             tag: 'a',
21970                             href: '#',
21971                             cls: 'btn',
21972                             cn: [
21973                                 {
21974                                     tag: 'span',
21975                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21976                                 }
21977                             ]
21978                         } 
21979                     ]
21980                 },
21981                 {
21982                     tag: 'td',
21983                     cls: 'separator'
21984                 },
21985                 {
21986                     tag: 'td',
21987                     cn: [
21988                         {
21989                             tag: 'a',
21990                             href: '#',
21991                             cls: 'btn',
21992                             cn: [
21993                                 {
21994                                     tag: 'span',
21995                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21996                                 }
21997                             ]
21998                         }
21999                     ]
22000                 },
22001                 {
22002                     tag: 'td',
22003                     cls: 'separator'
22004                 }
22005             ]
22006         });
22007         
22008         time.createChild({
22009             tag: 'tr',
22010             cn: [
22011                 {
22012                     tag: 'td',
22013                     cn: [
22014                         {
22015                             tag: 'span',
22016                             cls: 'timepicker-hour',
22017                             html: '00'
22018                         }  
22019                     ]
22020                 },
22021                 {
22022                     tag: 'td',
22023                     cls: 'separator',
22024                     html: ':'
22025                 },
22026                 {
22027                     tag: 'td',
22028                     cn: [
22029                         {
22030                             tag: 'span',
22031                             cls: 'timepicker-minute',
22032                             html: '00'
22033                         }  
22034                     ]
22035                 },
22036                 {
22037                     tag: 'td',
22038                     cls: 'separator'
22039                 },
22040                 {
22041                     tag: 'td',
22042                     cn: [
22043                         {
22044                             tag: 'button',
22045                             type: 'button',
22046                             cls: 'btn btn-primary period',
22047                             html: 'AM'
22048                             
22049                         }
22050                     ]
22051                 }
22052             ]
22053         });
22054         
22055         time.createChild({
22056             tag: 'tr',
22057             cn: [
22058                 {
22059                     tag: 'td',
22060                     cn: [
22061                         {
22062                             tag: 'a',
22063                             href: '#',
22064                             cls: 'btn',
22065                             cn: [
22066                                 {
22067                                     tag: 'span',
22068                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22069                                 }
22070                             ]
22071                         }
22072                     ]
22073                 },
22074                 {
22075                     tag: 'td',
22076                     cls: 'separator'
22077                 },
22078                 {
22079                     tag: 'td',
22080                     cn: [
22081                         {
22082                             tag: 'a',
22083                             href: '#',
22084                             cls: 'btn',
22085                             cn: [
22086                                 {
22087                                     tag: 'span',
22088                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22089                                 }
22090                             ]
22091                         }
22092                     ]
22093                 },
22094                 {
22095                     tag: 'td',
22096                     cls: 'separator'
22097                 }
22098             ]
22099         });
22100         
22101     },
22102     
22103     update: function()
22104     {
22105         
22106         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22107         
22108         this.fill();
22109     },
22110     
22111     fill: function() 
22112     {
22113         var hours = this.time.getHours();
22114         var minutes = this.time.getMinutes();
22115         var period = 'AM';
22116         
22117         if(hours > 11){
22118             period = 'PM';
22119         }
22120         
22121         if(hours == 0){
22122             hours = 12;
22123         }
22124         
22125         
22126         if(hours > 12){
22127             hours = hours - 12;
22128         }
22129         
22130         if(hours < 10){
22131             hours = '0' + hours;
22132         }
22133         
22134         if(minutes < 10){
22135             minutes = '0' + minutes;
22136         }
22137         
22138         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22139         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22140         this.pop.select('button', true).first().dom.innerHTML = period;
22141         
22142     },
22143     
22144     place: function()
22145     {   
22146         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22147         
22148         var cls = ['bottom'];
22149         
22150         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22151             cls.pop();
22152             cls.push('top');
22153         }
22154         
22155         cls.push('right');
22156         
22157         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22158             cls.pop();
22159             cls.push('left');
22160         }
22161         
22162         this.picker().addClass(cls.join('-'));
22163         
22164         var _this = this;
22165         
22166         Roo.each(cls, function(c){
22167             if(c == 'bottom'){
22168                 _this.picker().setTop(_this.inputEl().getHeight());
22169                 return;
22170             }
22171             if(c == 'top'){
22172                 _this.picker().setTop(0 - _this.picker().getHeight());
22173                 return;
22174             }
22175             
22176             if(c == 'left'){
22177                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22178                 return;
22179             }
22180             if(c == 'right'){
22181                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22182                 return;
22183             }
22184         });
22185         
22186     },
22187   
22188     onFocus : function()
22189     {
22190         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22191         this.show();
22192     },
22193     
22194     onBlur : function()
22195     {
22196         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22197         this.hide();
22198     },
22199     
22200     show : function()
22201     {
22202         this.picker().show();
22203         this.pop.show();
22204         this.update();
22205         this.place();
22206         
22207         this.fireEvent('show', this, this.date);
22208     },
22209     
22210     hide : function()
22211     {
22212         this.picker().hide();
22213         this.pop.hide();
22214         
22215         this.fireEvent('hide', this, this.date);
22216     },
22217     
22218     setTime : function()
22219     {
22220         this.hide();
22221         this.setValue(this.time.format(this.format));
22222         
22223         this.fireEvent('select', this, this.date);
22224         
22225         
22226     },
22227     
22228     onMousedown: function(e){
22229         e.stopPropagation();
22230         e.preventDefault();
22231     },
22232     
22233     onIncrementHours: function()
22234     {
22235         Roo.log('onIncrementHours');
22236         this.time = this.time.add(Date.HOUR, 1);
22237         this.update();
22238         
22239     },
22240     
22241     onDecrementHours: function()
22242     {
22243         Roo.log('onDecrementHours');
22244         this.time = this.time.add(Date.HOUR, -1);
22245         this.update();
22246     },
22247     
22248     onIncrementMinutes: function()
22249     {
22250         Roo.log('onIncrementMinutes');
22251         this.time = this.time.add(Date.MINUTE, 1);
22252         this.update();
22253     },
22254     
22255     onDecrementMinutes: function()
22256     {
22257         Roo.log('onDecrementMinutes');
22258         this.time = this.time.add(Date.MINUTE, -1);
22259         this.update();
22260     },
22261     
22262     onTogglePeriod: function()
22263     {
22264         Roo.log('onTogglePeriod');
22265         this.time = this.time.add(Date.HOUR, 12);
22266         this.update();
22267     }
22268     
22269    
22270 });
22271
22272 Roo.apply(Roo.bootstrap.TimeField,  {
22273     
22274     content : {
22275         tag: 'tbody',
22276         cn: [
22277             {
22278                 tag: 'tr',
22279                 cn: [
22280                 {
22281                     tag: 'td',
22282                     colspan: '7'
22283                 }
22284                 ]
22285             }
22286         ]
22287     },
22288     
22289     footer : {
22290         tag: 'tfoot',
22291         cn: [
22292             {
22293                 tag: 'tr',
22294                 cn: [
22295                 {
22296                     tag: 'th',
22297                     colspan: '7',
22298                     cls: '',
22299                     cn: [
22300                         {
22301                             tag: 'button',
22302                             cls: 'btn btn-info ok',
22303                             html: 'OK'
22304                         }
22305                     ]
22306                 }
22307
22308                 ]
22309             }
22310         ]
22311     }
22312 });
22313
22314 Roo.apply(Roo.bootstrap.TimeField,  {
22315   
22316     template : {
22317         tag: 'div',
22318         cls: 'datepicker dropdown-menu',
22319         cn: [
22320             {
22321                 tag: 'div',
22322                 cls: 'datepicker-time',
22323                 cn: [
22324                 {
22325                     tag: 'table',
22326                     cls: 'table-condensed',
22327                     cn:[
22328                     Roo.bootstrap.TimeField.content,
22329                     Roo.bootstrap.TimeField.footer
22330                     ]
22331                 }
22332                 ]
22333             }
22334         ]
22335     }
22336 });
22337
22338  
22339
22340  /*
22341  * - LGPL
22342  *
22343  * MonthField
22344  * 
22345  */
22346
22347 /**
22348  * @class Roo.bootstrap.MonthField
22349  * @extends Roo.bootstrap.Input
22350  * Bootstrap MonthField class
22351  * 
22352  * @cfg {String} language default en
22353  * 
22354  * @constructor
22355  * Create a new MonthField
22356  * @param {Object} config The config object
22357  */
22358
22359 Roo.bootstrap.MonthField = function(config){
22360     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22361     
22362     this.addEvents({
22363         /**
22364          * @event show
22365          * Fires when this field show.
22366          * @param {Roo.bootstrap.MonthField} this
22367          * @param {Mixed} date The date value
22368          */
22369         show : true,
22370         /**
22371          * @event show
22372          * Fires when this field hide.
22373          * @param {Roo.bootstrap.MonthField} this
22374          * @param {Mixed} date The date value
22375          */
22376         hide : true,
22377         /**
22378          * @event select
22379          * Fires when select a date.
22380          * @param {Roo.bootstrap.MonthField} this
22381          * @param {String} oldvalue The old value
22382          * @param {String} newvalue The new value
22383          */
22384         select : true
22385     });
22386 };
22387
22388 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22389     
22390     onRender: function(ct, position)
22391     {
22392         
22393         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22394         
22395         this.language = this.language || 'en';
22396         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22397         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22398         
22399         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22400         this.isInline = false;
22401         this.isInput = true;
22402         this.component = this.el.select('.add-on', true).first() || false;
22403         this.component = (this.component && this.component.length === 0) ? false : this.component;
22404         this.hasInput = this.component && this.inputEL().length;
22405         
22406         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22407         
22408         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22409         
22410         this.picker().on('mousedown', this.onMousedown, this);
22411         this.picker().on('click', this.onClick, this);
22412         
22413         this.picker().addClass('datepicker-dropdown');
22414         
22415         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22416             v.setStyle('width', '189px');
22417         });
22418         
22419         this.fillMonths();
22420         
22421         this.update();
22422         
22423         if(this.isInline) {
22424             this.show();
22425         }
22426         
22427     },
22428     
22429     setValue: function(v, suppressEvent)
22430     {   
22431         var o = this.getValue();
22432         
22433         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22434         
22435         this.update();
22436
22437         if(suppressEvent !== true){
22438             this.fireEvent('select', this, o, v);
22439         }
22440         
22441     },
22442     
22443     getValue: function()
22444     {
22445         return this.value;
22446     },
22447     
22448     onClick: function(e) 
22449     {
22450         e.stopPropagation();
22451         e.preventDefault();
22452         
22453         var target = e.getTarget();
22454         
22455         if(target.nodeName.toLowerCase() === 'i'){
22456             target = Roo.get(target).dom.parentNode;
22457         }
22458         
22459         var nodeName = target.nodeName;
22460         var className = target.className;
22461         var html = target.innerHTML;
22462         
22463         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22464             return;
22465         }
22466         
22467         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22468         
22469         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22470         
22471         this.hide();
22472                         
22473     },
22474     
22475     picker : function()
22476     {
22477         return this.pickerEl;
22478     },
22479     
22480     fillMonths: function()
22481     {    
22482         var i = 0;
22483         var months = this.picker().select('>.datepicker-months td', true).first();
22484         
22485         months.dom.innerHTML = '';
22486         
22487         while (i < 12) {
22488             var month = {
22489                 tag: 'span',
22490                 cls: 'month',
22491                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22492             };
22493             
22494             months.createChild(month);
22495         }
22496         
22497     },
22498     
22499     update: function()
22500     {
22501         var _this = this;
22502         
22503         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22504             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22505         }
22506         
22507         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22508             e.removeClass('active');
22509             
22510             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22511                 e.addClass('active');
22512             }
22513         })
22514     },
22515     
22516     place: function()
22517     {
22518         if(this.isInline) {
22519             return;
22520         }
22521         
22522         this.picker().removeClass(['bottom', 'top']);
22523         
22524         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22525             /*
22526              * place to the top of element!
22527              *
22528              */
22529             
22530             this.picker().addClass('top');
22531             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22532             
22533             return;
22534         }
22535         
22536         this.picker().addClass('bottom');
22537         
22538         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22539     },
22540     
22541     onFocus : function()
22542     {
22543         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22544         this.show();
22545     },
22546     
22547     onBlur : function()
22548     {
22549         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22550         
22551         var d = this.inputEl().getValue();
22552         
22553         this.setValue(d);
22554                 
22555         this.hide();
22556     },
22557     
22558     show : function()
22559     {
22560         this.picker().show();
22561         this.picker().select('>.datepicker-months', true).first().show();
22562         this.update();
22563         this.place();
22564         
22565         this.fireEvent('show', this, this.date);
22566     },
22567     
22568     hide : function()
22569     {
22570         if(this.isInline) {
22571             return;
22572         }
22573         this.picker().hide();
22574         this.fireEvent('hide', this, this.date);
22575         
22576     },
22577     
22578     onMousedown: function(e)
22579     {
22580         e.stopPropagation();
22581         e.preventDefault();
22582     },
22583     
22584     keyup: function(e)
22585     {
22586         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22587         this.update();
22588     },
22589
22590     fireKey: function(e)
22591     {
22592         if (!this.picker().isVisible()){
22593             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22594                 this.show();
22595             }
22596             return;
22597         }
22598         
22599         var dir;
22600         
22601         switch(e.keyCode){
22602             case 27: // escape
22603                 this.hide();
22604                 e.preventDefault();
22605                 break;
22606             case 37: // left
22607             case 39: // right
22608                 dir = e.keyCode == 37 ? -1 : 1;
22609                 
22610                 this.vIndex = this.vIndex + dir;
22611                 
22612                 if(this.vIndex < 0){
22613                     this.vIndex = 0;
22614                 }
22615                 
22616                 if(this.vIndex > 11){
22617                     this.vIndex = 11;
22618                 }
22619                 
22620                 if(isNaN(this.vIndex)){
22621                     this.vIndex = 0;
22622                 }
22623                 
22624                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22625                 
22626                 break;
22627             case 38: // up
22628             case 40: // down
22629                 
22630                 dir = e.keyCode == 38 ? -1 : 1;
22631                 
22632                 this.vIndex = this.vIndex + dir * 4;
22633                 
22634                 if(this.vIndex < 0){
22635                     this.vIndex = 0;
22636                 }
22637                 
22638                 if(this.vIndex > 11){
22639                     this.vIndex = 11;
22640                 }
22641                 
22642                 if(isNaN(this.vIndex)){
22643                     this.vIndex = 0;
22644                 }
22645                 
22646                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22647                 break;
22648                 
22649             case 13: // enter
22650                 
22651                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22652                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22653                 }
22654                 
22655                 this.hide();
22656                 e.preventDefault();
22657                 break;
22658             case 9: // tab
22659                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22660                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22661                 }
22662                 this.hide();
22663                 break;
22664             case 16: // shift
22665             case 17: // ctrl
22666             case 18: // alt
22667                 break;
22668             default :
22669                 this.hide();
22670                 
22671         }
22672     },
22673     
22674     remove: function() 
22675     {
22676         this.picker().remove();
22677     }
22678    
22679 });
22680
22681 Roo.apply(Roo.bootstrap.MonthField,  {
22682     
22683     content : {
22684         tag: 'tbody',
22685         cn: [
22686         {
22687             tag: 'tr',
22688             cn: [
22689             {
22690                 tag: 'td',
22691                 colspan: '7'
22692             }
22693             ]
22694         }
22695         ]
22696     },
22697     
22698     dates:{
22699         en: {
22700             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22701             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22702         }
22703     }
22704 });
22705
22706 Roo.apply(Roo.bootstrap.MonthField,  {
22707   
22708     template : {
22709         tag: 'div',
22710         cls: 'datepicker dropdown-menu roo-dynamic',
22711         cn: [
22712             {
22713                 tag: 'div',
22714                 cls: 'datepicker-months',
22715                 cn: [
22716                 {
22717                     tag: 'table',
22718                     cls: 'table-condensed',
22719                     cn:[
22720                         Roo.bootstrap.DateField.content
22721                     ]
22722                 }
22723                 ]
22724             }
22725         ]
22726     }
22727 });
22728
22729  
22730
22731  
22732  /*
22733  * - LGPL
22734  *
22735  * CheckBox
22736  * 
22737  */
22738
22739 /**
22740  * @class Roo.bootstrap.CheckBox
22741  * @extends Roo.bootstrap.Input
22742  * Bootstrap CheckBox class
22743  * 
22744  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22745  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22746  * @cfg {String} boxLabel The text that appears beside the checkbox
22747  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22748  * @cfg {Boolean} checked initnal the element
22749  * @cfg {Boolean} inline inline the element (default false)
22750  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22751  * @cfg {String} tooltip label tooltip
22752  * 
22753  * @constructor
22754  * Create a new CheckBox
22755  * @param {Object} config The config object
22756  */
22757
22758 Roo.bootstrap.CheckBox = function(config){
22759     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22760    
22761     this.addEvents({
22762         /**
22763         * @event check
22764         * Fires when the element is checked or unchecked.
22765         * @param {Roo.bootstrap.CheckBox} this This input
22766         * @param {Boolean} checked The new checked value
22767         */
22768        check : true,
22769        /**
22770         * @event click
22771         * Fires when the element is click.
22772         * @param {Roo.bootstrap.CheckBox} this This input
22773         */
22774        click : true
22775     });
22776     
22777 };
22778
22779 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22780   
22781     inputType: 'checkbox',
22782     inputValue: 1,
22783     valueOff: 0,
22784     boxLabel: false,
22785     checked: false,
22786     weight : false,
22787     inline: false,
22788     tooltip : '',
22789     
22790     // checkbox success does not make any sense really.. 
22791     invalidClass : "",
22792     validClass : "",
22793     
22794     
22795     getAutoCreate : function()
22796     {
22797         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22798         
22799         var id = Roo.id();
22800         
22801         var cfg = {};
22802         
22803         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22804         
22805         if(this.inline){
22806             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22807         }
22808         
22809         var input =  {
22810             tag: 'input',
22811             id : id,
22812             type : this.inputType,
22813             value : this.inputValue,
22814             cls : 'roo-' + this.inputType, //'form-box',
22815             placeholder : this.placeholder || ''
22816             
22817         };
22818         
22819         if(this.inputType != 'radio'){
22820             var hidden =  {
22821                 tag: 'input',
22822                 type : 'hidden',
22823                 cls : 'roo-hidden-value',
22824                 value : this.checked ? this.inputValue : this.valueOff
22825             };
22826         }
22827         
22828             
22829         if (this.weight) { // Validity check?
22830             cfg.cls += " " + this.inputType + "-" + this.weight;
22831         }
22832         
22833         if (this.disabled) {
22834             input.disabled=true;
22835         }
22836         
22837         if(this.checked){
22838             input.checked = this.checked;
22839         }
22840         
22841         if (this.name) {
22842             
22843             input.name = this.name;
22844             
22845             if(this.inputType != 'radio'){
22846                 hidden.name = this.name;
22847                 input.name = '_hidden_' + this.name;
22848             }
22849         }
22850         
22851         if (this.size) {
22852             input.cls += ' input-' + this.size;
22853         }
22854         
22855         var settings=this;
22856         
22857         ['xs','sm','md','lg'].map(function(size){
22858             if (settings[size]) {
22859                 cfg.cls += ' col-' + size + '-' + settings[size];
22860             }
22861         });
22862         
22863         var inputblock = input;
22864          
22865         if (this.before || this.after) {
22866             
22867             inputblock = {
22868                 cls : 'input-group',
22869                 cn :  [] 
22870             };
22871             
22872             if (this.before) {
22873                 inputblock.cn.push({
22874                     tag :'span',
22875                     cls : 'input-group-addon',
22876                     html : this.before
22877                 });
22878             }
22879             
22880             inputblock.cn.push(input);
22881             
22882             if(this.inputType != 'radio'){
22883                 inputblock.cn.push(hidden);
22884             }
22885             
22886             if (this.after) {
22887                 inputblock.cn.push({
22888                     tag :'span',
22889                     cls : 'input-group-addon',
22890                     html : this.after
22891                 });
22892             }
22893             
22894         }
22895         var boxLabelCfg = false;
22896         
22897         if(this.boxLabel){
22898            
22899             boxLabelCfg = {
22900                 tag: 'label',
22901                 //'for': id, // box label is handled by onclick - so no for...
22902                 cls: 'box-label',
22903                 html: this.boxLabel
22904             };
22905             if(this.tooltip){
22906                 boxLabelCfg.tooltip = this.tooltip;
22907             }
22908              
22909         }
22910         
22911         
22912         if (align ==='left' && this.fieldLabel.length) {
22913 //                Roo.log("left and has label");
22914             cfg.cn = [
22915                 {
22916                     tag: 'label',
22917                     'for' :  id,
22918                     cls : 'control-label',
22919                     html : this.fieldLabel
22920                 },
22921                 {
22922                     cls : "", 
22923                     cn: [
22924                         inputblock
22925                     ]
22926                 }
22927             ];
22928             
22929             if (boxLabelCfg) {
22930                 cfg.cn[1].cn.push(boxLabelCfg);
22931             }
22932             
22933             if(this.labelWidth > 12){
22934                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22935             }
22936             
22937             if(this.labelWidth < 13 && this.labelmd == 0){
22938                 this.labelmd = this.labelWidth;
22939             }
22940             
22941             if(this.labellg > 0){
22942                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22943                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22944             }
22945             
22946             if(this.labelmd > 0){
22947                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22948                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22949             }
22950             
22951             if(this.labelsm > 0){
22952                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22953                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22954             }
22955             
22956             if(this.labelxs > 0){
22957                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22958                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22959             }
22960             
22961         } else if ( this.fieldLabel.length) {
22962 //                Roo.log(" label");
22963                 cfg.cn = [
22964                    
22965                     {
22966                         tag: this.boxLabel ? 'span' : 'label',
22967                         'for': id,
22968                         cls: 'control-label box-input-label',
22969                         //cls : 'input-group-addon',
22970                         html : this.fieldLabel
22971                     },
22972                     
22973                     inputblock
22974                     
22975                 ];
22976                 if (boxLabelCfg) {
22977                     cfg.cn.push(boxLabelCfg);
22978                 }
22979
22980         } else {
22981             
22982 //                Roo.log(" no label && no align");
22983                 cfg.cn = [  inputblock ] ;
22984                 if (boxLabelCfg) {
22985                     cfg.cn.push(boxLabelCfg);
22986                 }
22987
22988                 
22989         }
22990         
22991        
22992         
22993         if(this.inputType != 'radio'){
22994             cfg.cn.push(hidden);
22995         }
22996         
22997         return cfg;
22998         
22999     },
23000     
23001     /**
23002      * return the real input element.
23003      */
23004     inputEl: function ()
23005     {
23006         return this.el.select('input.roo-' + this.inputType,true).first();
23007     },
23008     hiddenEl: function ()
23009     {
23010         return this.el.select('input.roo-hidden-value',true).first();
23011     },
23012     
23013     labelEl: function()
23014     {
23015         return this.el.select('label.control-label',true).first();
23016     },
23017     /* depricated... */
23018     
23019     label: function()
23020     {
23021         return this.labelEl();
23022     },
23023     
23024     boxLabelEl: function()
23025     {
23026         return this.el.select('label.box-label',true).first();
23027     },
23028     
23029     initEvents : function()
23030     {
23031 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23032         
23033         this.inputEl().on('click', this.onClick,  this);
23034         
23035         if (this.boxLabel) { 
23036             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23037         }
23038         
23039         this.startValue = this.getValue();
23040         
23041         if(this.groupId){
23042             Roo.bootstrap.CheckBox.register(this);
23043         }
23044     },
23045     
23046     onClick : function(e)
23047     {   
23048         if(this.fireEvent('click', this, e) !== false){
23049             this.setChecked(!this.checked);
23050         }
23051         
23052     },
23053     
23054     setChecked : function(state,suppressEvent)
23055     {
23056         this.startValue = this.getValue();
23057
23058         if(this.inputType == 'radio'){
23059             
23060             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23061                 e.dom.checked = false;
23062             });
23063             
23064             this.inputEl().dom.checked = true;
23065             
23066             this.inputEl().dom.value = this.inputValue;
23067             
23068             if(suppressEvent !== true){
23069                 this.fireEvent('check', this, true);
23070             }
23071             
23072             this.validate();
23073             
23074             return;
23075         }
23076         
23077         this.checked = state;
23078         
23079         this.inputEl().dom.checked = state;
23080         
23081         
23082         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23083         
23084         if(suppressEvent !== true){
23085             this.fireEvent('check', this, state);
23086         }
23087         
23088         this.validate();
23089     },
23090     
23091     getValue : function()
23092     {
23093         if(this.inputType == 'radio'){
23094             return this.getGroupValue();
23095         }
23096         
23097         return this.hiddenEl().dom.value;
23098         
23099     },
23100     
23101     getGroupValue : function()
23102     {
23103         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23104             return '';
23105         }
23106         
23107         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23108     },
23109     
23110     setValue : function(v,suppressEvent)
23111     {
23112         if(this.inputType == 'radio'){
23113             this.setGroupValue(v, suppressEvent);
23114             return;
23115         }
23116         
23117         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23118         
23119         this.validate();
23120     },
23121     
23122     setGroupValue : function(v, suppressEvent)
23123     {
23124         this.startValue = this.getValue();
23125         
23126         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23127             e.dom.checked = false;
23128             
23129             if(e.dom.value == v){
23130                 e.dom.checked = true;
23131             }
23132         });
23133         
23134         if(suppressEvent !== true){
23135             this.fireEvent('check', this, true);
23136         }
23137
23138         this.validate();
23139         
23140         return;
23141     },
23142     
23143     validate : function()
23144     {
23145         if(this.getVisibilityEl().hasClass('hidden')){
23146             return true;
23147         }
23148         
23149         if(
23150                 this.disabled || 
23151                 (this.inputType == 'radio' && this.validateRadio()) ||
23152                 (this.inputType == 'checkbox' && this.validateCheckbox())
23153         ){
23154             this.markValid();
23155             return true;
23156         }
23157         
23158         this.markInvalid();
23159         return false;
23160     },
23161     
23162     validateRadio : function()
23163     {
23164         if(this.getVisibilityEl().hasClass('hidden')){
23165             return true;
23166         }
23167         
23168         if(this.allowBlank){
23169             return true;
23170         }
23171         
23172         var valid = false;
23173         
23174         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23175             if(!e.dom.checked){
23176                 return;
23177             }
23178             
23179             valid = true;
23180             
23181             return false;
23182         });
23183         
23184         return valid;
23185     },
23186     
23187     validateCheckbox : function()
23188     {
23189         if(!this.groupId){
23190             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23191             //return (this.getValue() == this.inputValue) ? true : false;
23192         }
23193         
23194         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23195         
23196         if(!group){
23197             return false;
23198         }
23199         
23200         var r = false;
23201         
23202         for(var i in group){
23203             if(group[i].el.isVisible(true)){
23204                 r = false;
23205                 break;
23206             }
23207             
23208             r = true;
23209         }
23210         
23211         for(var i in group){
23212             if(r){
23213                 break;
23214             }
23215             
23216             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23217         }
23218         
23219         return r;
23220     },
23221     
23222     /**
23223      * Mark this field as valid
23224      */
23225     markValid : function()
23226     {
23227         var _this = this;
23228         
23229         this.fireEvent('valid', this);
23230         
23231         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23232         
23233         if(this.groupId){
23234             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23235         }
23236         
23237         if(label){
23238             label.markValid();
23239         }
23240
23241         if(this.inputType == 'radio'){
23242             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23243                 var fg = e.findParent('.form-group', false, true);
23244                 if (Roo.bootstrap.version == 3) {
23245                     fg.removeClass([_this.invalidClass, _this.validClass]);
23246                     fg.addClass(_this.validClass);
23247                 } else {
23248                     fg.removeClass(['is-valid', 'is-invalid']);
23249                     fg.addClass('is-valid');
23250                 }
23251             });
23252             
23253             return;
23254         }
23255
23256         if(!this.groupId){
23257             var fg = this.el.findParent('.form-group', false, true);
23258             if (Roo.bootstrap.version == 3) {
23259                 fg.removeClass([this.invalidClass, this.validClass]);
23260                 fg.addClass(this.validClass);
23261             } else {
23262                 fg.removeClass(['is-valid', 'is-invalid']);
23263                 fg.addClass('is-valid');
23264             }
23265             return;
23266         }
23267         
23268         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23269         
23270         if(!group){
23271             return;
23272         }
23273         
23274         for(var i in group){
23275             var fg = group[i].el.findParent('.form-group', false, true);
23276             if (Roo.bootstrap.version == 3) {
23277                 fg.removeClass([this.invalidClass, this.validClass]);
23278                 fg.addClass(this.validClass);
23279             } else {
23280                 fg.removeClass(['is-valid', 'is-invalid']);
23281                 fg.addClass('is-valid');
23282             }
23283         }
23284     },
23285     
23286      /**
23287      * Mark this field as invalid
23288      * @param {String} msg The validation message
23289      */
23290     markInvalid : function(msg)
23291     {
23292         if(this.allowBlank){
23293             return;
23294         }
23295         
23296         var _this = this;
23297         
23298         this.fireEvent('invalid', this, msg);
23299         
23300         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23301         
23302         if(this.groupId){
23303             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23304         }
23305         
23306         if(label){
23307             label.markInvalid();
23308         }
23309             
23310         if(this.inputType == 'radio'){
23311             
23312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23313                 var fg = e.findParent('.form-group', false, true);
23314                 if (Roo.bootstrap.version == 3) {
23315                     fg.removeClass([_this.invalidClass, _this.validClass]);
23316                     fg.addClass(_this.invalidClass);
23317                 } else {
23318                     fg.removeClass(['is-invalid', 'is-valid']);
23319                     fg.addClass('is-invalid');
23320                 }
23321             });
23322             
23323             return;
23324         }
23325         
23326         if(!this.groupId){
23327             var fg = this.el.findParent('.form-group', false, true);
23328             if (Roo.bootstrap.version == 3) {
23329                 fg.removeClass([_this.invalidClass, _this.validClass]);
23330                 fg.addClass(_this.invalidClass);
23331             } else {
23332                 fg.removeClass(['is-invalid', 'is-valid']);
23333                 fg.addClass('is-invalid');
23334             }
23335             return;
23336         }
23337         
23338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23339         
23340         if(!group){
23341             return;
23342         }
23343         
23344         for(var i in group){
23345             var fg = group[i].el.findParent('.form-group', false, true);
23346             if (Roo.bootstrap.version == 3) {
23347                 fg.removeClass([_this.invalidClass, _this.validClass]);
23348                 fg.addClass(_this.invalidClass);
23349             } else {
23350                 fg.removeClass(['is-invalid', 'is-valid']);
23351                 fg.addClass('is-invalid');
23352             }
23353         }
23354         
23355     },
23356     
23357     clearInvalid : function()
23358     {
23359         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23360         
23361         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23362         
23363         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23364         
23365         if (label && label.iconEl) {
23366             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23367             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23368         }
23369     },
23370     
23371     disable : function()
23372     {
23373         if(this.inputType != 'radio'){
23374             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23375             return;
23376         }
23377         
23378         var _this = this;
23379         
23380         if(this.rendered){
23381             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23382                 _this.getActionEl().addClass(this.disabledClass);
23383                 e.dom.disabled = true;
23384             });
23385         }
23386         
23387         this.disabled = true;
23388         this.fireEvent("disable", this);
23389         return this;
23390     },
23391
23392     enable : function()
23393     {
23394         if(this.inputType != 'radio'){
23395             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23396             return;
23397         }
23398         
23399         var _this = this;
23400         
23401         if(this.rendered){
23402             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23403                 _this.getActionEl().removeClass(this.disabledClass);
23404                 e.dom.disabled = false;
23405             });
23406         }
23407         
23408         this.disabled = false;
23409         this.fireEvent("enable", this);
23410         return this;
23411     },
23412     
23413     setBoxLabel : function(v)
23414     {
23415         this.boxLabel = v;
23416         
23417         if(this.rendered){
23418             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23419         }
23420     }
23421
23422 });
23423
23424 Roo.apply(Roo.bootstrap.CheckBox, {
23425     
23426     groups: {},
23427     
23428      /**
23429     * register a CheckBox Group
23430     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23431     */
23432     register : function(checkbox)
23433     {
23434         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23435             this.groups[checkbox.groupId] = {};
23436         }
23437         
23438         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23439             return;
23440         }
23441         
23442         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23443         
23444     },
23445     /**
23446     * fetch a CheckBox Group based on the group ID
23447     * @param {string} the group ID
23448     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23449     */
23450     get: function(groupId) {
23451         if (typeof(this.groups[groupId]) == 'undefined') {
23452             return false;
23453         }
23454         
23455         return this.groups[groupId] ;
23456     }
23457     
23458     
23459 });
23460 /*
23461  * - LGPL
23462  *
23463  * RadioItem
23464  * 
23465  */
23466
23467 /**
23468  * @class Roo.bootstrap.Radio
23469  * @extends Roo.bootstrap.Component
23470  * Bootstrap Radio class
23471  * @cfg {String} boxLabel - the label associated
23472  * @cfg {String} value - the value of radio
23473  * 
23474  * @constructor
23475  * Create a new Radio
23476  * @param {Object} config The config object
23477  */
23478 Roo.bootstrap.Radio = function(config){
23479     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23480     
23481 };
23482
23483 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23484     
23485     boxLabel : '',
23486     
23487     value : '',
23488     
23489     getAutoCreate : function()
23490     {
23491         var cfg = {
23492             tag : 'div',
23493             cls : 'form-group radio',
23494             cn : [
23495                 {
23496                     tag : 'label',
23497                     cls : 'box-label',
23498                     html : this.boxLabel
23499                 }
23500             ]
23501         };
23502         
23503         return cfg;
23504     },
23505     
23506     initEvents : function() 
23507     {
23508         this.parent().register(this);
23509         
23510         this.el.on('click', this.onClick, this);
23511         
23512     },
23513     
23514     onClick : function(e)
23515     {
23516         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23517             this.setChecked(true);
23518         }
23519     },
23520     
23521     setChecked : function(state, suppressEvent)
23522     {
23523         this.parent().setValue(this.value, suppressEvent);
23524         
23525     },
23526     
23527     setBoxLabel : function(v)
23528     {
23529         this.boxLabel = v;
23530         
23531         if(this.rendered){
23532             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23533         }
23534     }
23535     
23536 });
23537  
23538
23539  /*
23540  * - LGPL
23541  *
23542  * Input
23543  * 
23544  */
23545
23546 /**
23547  * @class Roo.bootstrap.SecurePass
23548  * @extends Roo.bootstrap.Input
23549  * Bootstrap SecurePass class
23550  *
23551  * 
23552  * @constructor
23553  * Create a new SecurePass
23554  * @param {Object} config The config object
23555  */
23556  
23557 Roo.bootstrap.SecurePass = function (config) {
23558     // these go here, so the translation tool can replace them..
23559     this.errors = {
23560         PwdEmpty: "Please type a password, and then retype it to confirm.",
23561         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23562         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23563         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23564         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23565         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23566         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23567         TooWeak: "Your password is Too Weak."
23568     },
23569     this.meterLabel = "Password strength:";
23570     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23571     this.meterClass = [
23572         "roo-password-meter-tooweak", 
23573         "roo-password-meter-weak", 
23574         "roo-password-meter-medium", 
23575         "roo-password-meter-strong", 
23576         "roo-password-meter-grey"
23577     ];
23578     
23579     this.errors = {};
23580     
23581     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23582 }
23583
23584 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23585     /**
23586      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23587      * {
23588      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23589      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23590      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23591      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23592      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23593      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23594      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23595      * })
23596      */
23597     // private
23598     
23599     meterWidth: 300,
23600     errorMsg :'',    
23601     errors: false,
23602     imageRoot: '/',
23603     /**
23604      * @cfg {String/Object} Label for the strength meter (defaults to
23605      * 'Password strength:')
23606      */
23607     // private
23608     meterLabel: '',
23609     /**
23610      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23611      * ['Weak', 'Medium', 'Strong'])
23612      */
23613     // private    
23614     pwdStrengths: false,    
23615     // private
23616     strength: 0,
23617     // private
23618     _lastPwd: null,
23619     // private
23620     kCapitalLetter: 0,
23621     kSmallLetter: 1,
23622     kDigit: 2,
23623     kPunctuation: 3,
23624     
23625     insecure: false,
23626     // private
23627     initEvents: function ()
23628     {
23629         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23630
23631         if (this.el.is('input[type=password]') && Roo.isSafari) {
23632             this.el.on('keydown', this.SafariOnKeyDown, this);
23633         }
23634
23635         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23636     },
23637     // private
23638     onRender: function (ct, position)
23639     {
23640         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23641         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23642         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23643
23644         this.trigger.createChild({
23645                    cn: [
23646                     {
23647                     //id: 'PwdMeter',
23648                     tag: 'div',
23649                     cls: 'roo-password-meter-grey col-xs-12',
23650                     style: {
23651                         //width: 0,
23652                         //width: this.meterWidth + 'px'                                                
23653                         }
23654                     },
23655                     {                            
23656                          cls: 'roo-password-meter-text'                          
23657                     }
23658                 ]            
23659         });
23660
23661          
23662         if (this.hideTrigger) {
23663             this.trigger.setDisplayed(false);
23664         }
23665         this.setSize(this.width || '', this.height || '');
23666     },
23667     // private
23668     onDestroy: function ()
23669     {
23670         if (this.trigger) {
23671             this.trigger.removeAllListeners();
23672             this.trigger.remove();
23673         }
23674         if (this.wrap) {
23675             this.wrap.remove();
23676         }
23677         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23678     },
23679     // private
23680     checkStrength: function ()
23681     {
23682         var pwd = this.inputEl().getValue();
23683         if (pwd == this._lastPwd) {
23684             return;
23685         }
23686
23687         var strength;
23688         if (this.ClientSideStrongPassword(pwd)) {
23689             strength = 3;
23690         } else if (this.ClientSideMediumPassword(pwd)) {
23691             strength = 2;
23692         } else if (this.ClientSideWeakPassword(pwd)) {
23693             strength = 1;
23694         } else {
23695             strength = 0;
23696         }
23697         
23698         Roo.log('strength1: ' + strength);
23699         
23700         //var pm = this.trigger.child('div/div/div').dom;
23701         var pm = this.trigger.child('div/div');
23702         pm.removeClass(this.meterClass);
23703         pm.addClass(this.meterClass[strength]);
23704                 
23705         
23706         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23707                 
23708         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23709         
23710         this._lastPwd = pwd;
23711     },
23712     reset: function ()
23713     {
23714         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23715         
23716         this._lastPwd = '';
23717         
23718         var pm = this.trigger.child('div/div');
23719         pm.removeClass(this.meterClass);
23720         pm.addClass('roo-password-meter-grey');        
23721         
23722         
23723         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23724         
23725         pt.innerHTML = '';
23726         this.inputEl().dom.type='password';
23727     },
23728     // private
23729     validateValue: function (value)
23730     {
23731         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23732             return false;
23733         }
23734         if (value.length == 0) {
23735             if (this.allowBlank) {
23736                 this.clearInvalid();
23737                 return true;
23738             }
23739
23740             this.markInvalid(this.errors.PwdEmpty);
23741             this.errorMsg = this.errors.PwdEmpty;
23742             return false;
23743         }
23744         
23745         if(this.insecure){
23746             return true;
23747         }
23748         
23749         if (!value.match(/[\x21-\x7e]+/)) {
23750             this.markInvalid(this.errors.PwdBadChar);
23751             this.errorMsg = this.errors.PwdBadChar;
23752             return false;
23753         }
23754         if (value.length < 6) {
23755             this.markInvalid(this.errors.PwdShort);
23756             this.errorMsg = this.errors.PwdShort;
23757             return false;
23758         }
23759         if (value.length > 16) {
23760             this.markInvalid(this.errors.PwdLong);
23761             this.errorMsg = this.errors.PwdLong;
23762             return false;
23763         }
23764         var strength;
23765         if (this.ClientSideStrongPassword(value)) {
23766             strength = 3;
23767         } else if (this.ClientSideMediumPassword(value)) {
23768             strength = 2;
23769         } else if (this.ClientSideWeakPassword(value)) {
23770             strength = 1;
23771         } else {
23772             strength = 0;
23773         }
23774
23775         
23776         if (strength < 2) {
23777             //this.markInvalid(this.errors.TooWeak);
23778             this.errorMsg = this.errors.TooWeak;
23779             //return false;
23780         }
23781         
23782         
23783         console.log('strength2: ' + strength);
23784         
23785         //var pm = this.trigger.child('div/div/div').dom;
23786         
23787         var pm = this.trigger.child('div/div');
23788         pm.removeClass(this.meterClass);
23789         pm.addClass(this.meterClass[strength]);
23790                 
23791         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23792                 
23793         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23794         
23795         this.errorMsg = ''; 
23796         return true;
23797     },
23798     // private
23799     CharacterSetChecks: function (type)
23800     {
23801         this.type = type;
23802         this.fResult = false;
23803     },
23804     // private
23805     isctype: function (character, type)
23806     {
23807         switch (type) {  
23808             case this.kCapitalLetter:
23809                 if (character >= 'A' && character <= 'Z') {
23810                     return true;
23811                 }
23812                 break;
23813             
23814             case this.kSmallLetter:
23815                 if (character >= 'a' && character <= 'z') {
23816                     return true;
23817                 }
23818                 break;
23819             
23820             case this.kDigit:
23821                 if (character >= '0' && character <= '9') {
23822                     return true;
23823                 }
23824                 break;
23825             
23826             case this.kPunctuation:
23827                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23828                     return true;
23829                 }
23830                 break;
23831             
23832             default:
23833                 return false;
23834         }
23835
23836     },
23837     // private
23838     IsLongEnough: function (pwd, size)
23839     {
23840         return !(pwd == null || isNaN(size) || pwd.length < size);
23841     },
23842     // private
23843     SpansEnoughCharacterSets: function (word, nb)
23844     {
23845         if (!this.IsLongEnough(word, nb))
23846         {
23847             return false;
23848         }
23849
23850         var characterSetChecks = new Array(
23851             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23852             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23853         );
23854         
23855         for (var index = 0; index < word.length; ++index) {
23856             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23857                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23858                     characterSetChecks[nCharSet].fResult = true;
23859                     break;
23860                 }
23861             }
23862         }
23863
23864         var nCharSets = 0;
23865         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23866             if (characterSetChecks[nCharSet].fResult) {
23867                 ++nCharSets;
23868             }
23869         }
23870
23871         if (nCharSets < nb) {
23872             return false;
23873         }
23874         return true;
23875     },
23876     // private
23877     ClientSideStrongPassword: function (pwd)
23878     {
23879         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23880     },
23881     // private
23882     ClientSideMediumPassword: function (pwd)
23883     {
23884         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23885     },
23886     // private
23887     ClientSideWeakPassword: function (pwd)
23888     {
23889         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23890     }
23891           
23892 })//<script type="text/javascript">
23893
23894 /*
23895  * Based  Ext JS Library 1.1.1
23896  * Copyright(c) 2006-2007, Ext JS, LLC.
23897  * LGPL
23898  *
23899  */
23900  
23901 /**
23902  * @class Roo.HtmlEditorCore
23903  * @extends Roo.Component
23904  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23905  *
23906  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23907  */
23908
23909 Roo.HtmlEditorCore = function(config){
23910     
23911     
23912     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23913     
23914     
23915     this.addEvents({
23916         /**
23917          * @event initialize
23918          * Fires when the editor is fully initialized (including the iframe)
23919          * @param {Roo.HtmlEditorCore} this
23920          */
23921         initialize: true,
23922         /**
23923          * @event activate
23924          * Fires when the editor is first receives the focus. Any insertion must wait
23925          * until after this event.
23926          * @param {Roo.HtmlEditorCore} this
23927          */
23928         activate: true,
23929          /**
23930          * @event beforesync
23931          * Fires before the textarea is updated with content from the editor iframe. Return false
23932          * to cancel the sync.
23933          * @param {Roo.HtmlEditorCore} this
23934          * @param {String} html
23935          */
23936         beforesync: true,
23937          /**
23938          * @event beforepush
23939          * Fires before the iframe editor is updated with content from the textarea. Return false
23940          * to cancel the push.
23941          * @param {Roo.HtmlEditorCore} this
23942          * @param {String} html
23943          */
23944         beforepush: true,
23945          /**
23946          * @event sync
23947          * Fires when the textarea is updated with content from the editor iframe.
23948          * @param {Roo.HtmlEditorCore} this
23949          * @param {String} html
23950          */
23951         sync: true,
23952          /**
23953          * @event push
23954          * Fires when the iframe editor is updated with content from the textarea.
23955          * @param {Roo.HtmlEditorCore} this
23956          * @param {String} html
23957          */
23958         push: true,
23959         
23960         /**
23961          * @event editorevent
23962          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23963          * @param {Roo.HtmlEditorCore} this
23964          */
23965         editorevent: true
23966         
23967     });
23968     
23969     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23970     
23971     // defaults : white / black...
23972     this.applyBlacklists();
23973     
23974     
23975     
23976 };
23977
23978
23979 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23980
23981
23982      /**
23983      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23984      */
23985     
23986     owner : false,
23987     
23988      /**
23989      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23990      *                        Roo.resizable.
23991      */
23992     resizable : false,
23993      /**
23994      * @cfg {Number} height (in pixels)
23995      */   
23996     height: 300,
23997    /**
23998      * @cfg {Number} width (in pixels)
23999      */   
24000     width: 500,
24001     
24002     /**
24003      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24004      * 
24005      */
24006     stylesheets: false,
24007     
24008     // id of frame..
24009     frameId: false,
24010     
24011     // private properties
24012     validationEvent : false,
24013     deferHeight: true,
24014     initialized : false,
24015     activated : false,
24016     sourceEditMode : false,
24017     onFocus : Roo.emptyFn,
24018     iframePad:3,
24019     hideMode:'offsets',
24020     
24021     clearUp: true,
24022     
24023     // blacklist + whitelisted elements..
24024     black: false,
24025     white: false,
24026      
24027     bodyCls : '',
24028
24029     /**
24030      * Protected method that will not generally be called directly. It
24031      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24032      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24033      */
24034     getDocMarkup : function(){
24035         // body styles..
24036         var st = '';
24037         
24038         // inherit styels from page...?? 
24039         if (this.stylesheets === false) {
24040             
24041             Roo.get(document.head).select('style').each(function(node) {
24042                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24043             });
24044             
24045             Roo.get(document.head).select('link').each(function(node) { 
24046                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24047             });
24048             
24049         } else if (!this.stylesheets.length) {
24050                 // simple..
24051                 st = '<style type="text/css">' +
24052                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24053                    '</style>';
24054         } else {
24055             for (var i in this.stylesheets) { 
24056                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24057             }
24058             
24059         }
24060         
24061         st +=  '<style type="text/css">' +
24062             'IMG { cursor: pointer } ' +
24063         '</style>';
24064
24065         var cls = 'roo-htmleditor-body';
24066         
24067         if(this.bodyCls.length){
24068             cls += ' ' + this.bodyCls;
24069         }
24070         
24071         return '<html><head>' + st  +
24072             //<style type="text/css">' +
24073             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24074             //'</style>' +
24075             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24076     },
24077
24078     // private
24079     onRender : function(ct, position)
24080     {
24081         var _t = this;
24082         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24083         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24084         
24085         
24086         this.el.dom.style.border = '0 none';
24087         this.el.dom.setAttribute('tabIndex', -1);
24088         this.el.addClass('x-hidden hide');
24089         
24090         
24091         
24092         if(Roo.isIE){ // fix IE 1px bogus margin
24093             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24094         }
24095        
24096         
24097         this.frameId = Roo.id();
24098         
24099          
24100         
24101         var iframe = this.owner.wrap.createChild({
24102             tag: 'iframe',
24103             cls: 'form-control', // bootstrap..
24104             id: this.frameId,
24105             name: this.frameId,
24106             frameBorder : 'no',
24107             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24108         }, this.el
24109         );
24110         
24111         
24112         this.iframe = iframe.dom;
24113
24114          this.assignDocWin();
24115         
24116         this.doc.designMode = 'on';
24117        
24118         this.doc.open();
24119         this.doc.write(this.getDocMarkup());
24120         this.doc.close();
24121
24122         
24123         var task = { // must defer to wait for browser to be ready
24124             run : function(){
24125                 //console.log("run task?" + this.doc.readyState);
24126                 this.assignDocWin();
24127                 if(this.doc.body || this.doc.readyState == 'complete'){
24128                     try {
24129                         this.doc.designMode="on";
24130                     } catch (e) {
24131                         return;
24132                     }
24133                     Roo.TaskMgr.stop(task);
24134                     this.initEditor.defer(10, this);
24135                 }
24136             },
24137             interval : 10,
24138             duration: 10000,
24139             scope: this
24140         };
24141         Roo.TaskMgr.start(task);
24142
24143     },
24144
24145     // private
24146     onResize : function(w, h)
24147     {
24148          Roo.log('resize: ' +w + ',' + h );
24149         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24150         if(!this.iframe){
24151             return;
24152         }
24153         if(typeof w == 'number'){
24154             
24155             this.iframe.style.width = w + 'px';
24156         }
24157         if(typeof h == 'number'){
24158             
24159             this.iframe.style.height = h + 'px';
24160             if(this.doc){
24161                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24162             }
24163         }
24164         
24165     },
24166
24167     /**
24168      * Toggles the editor between standard and source edit mode.
24169      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24170      */
24171     toggleSourceEdit : function(sourceEditMode){
24172         
24173         this.sourceEditMode = sourceEditMode === true;
24174         
24175         if(this.sourceEditMode){
24176  
24177             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24178             
24179         }else{
24180             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24181             //this.iframe.className = '';
24182             this.deferFocus();
24183         }
24184         //this.setSize(this.owner.wrap.getSize());
24185         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24186     },
24187
24188     
24189   
24190
24191     /**
24192      * Protected method that will not generally be called directly. If you need/want
24193      * custom HTML cleanup, this is the method you should override.
24194      * @param {String} html The HTML to be cleaned
24195      * return {String} The cleaned HTML
24196      */
24197     cleanHtml : function(html){
24198         html = String(html);
24199         if(html.length > 5){
24200             if(Roo.isSafari){ // strip safari nonsense
24201                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24202             }
24203         }
24204         if(html == '&nbsp;'){
24205             html = '';
24206         }
24207         return html;
24208     },
24209
24210     /**
24211      * HTML Editor -> Textarea
24212      * Protected method that will not generally be called directly. Syncs the contents
24213      * of the editor iframe with the textarea.
24214      */
24215     syncValue : function(){
24216         if(this.initialized){
24217             var bd = (this.doc.body || this.doc.documentElement);
24218             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24219             var html = bd.innerHTML;
24220             if(Roo.isSafari){
24221                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24222                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24223                 if(m && m[1]){
24224                     html = '<div style="'+m[0]+'">' + html + '</div>';
24225                 }
24226             }
24227             html = this.cleanHtml(html);
24228             // fix up the special chars.. normaly like back quotes in word...
24229             // however we do not want to do this with chinese..
24230             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24231                 
24232                 var cc = match.charCodeAt();
24233
24234                 // Get the character value, handling surrogate pairs
24235                 if (match.length == 2) {
24236                     // It's a surrogate pair, calculate the Unicode code point
24237                     var high = match.charCodeAt(0) - 0xD800;
24238                     var low  = match.charCodeAt(1) - 0xDC00;
24239                     cc = (high * 0x400) + low + 0x10000;
24240                 }  else if (
24241                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24242                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24243                     (cc >= 0xf900 && cc < 0xfb00 )
24244                 ) {
24245                         return match;
24246                 }  
24247          
24248                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24249                 return "&#" + cc + ";";
24250                 
24251                 
24252             });
24253             
24254             
24255              
24256             if(this.owner.fireEvent('beforesync', this, html) !== false){
24257                 this.el.dom.value = html;
24258                 this.owner.fireEvent('sync', this, html);
24259             }
24260         }
24261     },
24262
24263     /**
24264      * Protected method that will not generally be called directly. Pushes the value of the textarea
24265      * into the iframe editor.
24266      */
24267     pushValue : function(){
24268         if(this.initialized){
24269             var v = this.el.dom.value.trim();
24270             
24271 //            if(v.length < 1){
24272 //                v = '&#160;';
24273 //            }
24274             
24275             if(this.owner.fireEvent('beforepush', this, v) !== false){
24276                 var d = (this.doc.body || this.doc.documentElement);
24277                 d.innerHTML = v;
24278                 this.cleanUpPaste();
24279                 this.el.dom.value = d.innerHTML;
24280                 this.owner.fireEvent('push', this, v);
24281             }
24282         }
24283     },
24284
24285     // private
24286     deferFocus : function(){
24287         this.focus.defer(10, this);
24288     },
24289
24290     // doc'ed in Field
24291     focus : function(){
24292         if(this.win && !this.sourceEditMode){
24293             this.win.focus();
24294         }else{
24295             this.el.focus();
24296         }
24297     },
24298     
24299     assignDocWin: function()
24300     {
24301         var iframe = this.iframe;
24302         
24303          if(Roo.isIE){
24304             this.doc = iframe.contentWindow.document;
24305             this.win = iframe.contentWindow;
24306         } else {
24307 //            if (!Roo.get(this.frameId)) {
24308 //                return;
24309 //            }
24310 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24311 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24312             
24313             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24314                 return;
24315             }
24316             
24317             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24318             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24319         }
24320     },
24321     
24322     // private
24323     initEditor : function(){
24324         //console.log("INIT EDITOR");
24325         this.assignDocWin();
24326         
24327         
24328         
24329         this.doc.designMode="on";
24330         this.doc.open();
24331         this.doc.write(this.getDocMarkup());
24332         this.doc.close();
24333         
24334         var dbody = (this.doc.body || this.doc.documentElement);
24335         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24336         // this copies styles from the containing element into thsi one..
24337         // not sure why we need all of this..
24338         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24339         
24340         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24341         //ss['background-attachment'] = 'fixed'; // w3c
24342         dbody.bgProperties = 'fixed'; // ie
24343         //Roo.DomHelper.applyStyles(dbody, ss);
24344         Roo.EventManager.on(this.doc, {
24345             //'mousedown': this.onEditorEvent,
24346             'mouseup': this.onEditorEvent,
24347             'dblclick': this.onEditorEvent,
24348             'click': this.onEditorEvent,
24349             'keyup': this.onEditorEvent,
24350             buffer:100,
24351             scope: this
24352         });
24353         if(Roo.isGecko){
24354             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24355         }
24356         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24357             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24358         }
24359         this.initialized = true;
24360
24361         this.owner.fireEvent('initialize', this);
24362         this.pushValue();
24363     },
24364
24365     // private
24366     onDestroy : function(){
24367         
24368         
24369         
24370         if(this.rendered){
24371             
24372             //for (var i =0; i < this.toolbars.length;i++) {
24373             //    // fixme - ask toolbars for heights?
24374             //    this.toolbars[i].onDestroy();
24375            // }
24376             
24377             //this.wrap.dom.innerHTML = '';
24378             //this.wrap.remove();
24379         }
24380     },
24381
24382     // private
24383     onFirstFocus : function(){
24384         
24385         this.assignDocWin();
24386         
24387         
24388         this.activated = true;
24389          
24390     
24391         if(Roo.isGecko){ // prevent silly gecko errors
24392             this.win.focus();
24393             var s = this.win.getSelection();
24394             if(!s.focusNode || s.focusNode.nodeType != 3){
24395                 var r = s.getRangeAt(0);
24396                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24397                 r.collapse(true);
24398                 this.deferFocus();
24399             }
24400             try{
24401                 this.execCmd('useCSS', true);
24402                 this.execCmd('styleWithCSS', false);
24403             }catch(e){}
24404         }
24405         this.owner.fireEvent('activate', this);
24406     },
24407
24408     // private
24409     adjustFont: function(btn){
24410         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24411         //if(Roo.isSafari){ // safari
24412         //    adjust *= 2;
24413        // }
24414         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24415         if(Roo.isSafari){ // safari
24416             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24417             v =  (v < 10) ? 10 : v;
24418             v =  (v > 48) ? 48 : v;
24419             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24420             
24421         }
24422         
24423         
24424         v = Math.max(1, v+adjust);
24425         
24426         this.execCmd('FontSize', v  );
24427     },
24428
24429     onEditorEvent : function(e)
24430     {
24431         this.owner.fireEvent('editorevent', this, e);
24432       //  this.updateToolbar();
24433         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24434     },
24435
24436     insertTag : function(tg)
24437     {
24438         // could be a bit smarter... -> wrap the current selected tRoo..
24439         if (tg.toLowerCase() == 'span' ||
24440             tg.toLowerCase() == 'code' ||
24441             tg.toLowerCase() == 'sup' ||
24442             tg.toLowerCase() == 'sub' 
24443             ) {
24444             
24445             range = this.createRange(this.getSelection());
24446             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24447             wrappingNode.appendChild(range.extractContents());
24448             range.insertNode(wrappingNode);
24449
24450             return;
24451             
24452             
24453             
24454         }
24455         this.execCmd("formatblock",   tg);
24456         
24457     },
24458     
24459     insertText : function(txt)
24460     {
24461         
24462         
24463         var range = this.createRange();
24464         range.deleteContents();
24465                //alert(Sender.getAttribute('label'));
24466                
24467         range.insertNode(this.doc.createTextNode(txt));
24468     } ,
24469     
24470      
24471
24472     /**
24473      * Executes a Midas editor command on the editor document and performs necessary focus and
24474      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24475      * @param {String} cmd The Midas command
24476      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24477      */
24478     relayCmd : function(cmd, value){
24479         this.win.focus();
24480         this.execCmd(cmd, value);
24481         this.owner.fireEvent('editorevent', this);
24482         //this.updateToolbar();
24483         this.owner.deferFocus();
24484     },
24485
24486     /**
24487      * Executes a Midas editor command directly on the editor document.
24488      * For visual commands, you should use {@link #relayCmd} instead.
24489      * <b>This should only be called after the editor is initialized.</b>
24490      * @param {String} cmd The Midas command
24491      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24492      */
24493     execCmd : function(cmd, value){
24494         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24495         this.syncValue();
24496     },
24497  
24498  
24499    
24500     /**
24501      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24502      * to insert tRoo.
24503      * @param {String} text | dom node.. 
24504      */
24505     insertAtCursor : function(text)
24506     {
24507         
24508         if(!this.activated){
24509             return;
24510         }
24511         /*
24512         if(Roo.isIE){
24513             this.win.focus();
24514             var r = this.doc.selection.createRange();
24515             if(r){
24516                 r.collapse(true);
24517                 r.pasteHTML(text);
24518                 this.syncValue();
24519                 this.deferFocus();
24520             
24521             }
24522             return;
24523         }
24524         */
24525         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24526             this.win.focus();
24527             
24528             
24529             // from jquery ui (MIT licenced)
24530             var range, node;
24531             var win = this.win;
24532             
24533             if (win.getSelection && win.getSelection().getRangeAt) {
24534                 range = win.getSelection().getRangeAt(0);
24535                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24536                 range.insertNode(node);
24537             } else if (win.document.selection && win.document.selection.createRange) {
24538                 // no firefox support
24539                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24540                 win.document.selection.createRange().pasteHTML(txt);
24541             } else {
24542                 // no firefox support
24543                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24544                 this.execCmd('InsertHTML', txt);
24545             } 
24546             
24547             this.syncValue();
24548             
24549             this.deferFocus();
24550         }
24551     },
24552  // private
24553     mozKeyPress : function(e){
24554         if(e.ctrlKey){
24555             var c = e.getCharCode(), cmd;
24556           
24557             if(c > 0){
24558                 c = String.fromCharCode(c).toLowerCase();
24559                 switch(c){
24560                     case 'b':
24561                         cmd = 'bold';
24562                         break;
24563                     case 'i':
24564                         cmd = 'italic';
24565                         break;
24566                     
24567                     case 'u':
24568                         cmd = 'underline';
24569                         break;
24570                     
24571                     case 'v':
24572                         this.cleanUpPaste.defer(100, this);
24573                         return;
24574                         
24575                 }
24576                 if(cmd){
24577                     this.win.focus();
24578                     this.execCmd(cmd);
24579                     this.deferFocus();
24580                     e.preventDefault();
24581                 }
24582                 
24583             }
24584         }
24585     },
24586
24587     // private
24588     fixKeys : function(){ // load time branching for fastest keydown performance
24589         if(Roo.isIE){
24590             return function(e){
24591                 var k = e.getKey(), r;
24592                 if(k == e.TAB){
24593                     e.stopEvent();
24594                     r = this.doc.selection.createRange();
24595                     if(r){
24596                         r.collapse(true);
24597                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24598                         this.deferFocus();
24599                     }
24600                     return;
24601                 }
24602                 
24603                 if(k == e.ENTER){
24604                     r = this.doc.selection.createRange();
24605                     if(r){
24606                         var target = r.parentElement();
24607                         if(!target || target.tagName.toLowerCase() != 'li'){
24608                             e.stopEvent();
24609                             r.pasteHTML('<br />');
24610                             r.collapse(false);
24611                             r.select();
24612                         }
24613                     }
24614                 }
24615                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24616                     this.cleanUpPaste.defer(100, this);
24617                     return;
24618                 }
24619                 
24620                 
24621             };
24622         }else if(Roo.isOpera){
24623             return function(e){
24624                 var k = e.getKey();
24625                 if(k == e.TAB){
24626                     e.stopEvent();
24627                     this.win.focus();
24628                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24629                     this.deferFocus();
24630                 }
24631                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24632                     this.cleanUpPaste.defer(100, this);
24633                     return;
24634                 }
24635                 
24636             };
24637         }else if(Roo.isSafari){
24638             return function(e){
24639                 var k = e.getKey();
24640                 
24641                 if(k == e.TAB){
24642                     e.stopEvent();
24643                     this.execCmd('InsertText','\t');
24644                     this.deferFocus();
24645                     return;
24646                 }
24647                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24648                     this.cleanUpPaste.defer(100, this);
24649                     return;
24650                 }
24651                 
24652              };
24653         }
24654     }(),
24655     
24656     getAllAncestors: function()
24657     {
24658         var p = this.getSelectedNode();
24659         var a = [];
24660         if (!p) {
24661             a.push(p); // push blank onto stack..
24662             p = this.getParentElement();
24663         }
24664         
24665         
24666         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24667             a.push(p);
24668             p = p.parentNode;
24669         }
24670         a.push(this.doc.body);
24671         return a;
24672     },
24673     lastSel : false,
24674     lastSelNode : false,
24675     
24676     
24677     getSelection : function() 
24678     {
24679         this.assignDocWin();
24680         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24681     },
24682     
24683     getSelectedNode: function() 
24684     {
24685         // this may only work on Gecko!!!
24686         
24687         // should we cache this!!!!
24688         
24689         
24690         
24691          
24692         var range = this.createRange(this.getSelection()).cloneRange();
24693         
24694         if (Roo.isIE) {
24695             var parent = range.parentElement();
24696             while (true) {
24697                 var testRange = range.duplicate();
24698                 testRange.moveToElementText(parent);
24699                 if (testRange.inRange(range)) {
24700                     break;
24701                 }
24702                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24703                     break;
24704                 }
24705                 parent = parent.parentElement;
24706             }
24707             return parent;
24708         }
24709         
24710         // is ancestor a text element.
24711         var ac =  range.commonAncestorContainer;
24712         if (ac.nodeType == 3) {
24713             ac = ac.parentNode;
24714         }
24715         
24716         var ar = ac.childNodes;
24717          
24718         var nodes = [];
24719         var other_nodes = [];
24720         var has_other_nodes = false;
24721         for (var i=0;i<ar.length;i++) {
24722             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24723                 continue;
24724             }
24725             // fullly contained node.
24726             
24727             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24728                 nodes.push(ar[i]);
24729                 continue;
24730             }
24731             
24732             // probably selected..
24733             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24734                 other_nodes.push(ar[i]);
24735                 continue;
24736             }
24737             // outer..
24738             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24739                 continue;
24740             }
24741             
24742             
24743             has_other_nodes = true;
24744         }
24745         if (!nodes.length && other_nodes.length) {
24746             nodes= other_nodes;
24747         }
24748         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24749             return false;
24750         }
24751         
24752         return nodes[0];
24753     },
24754     createRange: function(sel)
24755     {
24756         // this has strange effects when using with 
24757         // top toolbar - not sure if it's a great idea.
24758         //this.editor.contentWindow.focus();
24759         if (typeof sel != "undefined") {
24760             try {
24761                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24762             } catch(e) {
24763                 return this.doc.createRange();
24764             }
24765         } else {
24766             return this.doc.createRange();
24767         }
24768     },
24769     getParentElement: function()
24770     {
24771         
24772         this.assignDocWin();
24773         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24774         
24775         var range = this.createRange(sel);
24776          
24777         try {
24778             var p = range.commonAncestorContainer;
24779             while (p.nodeType == 3) { // text node
24780                 p = p.parentNode;
24781             }
24782             return p;
24783         } catch (e) {
24784             return null;
24785         }
24786     
24787     },
24788     /***
24789      *
24790      * Range intersection.. the hard stuff...
24791      *  '-1' = before
24792      *  '0' = hits..
24793      *  '1' = after.
24794      *         [ -- selected range --- ]
24795      *   [fail]                        [fail]
24796      *
24797      *    basically..
24798      *      if end is before start or  hits it. fail.
24799      *      if start is after end or hits it fail.
24800      *
24801      *   if either hits (but other is outside. - then it's not 
24802      *   
24803      *    
24804      **/
24805     
24806     
24807     // @see http://www.thismuchiknow.co.uk/?p=64.
24808     rangeIntersectsNode : function(range, node)
24809     {
24810         var nodeRange = node.ownerDocument.createRange();
24811         try {
24812             nodeRange.selectNode(node);
24813         } catch (e) {
24814             nodeRange.selectNodeContents(node);
24815         }
24816     
24817         var rangeStartRange = range.cloneRange();
24818         rangeStartRange.collapse(true);
24819     
24820         var rangeEndRange = range.cloneRange();
24821         rangeEndRange.collapse(false);
24822     
24823         var nodeStartRange = nodeRange.cloneRange();
24824         nodeStartRange.collapse(true);
24825     
24826         var nodeEndRange = nodeRange.cloneRange();
24827         nodeEndRange.collapse(false);
24828     
24829         return rangeStartRange.compareBoundaryPoints(
24830                  Range.START_TO_START, nodeEndRange) == -1 &&
24831                rangeEndRange.compareBoundaryPoints(
24832                  Range.START_TO_START, nodeStartRange) == 1;
24833         
24834          
24835     },
24836     rangeCompareNode : function(range, node)
24837     {
24838         var nodeRange = node.ownerDocument.createRange();
24839         try {
24840             nodeRange.selectNode(node);
24841         } catch (e) {
24842             nodeRange.selectNodeContents(node);
24843         }
24844         
24845         
24846         range.collapse(true);
24847     
24848         nodeRange.collapse(true);
24849      
24850         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24851         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24852          
24853         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24854         
24855         var nodeIsBefore   =  ss == 1;
24856         var nodeIsAfter    = ee == -1;
24857         
24858         if (nodeIsBefore && nodeIsAfter) {
24859             return 0; // outer
24860         }
24861         if (!nodeIsBefore && nodeIsAfter) {
24862             return 1; //right trailed.
24863         }
24864         
24865         if (nodeIsBefore && !nodeIsAfter) {
24866             return 2;  // left trailed.
24867         }
24868         // fully contined.
24869         return 3;
24870     },
24871
24872     // private? - in a new class?
24873     cleanUpPaste :  function()
24874     {
24875         // cleans up the whole document..
24876         Roo.log('cleanuppaste');
24877         
24878         this.cleanUpChildren(this.doc.body);
24879         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24880         if (clean != this.doc.body.innerHTML) {
24881             this.doc.body.innerHTML = clean;
24882         }
24883         
24884     },
24885     
24886     cleanWordChars : function(input) {// change the chars to hex code
24887         var he = Roo.HtmlEditorCore;
24888         
24889         var output = input;
24890         Roo.each(he.swapCodes, function(sw) { 
24891             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24892             
24893             output = output.replace(swapper, sw[1]);
24894         });
24895         
24896         return output;
24897     },
24898     
24899     
24900     cleanUpChildren : function (n)
24901     {
24902         if (!n.childNodes.length) {
24903             return;
24904         }
24905         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24906            this.cleanUpChild(n.childNodes[i]);
24907         }
24908     },
24909     
24910     
24911         
24912     
24913     cleanUpChild : function (node)
24914     {
24915         var ed = this;
24916         //console.log(node);
24917         if (node.nodeName == "#text") {
24918             // clean up silly Windows -- stuff?
24919             return; 
24920         }
24921         if (node.nodeName == "#comment") {
24922             node.parentNode.removeChild(node);
24923             // clean up silly Windows -- stuff?
24924             return; 
24925         }
24926         var lcname = node.tagName.toLowerCase();
24927         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24928         // whitelist of tags..
24929         
24930         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24931             // remove node.
24932             node.parentNode.removeChild(node);
24933             return;
24934             
24935         }
24936         
24937         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24938         
24939         // spans with no attributes - just remove them..
24940         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24941             remove_keep_children = true;
24942         }
24943         
24944         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24945         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24946         
24947         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24948         //    remove_keep_children = true;
24949         //}
24950         
24951         if (remove_keep_children) {
24952             this.cleanUpChildren(node);
24953             // inserts everything just before this node...
24954             while (node.childNodes.length) {
24955                 var cn = node.childNodes[0];
24956                 node.removeChild(cn);
24957                 node.parentNode.insertBefore(cn, node);
24958             }
24959             node.parentNode.removeChild(node);
24960             return;
24961         }
24962         
24963         if (!node.attributes || !node.attributes.length) {
24964             
24965           
24966             
24967             
24968             this.cleanUpChildren(node);
24969             return;
24970         }
24971         
24972         function cleanAttr(n,v)
24973         {
24974             
24975             if (v.match(/^\./) || v.match(/^\//)) {
24976                 return;
24977             }
24978             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24979                 return;
24980             }
24981             if (v.match(/^#/)) {
24982                 return;
24983             }
24984             if (v.match(/^\{/)) { // allow template editing.
24985                 return;
24986             }
24987 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24988             node.removeAttribute(n);
24989             
24990         }
24991         
24992         var cwhite = this.cwhite;
24993         var cblack = this.cblack;
24994             
24995         function cleanStyle(n,v)
24996         {
24997             if (v.match(/expression/)) { //XSS?? should we even bother..
24998                 node.removeAttribute(n);
24999                 return;
25000             }
25001             
25002             var parts = v.split(/;/);
25003             var clean = [];
25004             
25005             Roo.each(parts, function(p) {
25006                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25007                 if (!p.length) {
25008                     return true;
25009                 }
25010                 var l = p.split(':').shift().replace(/\s+/g,'');
25011                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25012                 
25013                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25014 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25015                     //node.removeAttribute(n);
25016                     return true;
25017                 }
25018                 //Roo.log()
25019                 // only allow 'c whitelisted system attributes'
25020                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25022                     //node.removeAttribute(n);
25023                     return true;
25024                 }
25025                 
25026                 
25027                  
25028                 
25029                 clean.push(p);
25030                 return true;
25031             });
25032             if (clean.length) { 
25033                 node.setAttribute(n, clean.join(';'));
25034             } else {
25035                 node.removeAttribute(n);
25036             }
25037             
25038         }
25039         
25040         
25041         for (var i = node.attributes.length-1; i > -1 ; i--) {
25042             var a = node.attributes[i];
25043             //console.log(a);
25044             
25045             if (a.name.toLowerCase().substr(0,2)=='on')  {
25046                 node.removeAttribute(a.name);
25047                 continue;
25048             }
25049             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25050                 node.removeAttribute(a.name);
25051                 continue;
25052             }
25053             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25054                 cleanAttr(a.name,a.value); // fixme..
25055                 continue;
25056             }
25057             if (a.name == 'style') {
25058                 cleanStyle(a.name,a.value);
25059                 continue;
25060             }
25061             /// clean up MS crap..
25062             // tecnically this should be a list of valid class'es..
25063             
25064             
25065             if (a.name == 'class') {
25066                 if (a.value.match(/^Mso/)) {
25067                     node.removeAttribute('class');
25068                 }
25069                 
25070                 if (a.value.match(/^body$/)) {
25071                     node.removeAttribute('class');
25072                 }
25073                 continue;
25074             }
25075             
25076             // style cleanup!?
25077             // class cleanup?
25078             
25079         }
25080         
25081         
25082         this.cleanUpChildren(node);
25083         
25084         
25085     },
25086     
25087     /**
25088      * Clean up MS wordisms...
25089      */
25090     cleanWord : function(node)
25091     {
25092         if (!node) {
25093             this.cleanWord(this.doc.body);
25094             return;
25095         }
25096         
25097         if(
25098                 node.nodeName == 'SPAN' &&
25099                 !node.hasAttributes() &&
25100                 node.childNodes.length == 1 &&
25101                 node.firstChild.nodeName == "#text"  
25102         ) {
25103             var textNode = node.firstChild;
25104             node.removeChild(textNode);
25105             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25106                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25107             }
25108             node.parentNode.insertBefore(textNode, node);
25109             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25110                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25111             }
25112             node.parentNode.removeChild(node);
25113         }
25114         
25115         if (node.nodeName == "#text") {
25116             // clean up silly Windows -- stuff?
25117             return; 
25118         }
25119         if (node.nodeName == "#comment") {
25120             node.parentNode.removeChild(node);
25121             // clean up silly Windows -- stuff?
25122             return; 
25123         }
25124         
25125         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25126             node.parentNode.removeChild(node);
25127             return;
25128         }
25129         //Roo.log(node.tagName);
25130         // remove - but keep children..
25131         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25132             //Roo.log('-- removed');
25133             while (node.childNodes.length) {
25134                 var cn = node.childNodes[0];
25135                 node.removeChild(cn);
25136                 node.parentNode.insertBefore(cn, node);
25137                 // move node to parent - and clean it..
25138                 this.cleanWord(cn);
25139             }
25140             node.parentNode.removeChild(node);
25141             /// no need to iterate chidlren = it's got none..
25142             //this.iterateChildren(node, this.cleanWord);
25143             return;
25144         }
25145         // clean styles
25146         if (node.className.length) {
25147             
25148             var cn = node.className.split(/\W+/);
25149             var cna = [];
25150             Roo.each(cn, function(cls) {
25151                 if (cls.match(/Mso[a-zA-Z]+/)) {
25152                     return;
25153                 }
25154                 cna.push(cls);
25155             });
25156             node.className = cna.length ? cna.join(' ') : '';
25157             if (!cna.length) {
25158                 node.removeAttribute("class");
25159             }
25160         }
25161         
25162         if (node.hasAttribute("lang")) {
25163             node.removeAttribute("lang");
25164         }
25165         
25166         if (node.hasAttribute("style")) {
25167             
25168             var styles = node.getAttribute("style").split(";");
25169             var nstyle = [];
25170             Roo.each(styles, function(s) {
25171                 if (!s.match(/:/)) {
25172                     return;
25173                 }
25174                 var kv = s.split(":");
25175                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25176                     return;
25177                 }
25178                 // what ever is left... we allow.
25179                 nstyle.push(s);
25180             });
25181             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25182             if (!nstyle.length) {
25183                 node.removeAttribute('style');
25184             }
25185         }
25186         this.iterateChildren(node, this.cleanWord);
25187         
25188         
25189         
25190     },
25191     /**
25192      * iterateChildren of a Node, calling fn each time, using this as the scole..
25193      * @param {DomNode} node node to iterate children of.
25194      * @param {Function} fn method of this class to call on each item.
25195      */
25196     iterateChildren : function(node, fn)
25197     {
25198         if (!node.childNodes.length) {
25199                 return;
25200         }
25201         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25202            fn.call(this, node.childNodes[i])
25203         }
25204     },
25205     
25206     
25207     /**
25208      * cleanTableWidths.
25209      *
25210      * Quite often pasting from word etc.. results in tables with column and widths.
25211      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25212      *
25213      */
25214     cleanTableWidths : function(node)
25215     {
25216          
25217          
25218         if (!node) {
25219             this.cleanTableWidths(this.doc.body);
25220             return;
25221         }
25222         
25223         // ignore list...
25224         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25225             return; 
25226         }
25227         Roo.log(node.tagName);
25228         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25229             this.iterateChildren(node, this.cleanTableWidths);
25230             return;
25231         }
25232         if (node.hasAttribute('width')) {
25233             node.removeAttribute('width');
25234         }
25235         
25236          
25237         if (node.hasAttribute("style")) {
25238             // pretty basic...
25239             
25240             var styles = node.getAttribute("style").split(";");
25241             var nstyle = [];
25242             Roo.each(styles, function(s) {
25243                 if (!s.match(/:/)) {
25244                     return;
25245                 }
25246                 var kv = s.split(":");
25247                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25248                     return;
25249                 }
25250                 // what ever is left... we allow.
25251                 nstyle.push(s);
25252             });
25253             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25254             if (!nstyle.length) {
25255                 node.removeAttribute('style');
25256             }
25257         }
25258         
25259         this.iterateChildren(node, this.cleanTableWidths);
25260         
25261         
25262     },
25263     
25264     
25265     
25266     
25267     domToHTML : function(currentElement, depth, nopadtext) {
25268         
25269         depth = depth || 0;
25270         nopadtext = nopadtext || false;
25271     
25272         if (!currentElement) {
25273             return this.domToHTML(this.doc.body);
25274         }
25275         
25276         //Roo.log(currentElement);
25277         var j;
25278         var allText = false;
25279         var nodeName = currentElement.nodeName;
25280         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25281         
25282         if  (nodeName == '#text') {
25283             
25284             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25285         }
25286         
25287         
25288         var ret = '';
25289         if (nodeName != 'BODY') {
25290              
25291             var i = 0;
25292             // Prints the node tagName, such as <A>, <IMG>, etc
25293             if (tagName) {
25294                 var attr = [];
25295                 for(i = 0; i < currentElement.attributes.length;i++) {
25296                     // quoting?
25297                     var aname = currentElement.attributes.item(i).name;
25298                     if (!currentElement.attributes.item(i).value.length) {
25299                         continue;
25300                     }
25301                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25302                 }
25303                 
25304                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25305             } 
25306             else {
25307                 
25308                 // eack
25309             }
25310         } else {
25311             tagName = false;
25312         }
25313         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25314             return ret;
25315         }
25316         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25317             nopadtext = true;
25318         }
25319         
25320         
25321         // Traverse the tree
25322         i = 0;
25323         var currentElementChild = currentElement.childNodes.item(i);
25324         var allText = true;
25325         var innerHTML  = '';
25326         lastnode = '';
25327         while (currentElementChild) {
25328             // Formatting code (indent the tree so it looks nice on the screen)
25329             var nopad = nopadtext;
25330             if (lastnode == 'SPAN') {
25331                 nopad  = true;
25332             }
25333             // text
25334             if  (currentElementChild.nodeName == '#text') {
25335                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25336                 toadd = nopadtext ? toadd : toadd.trim();
25337                 if (!nopad && toadd.length > 80) {
25338                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25339                 }
25340                 innerHTML  += toadd;
25341                 
25342                 i++;
25343                 currentElementChild = currentElement.childNodes.item(i);
25344                 lastNode = '';
25345                 continue;
25346             }
25347             allText = false;
25348             
25349             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25350                 
25351             // Recursively traverse the tree structure of the child node
25352             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25353             lastnode = currentElementChild.nodeName;
25354             i++;
25355             currentElementChild=currentElement.childNodes.item(i);
25356         }
25357         
25358         ret += innerHTML;
25359         
25360         if (!allText) {
25361                 // The remaining code is mostly for formatting the tree
25362             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25363         }
25364         
25365         
25366         if (tagName) {
25367             ret+= "</"+tagName+">";
25368         }
25369         return ret;
25370         
25371     },
25372         
25373     applyBlacklists : function()
25374     {
25375         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25376         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25377         
25378         this.white = [];
25379         this.black = [];
25380         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25381             if (b.indexOf(tag) > -1) {
25382                 return;
25383             }
25384             this.white.push(tag);
25385             
25386         }, this);
25387         
25388         Roo.each(w, function(tag) {
25389             if (b.indexOf(tag) > -1) {
25390                 return;
25391             }
25392             if (this.white.indexOf(tag) > -1) {
25393                 return;
25394             }
25395             this.white.push(tag);
25396             
25397         }, this);
25398         
25399         
25400         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25401             if (w.indexOf(tag) > -1) {
25402                 return;
25403             }
25404             this.black.push(tag);
25405             
25406         }, this);
25407         
25408         Roo.each(b, function(tag) {
25409             if (w.indexOf(tag) > -1) {
25410                 return;
25411             }
25412             if (this.black.indexOf(tag) > -1) {
25413                 return;
25414             }
25415             this.black.push(tag);
25416             
25417         }, this);
25418         
25419         
25420         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25421         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25422         
25423         this.cwhite = [];
25424         this.cblack = [];
25425         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25426             if (b.indexOf(tag) > -1) {
25427                 return;
25428             }
25429             this.cwhite.push(tag);
25430             
25431         }, this);
25432         
25433         Roo.each(w, function(tag) {
25434             if (b.indexOf(tag) > -1) {
25435                 return;
25436             }
25437             if (this.cwhite.indexOf(tag) > -1) {
25438                 return;
25439             }
25440             this.cwhite.push(tag);
25441             
25442         }, this);
25443         
25444         
25445         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25446             if (w.indexOf(tag) > -1) {
25447                 return;
25448             }
25449             this.cblack.push(tag);
25450             
25451         }, this);
25452         
25453         Roo.each(b, function(tag) {
25454             if (w.indexOf(tag) > -1) {
25455                 return;
25456             }
25457             if (this.cblack.indexOf(tag) > -1) {
25458                 return;
25459             }
25460             this.cblack.push(tag);
25461             
25462         }, this);
25463     },
25464     
25465     setStylesheets : function(stylesheets)
25466     {
25467         if(typeof(stylesheets) == 'string'){
25468             Roo.get(this.iframe.contentDocument.head).createChild({
25469                 tag : 'link',
25470                 rel : 'stylesheet',
25471                 type : 'text/css',
25472                 href : stylesheets
25473             });
25474             
25475             return;
25476         }
25477         var _this = this;
25478      
25479         Roo.each(stylesheets, function(s) {
25480             if(!s.length){
25481                 return;
25482             }
25483             
25484             Roo.get(_this.iframe.contentDocument.head).createChild({
25485                 tag : 'link',
25486                 rel : 'stylesheet',
25487                 type : 'text/css',
25488                 href : s
25489             });
25490         });
25491
25492         
25493     },
25494     
25495     removeStylesheets : function()
25496     {
25497         var _this = this;
25498         
25499         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25500             s.remove();
25501         });
25502     },
25503     
25504     setStyle : function(style)
25505     {
25506         Roo.get(this.iframe.contentDocument.head).createChild({
25507             tag : 'style',
25508             type : 'text/css',
25509             html : style
25510         });
25511
25512         return;
25513     }
25514     
25515     // hide stuff that is not compatible
25516     /**
25517      * @event blur
25518      * @hide
25519      */
25520     /**
25521      * @event change
25522      * @hide
25523      */
25524     /**
25525      * @event focus
25526      * @hide
25527      */
25528     /**
25529      * @event specialkey
25530      * @hide
25531      */
25532     /**
25533      * @cfg {String} fieldClass @hide
25534      */
25535     /**
25536      * @cfg {String} focusClass @hide
25537      */
25538     /**
25539      * @cfg {String} autoCreate @hide
25540      */
25541     /**
25542      * @cfg {String} inputType @hide
25543      */
25544     /**
25545      * @cfg {String} invalidClass @hide
25546      */
25547     /**
25548      * @cfg {String} invalidText @hide
25549      */
25550     /**
25551      * @cfg {String} msgFx @hide
25552      */
25553     /**
25554      * @cfg {String} validateOnBlur @hide
25555      */
25556 });
25557
25558 Roo.HtmlEditorCore.white = [
25559         'area', 'br', 'img', 'input', 'hr', 'wbr',
25560         
25561        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25562        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25563        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25564        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25565        'table',   'ul',         'xmp', 
25566        
25567        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25568       'thead',   'tr', 
25569      
25570       'dir', 'menu', 'ol', 'ul', 'dl',
25571        
25572       'embed',  'object'
25573 ];
25574
25575
25576 Roo.HtmlEditorCore.black = [
25577     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25578         'applet', // 
25579         'base',   'basefont', 'bgsound', 'blink',  'body', 
25580         'frame',  'frameset', 'head',    'html',   'ilayer', 
25581         'iframe', 'layer',  'link',     'meta',    'object',   
25582         'script', 'style' ,'title',  'xml' // clean later..
25583 ];
25584 Roo.HtmlEditorCore.clean = [
25585     'script', 'style', 'title', 'xml'
25586 ];
25587 Roo.HtmlEditorCore.remove = [
25588     'font'
25589 ];
25590 // attributes..
25591
25592 Roo.HtmlEditorCore.ablack = [
25593     'on'
25594 ];
25595     
25596 Roo.HtmlEditorCore.aclean = [ 
25597     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25598 ];
25599
25600 // protocols..
25601 Roo.HtmlEditorCore.pwhite= [
25602         'http',  'https',  'mailto'
25603 ];
25604
25605 // white listed style attributes.
25606 Roo.HtmlEditorCore.cwhite= [
25607       //  'text-align', /// default is to allow most things..
25608       
25609          
25610 //        'font-size'//??
25611 ];
25612
25613 // black listed style attributes.
25614 Roo.HtmlEditorCore.cblack= [
25615       //  'font-size' -- this can be set by the project 
25616 ];
25617
25618
25619 Roo.HtmlEditorCore.swapCodes   =[ 
25620     [    8211, "--" ], 
25621     [    8212, "--" ], 
25622     [    8216,  "'" ],  
25623     [    8217, "'" ],  
25624     [    8220, '"' ],  
25625     [    8221, '"' ],  
25626     [    8226, "*" ],  
25627     [    8230, "..." ]
25628 ]; 
25629
25630     /*
25631  * - LGPL
25632  *
25633  * HtmlEditor
25634  * 
25635  */
25636
25637 /**
25638  * @class Roo.bootstrap.HtmlEditor
25639  * @extends Roo.bootstrap.TextArea
25640  * Bootstrap HtmlEditor class
25641
25642  * @constructor
25643  * Create a new HtmlEditor
25644  * @param {Object} config The config object
25645  */
25646
25647 Roo.bootstrap.HtmlEditor = function(config){
25648     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25649     if (!this.toolbars) {
25650         this.toolbars = [];
25651     }
25652     
25653     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25654     this.addEvents({
25655             /**
25656              * @event initialize
25657              * Fires when the editor is fully initialized (including the iframe)
25658              * @param {HtmlEditor} this
25659              */
25660             initialize: true,
25661             /**
25662              * @event activate
25663              * Fires when the editor is first receives the focus. Any insertion must wait
25664              * until after this event.
25665              * @param {HtmlEditor} this
25666              */
25667             activate: true,
25668              /**
25669              * @event beforesync
25670              * Fires before the textarea is updated with content from the editor iframe. Return false
25671              * to cancel the sync.
25672              * @param {HtmlEditor} this
25673              * @param {String} html
25674              */
25675             beforesync: true,
25676              /**
25677              * @event beforepush
25678              * Fires before the iframe editor is updated with content from the textarea. Return false
25679              * to cancel the push.
25680              * @param {HtmlEditor} this
25681              * @param {String} html
25682              */
25683             beforepush: true,
25684              /**
25685              * @event sync
25686              * Fires when the textarea is updated with content from the editor iframe.
25687              * @param {HtmlEditor} this
25688              * @param {String} html
25689              */
25690             sync: true,
25691              /**
25692              * @event push
25693              * Fires when the iframe editor is updated with content from the textarea.
25694              * @param {HtmlEditor} this
25695              * @param {String} html
25696              */
25697             push: true,
25698              /**
25699              * @event editmodechange
25700              * Fires when the editor switches edit modes
25701              * @param {HtmlEditor} this
25702              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25703              */
25704             editmodechange: true,
25705             /**
25706              * @event editorevent
25707              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25708              * @param {HtmlEditor} this
25709              */
25710             editorevent: true,
25711             /**
25712              * @event firstfocus
25713              * Fires when on first focus - needed by toolbars..
25714              * @param {HtmlEditor} this
25715              */
25716             firstfocus: true,
25717             /**
25718              * @event autosave
25719              * Auto save the htmlEditor value as a file into Events
25720              * @param {HtmlEditor} this
25721              */
25722             autosave: true,
25723             /**
25724              * @event savedpreview
25725              * preview the saved version of htmlEditor
25726              * @param {HtmlEditor} this
25727              */
25728             savedpreview: true
25729         });
25730 };
25731
25732
25733 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25734     
25735     
25736       /**
25737      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25738      */
25739     toolbars : false,
25740     
25741      /**
25742     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25743     */
25744     btns : [],
25745    
25746      /**
25747      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25748      *                        Roo.resizable.
25749      */
25750     resizable : false,
25751      /**
25752      * @cfg {Number} height (in pixels)
25753      */   
25754     height: 300,
25755    /**
25756      * @cfg {Number} width (in pixels)
25757      */   
25758     width: false,
25759     
25760     /**
25761      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25762      * 
25763      */
25764     stylesheets: false,
25765     
25766     // id of frame..
25767     frameId: false,
25768     
25769     // private properties
25770     validationEvent : false,
25771     deferHeight: true,
25772     initialized : false,
25773     activated : false,
25774     
25775     onFocus : Roo.emptyFn,
25776     iframePad:3,
25777     hideMode:'offsets',
25778     
25779     tbContainer : false,
25780     
25781     bodyCls : '',
25782     
25783     toolbarContainer :function() {
25784         return this.wrap.select('.x-html-editor-tb',true).first();
25785     },
25786
25787     /**
25788      * Protected method that will not generally be called directly. It
25789      * is called when the editor creates its toolbar. Override this method if you need to
25790      * add custom toolbar buttons.
25791      * @param {HtmlEditor} editor
25792      */
25793     createToolbar : function(){
25794         Roo.log('renewing');
25795         Roo.log("create toolbars");
25796         
25797         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25798         this.toolbars[0].render(this.toolbarContainer());
25799         
25800         return;
25801         
25802 //        if (!editor.toolbars || !editor.toolbars.length) {
25803 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25804 //        }
25805 //        
25806 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25807 //            editor.toolbars[i] = Roo.factory(
25808 //                    typeof(editor.toolbars[i]) == 'string' ?
25809 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25810 //                Roo.bootstrap.HtmlEditor);
25811 //            editor.toolbars[i].init(editor);
25812 //        }
25813     },
25814
25815      
25816     // private
25817     onRender : function(ct, position)
25818     {
25819        // Roo.log("Call onRender: " + this.xtype);
25820         var _t = this;
25821         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25822       
25823         this.wrap = this.inputEl().wrap({
25824             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25825         });
25826         
25827         this.editorcore.onRender(ct, position);
25828          
25829         if (this.resizable) {
25830             this.resizeEl = new Roo.Resizable(this.wrap, {
25831                 pinned : true,
25832                 wrap: true,
25833                 dynamic : true,
25834                 minHeight : this.height,
25835                 height: this.height,
25836                 handles : this.resizable,
25837                 width: this.width,
25838                 listeners : {
25839                     resize : function(r, w, h) {
25840                         _t.onResize(w,h); // -something
25841                     }
25842                 }
25843             });
25844             
25845         }
25846         this.createToolbar(this);
25847        
25848         
25849         if(!this.width && this.resizable){
25850             this.setSize(this.wrap.getSize());
25851         }
25852         if (this.resizeEl) {
25853             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25854             // should trigger onReize..
25855         }
25856         
25857     },
25858
25859     // private
25860     onResize : function(w, h)
25861     {
25862         Roo.log('resize: ' +w + ',' + h );
25863         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25864         var ew = false;
25865         var eh = false;
25866         
25867         if(this.inputEl() ){
25868             if(typeof w == 'number'){
25869                 var aw = w - this.wrap.getFrameWidth('lr');
25870                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25871                 ew = aw;
25872             }
25873             if(typeof h == 'number'){
25874                  var tbh = -11;  // fixme it needs to tool bar size!
25875                 for (var i =0; i < this.toolbars.length;i++) {
25876                     // fixme - ask toolbars for heights?
25877                     tbh += this.toolbars[i].el.getHeight();
25878                     //if (this.toolbars[i].footer) {
25879                     //    tbh += this.toolbars[i].footer.el.getHeight();
25880                     //}
25881                 }
25882               
25883                 
25884                 
25885                 
25886                 
25887                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25888                 ah -= 5; // knock a few pixes off for look..
25889                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25890                 var eh = ah;
25891             }
25892         }
25893         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25894         this.editorcore.onResize(ew,eh);
25895         
25896     },
25897
25898     /**
25899      * Toggles the editor between standard and source edit mode.
25900      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25901      */
25902     toggleSourceEdit : function(sourceEditMode)
25903     {
25904         this.editorcore.toggleSourceEdit(sourceEditMode);
25905         
25906         if(this.editorcore.sourceEditMode){
25907             Roo.log('editor - showing textarea');
25908             
25909 //            Roo.log('in');
25910 //            Roo.log(this.syncValue());
25911             this.syncValue();
25912             this.inputEl().removeClass(['hide', 'x-hidden']);
25913             this.inputEl().dom.removeAttribute('tabIndex');
25914             this.inputEl().focus();
25915         }else{
25916             Roo.log('editor - hiding textarea');
25917 //            Roo.log('out')
25918 //            Roo.log(this.pushValue()); 
25919             this.pushValue();
25920             
25921             this.inputEl().addClass(['hide', 'x-hidden']);
25922             this.inputEl().dom.setAttribute('tabIndex', -1);
25923             //this.deferFocus();
25924         }
25925          
25926         if(this.resizable){
25927             this.setSize(this.wrap.getSize());
25928         }
25929         
25930         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25931     },
25932  
25933     // private (for BoxComponent)
25934     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25935
25936     // private (for BoxComponent)
25937     getResizeEl : function(){
25938         return this.wrap;
25939     },
25940
25941     // private (for BoxComponent)
25942     getPositionEl : function(){
25943         return this.wrap;
25944     },
25945
25946     // private
25947     initEvents : function(){
25948         this.originalValue = this.getValue();
25949     },
25950
25951 //    /**
25952 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25953 //     * @method
25954 //     */
25955 //    markInvalid : Roo.emptyFn,
25956 //    /**
25957 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25958 //     * @method
25959 //     */
25960 //    clearInvalid : Roo.emptyFn,
25961
25962     setValue : function(v){
25963         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25964         this.editorcore.pushValue();
25965     },
25966
25967      
25968     // private
25969     deferFocus : function(){
25970         this.focus.defer(10, this);
25971     },
25972
25973     // doc'ed in Field
25974     focus : function(){
25975         this.editorcore.focus();
25976         
25977     },
25978       
25979
25980     // private
25981     onDestroy : function(){
25982         
25983         
25984         
25985         if(this.rendered){
25986             
25987             for (var i =0; i < this.toolbars.length;i++) {
25988                 // fixme - ask toolbars for heights?
25989                 this.toolbars[i].onDestroy();
25990             }
25991             
25992             this.wrap.dom.innerHTML = '';
25993             this.wrap.remove();
25994         }
25995     },
25996
25997     // private
25998     onFirstFocus : function(){
25999         //Roo.log("onFirstFocus");
26000         this.editorcore.onFirstFocus();
26001          for (var i =0; i < this.toolbars.length;i++) {
26002             this.toolbars[i].onFirstFocus();
26003         }
26004         
26005     },
26006     
26007     // private
26008     syncValue : function()
26009     {   
26010         this.editorcore.syncValue();
26011     },
26012     
26013     pushValue : function()
26014     {   
26015         this.editorcore.pushValue();
26016     }
26017      
26018     
26019     // hide stuff that is not compatible
26020     /**
26021      * @event blur
26022      * @hide
26023      */
26024     /**
26025      * @event change
26026      * @hide
26027      */
26028     /**
26029      * @event focus
26030      * @hide
26031      */
26032     /**
26033      * @event specialkey
26034      * @hide
26035      */
26036     /**
26037      * @cfg {String} fieldClass @hide
26038      */
26039     /**
26040      * @cfg {String} focusClass @hide
26041      */
26042     /**
26043      * @cfg {String} autoCreate @hide
26044      */
26045     /**
26046      * @cfg {String} inputType @hide
26047      */
26048      
26049     /**
26050      * @cfg {String} invalidText @hide
26051      */
26052     /**
26053      * @cfg {String} msgFx @hide
26054      */
26055     /**
26056      * @cfg {String} validateOnBlur @hide
26057      */
26058 });
26059  
26060     
26061    
26062    
26063    
26064       
26065 Roo.namespace('Roo.bootstrap.htmleditor');
26066 /**
26067  * @class Roo.bootstrap.HtmlEditorToolbar1
26068  * Basic Toolbar
26069  * 
26070  * @example
26071  * Usage:
26072  *
26073  new Roo.bootstrap.HtmlEditor({
26074     ....
26075     toolbars : [
26076         new Roo.bootstrap.HtmlEditorToolbar1({
26077             disable : { fonts: 1 , format: 1, ..., ... , ...],
26078             btns : [ .... ]
26079         })
26080     }
26081      
26082  * 
26083  * @cfg {Object} disable List of elements to disable..
26084  * @cfg {Array} btns List of additional buttons.
26085  * 
26086  * 
26087  * NEEDS Extra CSS? 
26088  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26089  */
26090  
26091 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26092 {
26093     
26094     Roo.apply(this, config);
26095     
26096     // default disabled, based on 'good practice'..
26097     this.disable = this.disable || {};
26098     Roo.applyIf(this.disable, {
26099         fontSize : true,
26100         colors : true,
26101         specialElements : true
26102     });
26103     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26104     
26105     this.editor = config.editor;
26106     this.editorcore = config.editor.editorcore;
26107     
26108     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26109     
26110     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26111     // dont call parent... till later.
26112 }
26113 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26114      
26115     bar : true,
26116     
26117     editor : false,
26118     editorcore : false,
26119     
26120     
26121     formats : [
26122         "p" ,  
26123         "h1","h2","h3","h4","h5","h6", 
26124         "pre", "code", 
26125         "abbr", "acronym", "address", "cite", "samp", "var",
26126         'div','span'
26127     ],
26128     
26129     onRender : function(ct, position)
26130     {
26131        // Roo.log("Call onRender: " + this.xtype);
26132         
26133        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26134        Roo.log(this.el);
26135        this.el.dom.style.marginBottom = '0';
26136        var _this = this;
26137        var editorcore = this.editorcore;
26138        var editor= this.editor;
26139        
26140        var children = [];
26141        var btn = function(id,cmd , toggle, handler, html){
26142        
26143             var  event = toggle ? 'toggle' : 'click';
26144        
26145             var a = {
26146                 size : 'sm',
26147                 xtype: 'Button',
26148                 xns: Roo.bootstrap,
26149                 //glyphicon : id,
26150                 fa: id,
26151                 cmd : id || cmd,
26152                 enableToggle:toggle !== false,
26153                 html : html || '',
26154                 pressed : toggle ? false : null,
26155                 listeners : {}
26156             };
26157             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26158                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26159             };
26160             children.push(a);
26161             return a;
26162        }
26163        
26164     //    var cb_box = function...
26165         
26166         var style = {
26167                 xtype: 'Button',
26168                 size : 'sm',
26169                 xns: Roo.bootstrap,
26170                 fa : 'font',
26171                 //html : 'submit'
26172                 menu : {
26173                     xtype: 'Menu',
26174                     xns: Roo.bootstrap,
26175                     items:  []
26176                 }
26177         };
26178         Roo.each(this.formats, function(f) {
26179             style.menu.items.push({
26180                 xtype :'MenuItem',
26181                 xns: Roo.bootstrap,
26182                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26183                 tagname : f,
26184                 listeners : {
26185                     click : function()
26186                     {
26187                         editorcore.insertTag(this.tagname);
26188                         editor.focus();
26189                     }
26190                 }
26191                 
26192             });
26193         });
26194         children.push(style);   
26195         
26196         btn('bold',false,true);
26197         btn('italic',false,true);
26198         btn('align-left', 'justifyleft',true);
26199         btn('align-center', 'justifycenter',true);
26200         btn('align-right' , 'justifyright',true);
26201         btn('link', false, false, function(btn) {
26202             //Roo.log("create link?");
26203             var url = prompt(this.createLinkText, this.defaultLinkValue);
26204             if(url && url != 'http:/'+'/'){
26205                 this.editorcore.relayCmd('createlink', url);
26206             }
26207         }),
26208         btn('list','insertunorderedlist',true);
26209         btn('pencil', false,true, function(btn){
26210                 Roo.log(this);
26211                 this.toggleSourceEdit(btn.pressed);
26212         });
26213         
26214         if (this.editor.btns.length > 0) {
26215             for (var i = 0; i<this.editor.btns.length; i++) {
26216                 children.push(this.editor.btns[i]);
26217             }
26218         }
26219         
26220         /*
26221         var cog = {
26222                 xtype: 'Button',
26223                 size : 'sm',
26224                 xns: Roo.bootstrap,
26225                 glyphicon : 'cog',
26226                 //html : 'submit'
26227                 menu : {
26228                     xtype: 'Menu',
26229                     xns: Roo.bootstrap,
26230                     items:  []
26231                 }
26232         };
26233         
26234         cog.menu.items.push({
26235             xtype :'MenuItem',
26236             xns: Roo.bootstrap,
26237             html : Clean styles,
26238             tagname : f,
26239             listeners : {
26240                 click : function()
26241                 {
26242                     editorcore.insertTag(this.tagname);
26243                     editor.focus();
26244                 }
26245             }
26246             
26247         });
26248        */
26249         
26250          
26251        this.xtype = 'NavSimplebar';
26252         
26253         for(var i=0;i< children.length;i++) {
26254             
26255             this.buttons.add(this.addxtypeChild(children[i]));
26256             
26257         }
26258         
26259         editor.on('editorevent', this.updateToolbar, this);
26260     },
26261     onBtnClick : function(id)
26262     {
26263        this.editorcore.relayCmd(id);
26264        this.editorcore.focus();
26265     },
26266     
26267     /**
26268      * Protected method that will not generally be called directly. It triggers
26269      * a toolbar update by reading the markup state of the current selection in the editor.
26270      */
26271     updateToolbar: function(){
26272
26273         if(!this.editorcore.activated){
26274             this.editor.onFirstFocus(); // is this neeed?
26275             return;
26276         }
26277
26278         var btns = this.buttons; 
26279         var doc = this.editorcore.doc;
26280         btns.get('bold').setActive(doc.queryCommandState('bold'));
26281         btns.get('italic').setActive(doc.queryCommandState('italic'));
26282         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26283         
26284         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26285         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26286         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26287         
26288         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26289         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26290          /*
26291         
26292         var ans = this.editorcore.getAllAncestors();
26293         if (this.formatCombo) {
26294             
26295             
26296             var store = this.formatCombo.store;
26297             this.formatCombo.setValue("");
26298             for (var i =0; i < ans.length;i++) {
26299                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26300                     // select it..
26301                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26302                     break;
26303                 }
26304             }
26305         }
26306         
26307         
26308         
26309         // hides menus... - so this cant be on a menu...
26310         Roo.bootstrap.MenuMgr.hideAll();
26311         */
26312         Roo.bootstrap.MenuMgr.hideAll();
26313         //this.editorsyncValue();
26314     },
26315     onFirstFocus: function() {
26316         this.buttons.each(function(item){
26317            item.enable();
26318         });
26319     },
26320     toggleSourceEdit : function(sourceEditMode){
26321         
26322           
26323         if(sourceEditMode){
26324             Roo.log("disabling buttons");
26325            this.buttons.each( function(item){
26326                 if(item.cmd != 'pencil'){
26327                     item.disable();
26328                 }
26329             });
26330           
26331         }else{
26332             Roo.log("enabling buttons");
26333             if(this.editorcore.initialized){
26334                 this.buttons.each( function(item){
26335                     item.enable();
26336                 });
26337             }
26338             
26339         }
26340         Roo.log("calling toggole on editor");
26341         // tell the editor that it's been pressed..
26342         this.editor.toggleSourceEdit(sourceEditMode);
26343        
26344     }
26345 });
26346
26347
26348
26349
26350  
26351 /*
26352  * - LGPL
26353  */
26354
26355 /**
26356  * @class Roo.bootstrap.Markdown
26357  * @extends Roo.bootstrap.TextArea
26358  * Bootstrap Showdown editable area
26359  * @cfg {string} content
26360  * 
26361  * @constructor
26362  * Create a new Showdown
26363  */
26364
26365 Roo.bootstrap.Markdown = function(config){
26366     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26367    
26368 };
26369
26370 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26371     
26372     editing :false,
26373     
26374     initEvents : function()
26375     {
26376         
26377         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26378         this.markdownEl = this.el.createChild({
26379             cls : 'roo-markdown-area'
26380         });
26381         this.inputEl().addClass('d-none');
26382         if (this.getValue() == '') {
26383             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26384             
26385         } else {
26386             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26387         }
26388         this.markdownEl.on('click', this.toggleTextEdit, this);
26389         this.on('blur', this.toggleTextEdit, this);
26390         this.on('specialkey', this.resizeTextArea, this);
26391     },
26392     
26393     toggleTextEdit : function()
26394     {
26395         var sh = this.markdownEl.getHeight();
26396         this.inputEl().addClass('d-none');
26397         this.markdownEl.addClass('d-none');
26398         if (!this.editing) {
26399             // show editor?
26400             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26401             this.inputEl().removeClass('d-none');
26402             this.inputEl().focus();
26403             this.editing = true;
26404             return;
26405         }
26406         // show showdown...
26407         this.updateMarkdown();
26408         this.markdownEl.removeClass('d-none');
26409         this.editing = false;
26410         return;
26411     },
26412     updateMarkdown : function()
26413     {
26414         if (this.getValue() == '') {
26415             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26416             return;
26417         }
26418  
26419         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26420     },
26421     
26422     resizeTextArea: function () {
26423         
26424         var sh = 100;
26425         Roo.log([sh, this.getValue().split("\n").length * 30]);
26426         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26427     },
26428     setValue : function(val)
26429     {
26430         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26431         if (!this.editing) {
26432             this.updateMarkdown();
26433         }
26434         
26435     },
26436     focus : function()
26437     {
26438         if (!this.editing) {
26439             this.toggleTextEdit();
26440         }
26441         
26442     }
26443
26444
26445 });
26446 /**
26447  * @class Roo.bootstrap.Table.AbstractSelectionModel
26448  * @extends Roo.util.Observable
26449  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26450  * implemented by descendant classes.  This class should not be directly instantiated.
26451  * @constructor
26452  */
26453 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26454     this.locked = false;
26455     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26456 };
26457
26458
26459 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26460     /** @ignore Called by the grid automatically. Do not call directly. */
26461     init : function(grid){
26462         this.grid = grid;
26463         this.initEvents();
26464     },
26465
26466     /**
26467      * Locks the selections.
26468      */
26469     lock : function(){
26470         this.locked = true;
26471     },
26472
26473     /**
26474      * Unlocks the selections.
26475      */
26476     unlock : function(){
26477         this.locked = false;
26478     },
26479
26480     /**
26481      * Returns true if the selections are locked.
26482      * @return {Boolean}
26483      */
26484     isLocked : function(){
26485         return this.locked;
26486     },
26487     
26488     
26489     initEvents : function ()
26490     {
26491         
26492     }
26493 });
26494 /**
26495  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26496  * @class Roo.bootstrap.Table.RowSelectionModel
26497  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26498  * It supports multiple selections and keyboard selection/navigation. 
26499  * @constructor
26500  * @param {Object} config
26501  */
26502
26503 Roo.bootstrap.Table.RowSelectionModel = function(config){
26504     Roo.apply(this, config);
26505     this.selections = new Roo.util.MixedCollection(false, function(o){
26506         return o.id;
26507     });
26508
26509     this.last = false;
26510     this.lastActive = false;
26511
26512     this.addEvents({
26513         /**
26514              * @event selectionchange
26515              * Fires when the selection changes
26516              * @param {SelectionModel} this
26517              */
26518             "selectionchange" : true,
26519         /**
26520              * @event afterselectionchange
26521              * Fires after the selection changes (eg. by key press or clicking)
26522              * @param {SelectionModel} this
26523              */
26524             "afterselectionchange" : true,
26525         /**
26526              * @event beforerowselect
26527              * Fires when a row is selected being selected, return false to cancel.
26528              * @param {SelectionModel} this
26529              * @param {Number} rowIndex The selected index
26530              * @param {Boolean} keepExisting False if other selections will be cleared
26531              */
26532             "beforerowselect" : true,
26533         /**
26534              * @event rowselect
26535              * Fires when a row is selected.
26536              * @param {SelectionModel} this
26537              * @param {Number} rowIndex The selected index
26538              * @param {Roo.data.Record} r The record
26539              */
26540             "rowselect" : true,
26541         /**
26542              * @event rowdeselect
26543              * Fires when a row is deselected.
26544              * @param {SelectionModel} this
26545              * @param {Number} rowIndex The selected index
26546              */
26547         "rowdeselect" : true
26548     });
26549     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26550     this.locked = false;
26551  };
26552
26553 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26554     /**
26555      * @cfg {Boolean} singleSelect
26556      * True to allow selection of only one row at a time (defaults to false)
26557      */
26558     singleSelect : false,
26559
26560     // private
26561     initEvents : function()
26562     {
26563
26564         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26565         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26566         //}else{ // allow click to work like normal
26567          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26568         //}
26569         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26570         this.grid.on("rowclick", this.handleMouseDown, this);
26571         
26572         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26573             "up" : function(e){
26574                 if(!e.shiftKey){
26575                     this.selectPrevious(e.shiftKey);
26576                 }else if(this.last !== false && this.lastActive !== false){
26577                     var last = this.last;
26578                     this.selectRange(this.last,  this.lastActive-1);
26579                     this.grid.getView().focusRow(this.lastActive);
26580                     if(last !== false){
26581                         this.last = last;
26582                     }
26583                 }else{
26584                     this.selectFirstRow();
26585                 }
26586                 this.fireEvent("afterselectionchange", this);
26587             },
26588             "down" : function(e){
26589                 if(!e.shiftKey){
26590                     this.selectNext(e.shiftKey);
26591                 }else if(this.last !== false && this.lastActive !== false){
26592                     var last = this.last;
26593                     this.selectRange(this.last,  this.lastActive+1);
26594                     this.grid.getView().focusRow(this.lastActive);
26595                     if(last !== false){
26596                         this.last = last;
26597                     }
26598                 }else{
26599                     this.selectFirstRow();
26600                 }
26601                 this.fireEvent("afterselectionchange", this);
26602             },
26603             scope: this
26604         });
26605         this.grid.store.on('load', function(){
26606             this.selections.clear();
26607         },this);
26608         /*
26609         var view = this.grid.view;
26610         view.on("refresh", this.onRefresh, this);
26611         view.on("rowupdated", this.onRowUpdated, this);
26612         view.on("rowremoved", this.onRemove, this);
26613         */
26614     },
26615
26616     // private
26617     onRefresh : function()
26618     {
26619         var ds = this.grid.store, i, v = this.grid.view;
26620         var s = this.selections;
26621         s.each(function(r){
26622             if((i = ds.indexOfId(r.id)) != -1){
26623                 v.onRowSelect(i);
26624             }else{
26625                 s.remove(r);
26626             }
26627         });
26628     },
26629
26630     // private
26631     onRemove : function(v, index, r){
26632         this.selections.remove(r);
26633     },
26634
26635     // private
26636     onRowUpdated : function(v, index, r){
26637         if(this.isSelected(r)){
26638             v.onRowSelect(index);
26639         }
26640     },
26641
26642     /**
26643      * Select records.
26644      * @param {Array} records The records to select
26645      * @param {Boolean} keepExisting (optional) True to keep existing selections
26646      */
26647     selectRecords : function(records, keepExisting)
26648     {
26649         if(!keepExisting){
26650             this.clearSelections();
26651         }
26652             var ds = this.grid.store;
26653         for(var i = 0, len = records.length; i < len; i++){
26654             this.selectRow(ds.indexOf(records[i]), true);
26655         }
26656     },
26657
26658     /**
26659      * Gets the number of selected rows.
26660      * @return {Number}
26661      */
26662     getCount : function(){
26663         return this.selections.length;
26664     },
26665
26666     /**
26667      * Selects the first row in the grid.
26668      */
26669     selectFirstRow : function(){
26670         this.selectRow(0);
26671     },
26672
26673     /**
26674      * Select the last row.
26675      * @param {Boolean} keepExisting (optional) True to keep existing selections
26676      */
26677     selectLastRow : function(keepExisting){
26678         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26679         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26680     },
26681
26682     /**
26683      * Selects the row immediately following the last selected row.
26684      * @param {Boolean} keepExisting (optional) True to keep existing selections
26685      */
26686     selectNext : function(keepExisting)
26687     {
26688             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26689             this.selectRow(this.last+1, keepExisting);
26690             this.grid.getView().focusRow(this.last);
26691         }
26692     },
26693
26694     /**
26695      * Selects the row that precedes the last selected row.
26696      * @param {Boolean} keepExisting (optional) True to keep existing selections
26697      */
26698     selectPrevious : function(keepExisting){
26699         if(this.last){
26700             this.selectRow(this.last-1, keepExisting);
26701             this.grid.getView().focusRow(this.last);
26702         }
26703     },
26704
26705     /**
26706      * Returns the selected records
26707      * @return {Array} Array of selected records
26708      */
26709     getSelections : function(){
26710         return [].concat(this.selections.items);
26711     },
26712
26713     /**
26714      * Returns the first selected record.
26715      * @return {Record}
26716      */
26717     getSelected : function(){
26718         return this.selections.itemAt(0);
26719     },
26720
26721
26722     /**
26723      * Clears all selections.
26724      */
26725     clearSelections : function(fast)
26726     {
26727         if(this.locked) {
26728             return;
26729         }
26730         if(fast !== true){
26731                 var ds = this.grid.store;
26732             var s = this.selections;
26733             s.each(function(r){
26734                 this.deselectRow(ds.indexOfId(r.id));
26735             }, this);
26736             s.clear();
26737         }else{
26738             this.selections.clear();
26739         }
26740         this.last = false;
26741     },
26742
26743
26744     /**
26745      * Selects all rows.
26746      */
26747     selectAll : function(){
26748         if(this.locked) {
26749             return;
26750         }
26751         this.selections.clear();
26752         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26753             this.selectRow(i, true);
26754         }
26755     },
26756
26757     /**
26758      * Returns True if there is a selection.
26759      * @return {Boolean}
26760      */
26761     hasSelection : function(){
26762         return this.selections.length > 0;
26763     },
26764
26765     /**
26766      * Returns True if the specified row is selected.
26767      * @param {Number/Record} record The record or index of the record to check
26768      * @return {Boolean}
26769      */
26770     isSelected : function(index){
26771             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26772         return (r && this.selections.key(r.id) ? true : false);
26773     },
26774
26775     /**
26776      * Returns True if the specified record id is selected.
26777      * @param {String} id The id of record to check
26778      * @return {Boolean}
26779      */
26780     isIdSelected : function(id){
26781         return (this.selections.key(id) ? true : false);
26782     },
26783
26784
26785     // private
26786     handleMouseDBClick : function(e, t){
26787         
26788     },
26789     // private
26790     handleMouseDown : function(e, t)
26791     {
26792             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26793         if(this.isLocked() || rowIndex < 0 ){
26794             return;
26795         };
26796         if(e.shiftKey && this.last !== false){
26797             var last = this.last;
26798             this.selectRange(last, rowIndex, e.ctrlKey);
26799             this.last = last; // reset the last
26800             t.focus();
26801     
26802         }else{
26803             var isSelected = this.isSelected(rowIndex);
26804             //Roo.log("select row:" + rowIndex);
26805             if(isSelected){
26806                 this.deselectRow(rowIndex);
26807             } else {
26808                         this.selectRow(rowIndex, true);
26809             }
26810     
26811             /*
26812                 if(e.button !== 0 && isSelected){
26813                 alert('rowIndex 2: ' + rowIndex);
26814                     view.focusRow(rowIndex);
26815                 }else if(e.ctrlKey && isSelected){
26816                     this.deselectRow(rowIndex);
26817                 }else if(!isSelected){
26818                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26819                     view.focusRow(rowIndex);
26820                 }
26821             */
26822         }
26823         this.fireEvent("afterselectionchange", this);
26824     },
26825     // private
26826     handleDragableRowClick :  function(grid, rowIndex, e) 
26827     {
26828         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26829             this.selectRow(rowIndex, false);
26830             grid.view.focusRow(rowIndex);
26831              this.fireEvent("afterselectionchange", this);
26832         }
26833     },
26834     
26835     /**
26836      * Selects multiple rows.
26837      * @param {Array} rows Array of the indexes of the row to select
26838      * @param {Boolean} keepExisting (optional) True to keep existing selections
26839      */
26840     selectRows : function(rows, keepExisting){
26841         if(!keepExisting){
26842             this.clearSelections();
26843         }
26844         for(var i = 0, len = rows.length; i < len; i++){
26845             this.selectRow(rows[i], true);
26846         }
26847     },
26848
26849     /**
26850      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26851      * @param {Number} startRow The index of the first row in the range
26852      * @param {Number} endRow The index of the last row in the range
26853      * @param {Boolean} keepExisting (optional) True to retain existing selections
26854      */
26855     selectRange : function(startRow, endRow, keepExisting){
26856         if(this.locked) {
26857             return;
26858         }
26859         if(!keepExisting){
26860             this.clearSelections();
26861         }
26862         if(startRow <= endRow){
26863             for(var i = startRow; i <= endRow; i++){
26864                 this.selectRow(i, true);
26865             }
26866         }else{
26867             for(var i = startRow; i >= endRow; i--){
26868                 this.selectRow(i, true);
26869             }
26870         }
26871     },
26872
26873     /**
26874      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26875      * @param {Number} startRow The index of the first row in the range
26876      * @param {Number} endRow The index of the last row in the range
26877      */
26878     deselectRange : function(startRow, endRow, preventViewNotify){
26879         if(this.locked) {
26880             return;
26881         }
26882         for(var i = startRow; i <= endRow; i++){
26883             this.deselectRow(i, preventViewNotify);
26884         }
26885     },
26886
26887     /**
26888      * Selects a row.
26889      * @param {Number} row The index of the row to select
26890      * @param {Boolean} keepExisting (optional) True to keep existing selections
26891      */
26892     selectRow : function(index, keepExisting, preventViewNotify)
26893     {
26894             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26895             return;
26896         }
26897         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26898             if(!keepExisting || this.singleSelect){
26899                 this.clearSelections();
26900             }
26901             
26902             var r = this.grid.store.getAt(index);
26903             //console.log('selectRow - record id :' + r.id);
26904             
26905             this.selections.add(r);
26906             this.last = this.lastActive = index;
26907             if(!preventViewNotify){
26908                 var proxy = new Roo.Element(
26909                                 this.grid.getRowDom(index)
26910                 );
26911                 proxy.addClass('bg-info info');
26912             }
26913             this.fireEvent("rowselect", this, index, r);
26914             this.fireEvent("selectionchange", this);
26915         }
26916     },
26917
26918     /**
26919      * Deselects a row.
26920      * @param {Number} row The index of the row to deselect
26921      */
26922     deselectRow : function(index, preventViewNotify)
26923     {
26924         if(this.locked) {
26925             return;
26926         }
26927         if(this.last == index){
26928             this.last = false;
26929         }
26930         if(this.lastActive == index){
26931             this.lastActive = false;
26932         }
26933         
26934         var r = this.grid.store.getAt(index);
26935         if (!r) {
26936             return;
26937         }
26938         
26939         this.selections.remove(r);
26940         //.console.log('deselectRow - record id :' + r.id);
26941         if(!preventViewNotify){
26942         
26943             var proxy = new Roo.Element(
26944                 this.grid.getRowDom(index)
26945             );
26946             proxy.removeClass('bg-info info');
26947         }
26948         this.fireEvent("rowdeselect", this, index);
26949         this.fireEvent("selectionchange", this);
26950     },
26951
26952     // private
26953     restoreLast : function(){
26954         if(this._last){
26955             this.last = this._last;
26956         }
26957     },
26958
26959     // private
26960     acceptsNav : function(row, col, cm){
26961         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26962     },
26963
26964     // private
26965     onEditorKey : function(field, e){
26966         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26967         if(k == e.TAB){
26968             e.stopEvent();
26969             ed.completeEdit();
26970             if(e.shiftKey){
26971                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26972             }else{
26973                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26974             }
26975         }else if(k == e.ENTER && !e.ctrlKey){
26976             e.stopEvent();
26977             ed.completeEdit();
26978             if(e.shiftKey){
26979                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26980             }else{
26981                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26982             }
26983         }else if(k == e.ESC){
26984             ed.cancelEdit();
26985         }
26986         if(newCell){
26987             g.startEditing(newCell[0], newCell[1]);
26988         }
26989     }
26990 });
26991 /*
26992  * Based on:
26993  * Ext JS Library 1.1.1
26994  * Copyright(c) 2006-2007, Ext JS, LLC.
26995  *
26996  * Originally Released Under LGPL - original licence link has changed is not relivant.
26997  *
26998  * Fork - LGPL
26999  * <script type="text/javascript">
27000  */
27001  
27002 /**
27003  * @class Roo.bootstrap.PagingToolbar
27004  * @extends Roo.bootstrap.NavSimplebar
27005  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27006  * @constructor
27007  * Create a new PagingToolbar
27008  * @param {Object} config The config object
27009  * @param {Roo.data.Store} store
27010  */
27011 Roo.bootstrap.PagingToolbar = function(config)
27012 {
27013     // old args format still supported... - xtype is prefered..
27014         // created from xtype...
27015     
27016     this.ds = config.dataSource;
27017     
27018     if (config.store && !this.ds) {
27019         this.store= Roo.factory(config.store, Roo.data);
27020         this.ds = this.store;
27021         this.ds.xmodule = this.xmodule || false;
27022     }
27023     
27024     this.toolbarItems = [];
27025     if (config.items) {
27026         this.toolbarItems = config.items;
27027     }
27028     
27029     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27030     
27031     this.cursor = 0;
27032     
27033     if (this.ds) { 
27034         this.bind(this.ds);
27035     }
27036     
27037     if (Roo.bootstrap.version == 4) {
27038         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27039     } else {
27040         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27041     }
27042     
27043 };
27044
27045 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27046     /**
27047      * @cfg {Roo.data.Store} dataSource
27048      * The underlying data store providing the paged data
27049      */
27050     /**
27051      * @cfg {String/HTMLElement/Element} container
27052      * container The id or element that will contain the toolbar
27053      */
27054     /**
27055      * @cfg {Boolean} displayInfo
27056      * True to display the displayMsg (defaults to false)
27057      */
27058     /**
27059      * @cfg {Number} pageSize
27060      * The number of records to display per page (defaults to 20)
27061      */
27062     pageSize: 20,
27063     /**
27064      * @cfg {String} displayMsg
27065      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27066      */
27067     displayMsg : 'Displaying {0} - {1} of {2}',
27068     /**
27069      * @cfg {String} emptyMsg
27070      * The message to display when no records are found (defaults to "No data to display")
27071      */
27072     emptyMsg : 'No data to display',
27073     /**
27074      * Customizable piece of the default paging text (defaults to "Page")
27075      * @type String
27076      */
27077     beforePageText : "Page",
27078     /**
27079      * Customizable piece of the default paging text (defaults to "of %0")
27080      * @type String
27081      */
27082     afterPageText : "of {0}",
27083     /**
27084      * Customizable piece of the default paging text (defaults to "First Page")
27085      * @type String
27086      */
27087     firstText : "First Page",
27088     /**
27089      * Customizable piece of the default paging text (defaults to "Previous Page")
27090      * @type String
27091      */
27092     prevText : "Previous Page",
27093     /**
27094      * Customizable piece of the default paging text (defaults to "Next Page")
27095      * @type String
27096      */
27097     nextText : "Next Page",
27098     /**
27099      * Customizable piece of the default paging text (defaults to "Last Page")
27100      * @type String
27101      */
27102     lastText : "Last Page",
27103     /**
27104      * Customizable piece of the default paging text (defaults to "Refresh")
27105      * @type String
27106      */
27107     refreshText : "Refresh",
27108
27109     buttons : false,
27110     // private
27111     onRender : function(ct, position) 
27112     {
27113         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27114         this.navgroup.parentId = this.id;
27115         this.navgroup.onRender(this.el, null);
27116         // add the buttons to the navgroup
27117         
27118         if(this.displayInfo){
27119             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27120             this.displayEl = this.el.select('.x-paging-info', true).first();
27121 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27122 //            this.displayEl = navel.el.select('span',true).first();
27123         }
27124         
27125         var _this = this;
27126         
27127         if(this.buttons){
27128             Roo.each(_this.buttons, function(e){ // this might need to use render????
27129                Roo.factory(e).render(_this.el);
27130             });
27131         }
27132             
27133         Roo.each(_this.toolbarItems, function(e) {
27134             _this.navgroup.addItem(e);
27135         });
27136         
27137         
27138         this.first = this.navgroup.addItem({
27139             tooltip: this.firstText,
27140             cls: "prev btn-outline-secondary",
27141             html : ' <i class="fa fa-step-backward"></i>',
27142             disabled: true,
27143             preventDefault: true,
27144             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27145         });
27146         
27147         this.prev =  this.navgroup.addItem({
27148             tooltip: this.prevText,
27149             cls: "prev btn-outline-secondary",
27150             html : ' <i class="fa fa-backward"></i>',
27151             disabled: true,
27152             preventDefault: true,
27153             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27154         });
27155     //this.addSeparator();
27156         
27157         
27158         var field = this.navgroup.addItem( {
27159             tagtype : 'span',
27160             cls : 'x-paging-position  btn-outline-secondary',
27161              disabled: true,
27162             html : this.beforePageText  +
27163                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27164                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27165          } ); //?? escaped?
27166         
27167         this.field = field.el.select('input', true).first();
27168         this.field.on("keydown", this.onPagingKeydown, this);
27169         this.field.on("focus", function(){this.dom.select();});
27170     
27171     
27172         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27173         //this.field.setHeight(18);
27174         //this.addSeparator();
27175         this.next = this.navgroup.addItem({
27176             tooltip: this.nextText,
27177             cls: "next btn-outline-secondary",
27178             html : ' <i class="fa fa-forward"></i>',
27179             disabled: true,
27180             preventDefault: true,
27181             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27182         });
27183         this.last = this.navgroup.addItem({
27184             tooltip: this.lastText,
27185             html : ' <i class="fa fa-step-forward"></i>',
27186             cls: "next btn-outline-secondary",
27187             disabled: true,
27188             preventDefault: true,
27189             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27190         });
27191     //this.addSeparator();
27192         this.loading = this.navgroup.addItem({
27193             tooltip: this.refreshText,
27194             cls: "btn-outline-secondary",
27195             html : ' <i class="fa fa-refresh"></i>',
27196             preventDefault: true,
27197             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27198         });
27199         
27200     },
27201
27202     // private
27203     updateInfo : function(){
27204         if(this.displayEl){
27205             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27206             var msg = count == 0 ?
27207                 this.emptyMsg :
27208                 String.format(
27209                     this.displayMsg,
27210                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27211                 );
27212             this.displayEl.update(msg);
27213         }
27214     },
27215
27216     // private
27217     onLoad : function(ds, r, o)
27218     {
27219         this.cursor = o.params.start ? o.params.start : 0;
27220         
27221         var d = this.getPageData(),
27222             ap = d.activePage,
27223             ps = d.pages;
27224         
27225         
27226         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27227         this.field.dom.value = ap;
27228         this.first.setDisabled(ap == 1);
27229         this.prev.setDisabled(ap == 1);
27230         this.next.setDisabled(ap == ps);
27231         this.last.setDisabled(ap == ps);
27232         this.loading.enable();
27233         this.updateInfo();
27234     },
27235
27236     // private
27237     getPageData : function(){
27238         var total = this.ds.getTotalCount();
27239         return {
27240             total : total,
27241             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27242             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27243         };
27244     },
27245
27246     // private
27247     onLoadError : function(){
27248         this.loading.enable();
27249     },
27250
27251     // private
27252     onPagingKeydown : function(e){
27253         var k = e.getKey();
27254         var d = this.getPageData();
27255         if(k == e.RETURN){
27256             var v = this.field.dom.value, pageNum;
27257             if(!v || isNaN(pageNum = parseInt(v, 10))){
27258                 this.field.dom.value = d.activePage;
27259                 return;
27260             }
27261             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27262             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27263             e.stopEvent();
27264         }
27265         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))
27266         {
27267           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27268           this.field.dom.value = pageNum;
27269           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27270           e.stopEvent();
27271         }
27272         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27273         {
27274           var v = this.field.dom.value, pageNum; 
27275           var increment = (e.shiftKey) ? 10 : 1;
27276           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27277                 increment *= -1;
27278           }
27279           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27280             this.field.dom.value = d.activePage;
27281             return;
27282           }
27283           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27284           {
27285             this.field.dom.value = parseInt(v, 10) + increment;
27286             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27287             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27288           }
27289           e.stopEvent();
27290         }
27291     },
27292
27293     // private
27294     beforeLoad : function(){
27295         if(this.loading){
27296             this.loading.disable();
27297         }
27298     },
27299
27300     // private
27301     onClick : function(which){
27302         
27303         var ds = this.ds;
27304         if (!ds) {
27305             return;
27306         }
27307         
27308         switch(which){
27309             case "first":
27310                 ds.load({params:{start: 0, limit: this.pageSize}});
27311             break;
27312             case "prev":
27313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27314             break;
27315             case "next":
27316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27317             break;
27318             case "last":
27319                 var total = ds.getTotalCount();
27320                 var extra = total % this.pageSize;
27321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27323             break;
27324             case "refresh":
27325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27326             break;
27327         }
27328     },
27329
27330     /**
27331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27332      * @param {Roo.data.Store} store The data store to unbind
27333      */
27334     unbind : function(ds){
27335         ds.un("beforeload", this.beforeLoad, this);
27336         ds.un("load", this.onLoad, this);
27337         ds.un("loadexception", this.onLoadError, this);
27338         ds.un("remove", this.updateInfo, this);
27339         ds.un("add", this.updateInfo, this);
27340         this.ds = undefined;
27341     },
27342
27343     /**
27344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27345      * @param {Roo.data.Store} store The data store to bind
27346      */
27347     bind : function(ds){
27348         ds.on("beforeload", this.beforeLoad, this);
27349         ds.on("load", this.onLoad, this);
27350         ds.on("loadexception", this.onLoadError, this);
27351         ds.on("remove", this.updateInfo, this);
27352         ds.on("add", this.updateInfo, this);
27353         this.ds = ds;
27354     }
27355 });/*
27356  * - LGPL
27357  *
27358  * element
27359  * 
27360  */
27361
27362 /**
27363  * @class Roo.bootstrap.MessageBar
27364  * @extends Roo.bootstrap.Component
27365  * Bootstrap MessageBar class
27366  * @cfg {String} html contents of the MessageBar
27367  * @cfg {String} weight (info | success | warning | danger) default info
27368  * @cfg {String} beforeClass insert the bar before the given class
27369  * @cfg {Boolean} closable (true | false) default false
27370  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27371  * 
27372  * @constructor
27373  * Create a new Element
27374  * @param {Object} config The config object
27375  */
27376
27377 Roo.bootstrap.MessageBar = function(config){
27378     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27379 };
27380
27381 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27382     
27383     html: '',
27384     weight: 'info',
27385     closable: false,
27386     fixed: false,
27387     beforeClass: 'bootstrap-sticky-wrap',
27388     
27389     getAutoCreate : function(){
27390         
27391         var cfg = {
27392             tag: 'div',
27393             cls: 'alert alert-dismissable alert-' + this.weight,
27394             cn: [
27395                 {
27396                     tag: 'span',
27397                     cls: 'message',
27398                     html: this.html || ''
27399                 }
27400             ]
27401         };
27402         
27403         if(this.fixed){
27404             cfg.cls += ' alert-messages-fixed';
27405         }
27406         
27407         if(this.closable){
27408             cfg.cn.push({
27409                 tag: 'button',
27410                 cls: 'close',
27411                 html: 'x'
27412             });
27413         }
27414         
27415         return cfg;
27416     },
27417     
27418     onRender : function(ct, position)
27419     {
27420         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27421         
27422         if(!this.el){
27423             var cfg = Roo.apply({},  this.getAutoCreate());
27424             cfg.id = Roo.id();
27425             
27426             if (this.cls) {
27427                 cfg.cls += ' ' + this.cls;
27428             }
27429             if (this.style) {
27430                 cfg.style = this.style;
27431             }
27432             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27433             
27434             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27435         }
27436         
27437         this.el.select('>button.close').on('click', this.hide, this);
27438         
27439     },
27440     
27441     show : function()
27442     {
27443         if (!this.rendered) {
27444             this.render();
27445         }
27446         
27447         this.el.show();
27448         
27449         this.fireEvent('show', this);
27450         
27451     },
27452     
27453     hide : function()
27454     {
27455         if (!this.rendered) {
27456             this.render();
27457         }
27458         
27459         this.el.hide();
27460         
27461         this.fireEvent('hide', this);
27462     },
27463     
27464     update : function()
27465     {
27466 //        var e = this.el.dom.firstChild;
27467 //        
27468 //        if(this.closable){
27469 //            e = e.nextSibling;
27470 //        }
27471 //        
27472 //        e.data = this.html || '';
27473
27474         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27475     }
27476    
27477 });
27478
27479  
27480
27481      /*
27482  * - LGPL
27483  *
27484  * Graph
27485  * 
27486  */
27487
27488
27489 /**
27490  * @class Roo.bootstrap.Graph
27491  * @extends Roo.bootstrap.Component
27492  * Bootstrap Graph class
27493 > Prameters
27494  -sm {number} sm 4
27495  -md {number} md 5
27496  @cfg {String} graphtype  bar | vbar | pie
27497  @cfg {number} g_x coodinator | centre x (pie)
27498  @cfg {number} g_y coodinator | centre y (pie)
27499  @cfg {number} g_r radius (pie)
27500  @cfg {number} g_height height of the chart (respected by all elements in the set)
27501  @cfg {number} g_width width of the chart (respected by all elements in the set)
27502  @cfg {Object} title The title of the chart
27503     
27504  -{Array}  values
27505  -opts (object) options for the chart 
27506      o {
27507      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27508      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27509      o vgutter (number)
27510      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.
27511      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27512      o to
27513      o stretch (boolean)
27514      o }
27515  -opts (object) options for the pie
27516      o{
27517      o cut
27518      o startAngle (number)
27519      o endAngle (number)
27520      } 
27521  *
27522  * @constructor
27523  * Create a new Input
27524  * @param {Object} config The config object
27525  */
27526
27527 Roo.bootstrap.Graph = function(config){
27528     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27529     
27530     this.addEvents({
27531         // img events
27532         /**
27533          * @event click
27534          * The img click event for the img.
27535          * @param {Roo.EventObject} e
27536          */
27537         "click" : true
27538     });
27539 };
27540
27541 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27542     
27543     sm: 4,
27544     md: 5,
27545     graphtype: 'bar',
27546     g_height: 250,
27547     g_width: 400,
27548     g_x: 50,
27549     g_y: 50,
27550     g_r: 30,
27551     opts:{
27552         //g_colors: this.colors,
27553         g_type: 'soft',
27554         g_gutter: '20%'
27555
27556     },
27557     title : false,
27558
27559     getAutoCreate : function(){
27560         
27561         var cfg = {
27562             tag: 'div',
27563             html : null
27564         };
27565         
27566         
27567         return  cfg;
27568     },
27569
27570     onRender : function(ct,position){
27571         
27572         
27573         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27574         
27575         if (typeof(Raphael) == 'undefined') {
27576             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27577             return;
27578         }
27579         
27580         this.raphael = Raphael(this.el.dom);
27581         
27582                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27583                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27584                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27585                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27586                 /*
27587                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27588                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27589                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27590                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27591                 
27592                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27593                 r.barchart(330, 10, 300, 220, data1);
27594                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27595                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27596                 */
27597                 
27598                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27599                 // r.barchart(30, 30, 560, 250,  xdata, {
27600                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27601                 //     axis : "0 0 1 1",
27602                 //     axisxlabels :  xdata
27603                 //     //yvalues : cols,
27604                    
27605                 // });
27606 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27607 //        
27608 //        this.load(null,xdata,{
27609 //                axis : "0 0 1 1",
27610 //                axisxlabels :  xdata
27611 //                });
27612
27613     },
27614
27615     load : function(graphtype,xdata,opts)
27616     {
27617         this.raphael.clear();
27618         if(!graphtype) {
27619             graphtype = this.graphtype;
27620         }
27621         if(!opts){
27622             opts = this.opts;
27623         }
27624         var r = this.raphael,
27625             fin = function () {
27626                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27627             },
27628             fout = function () {
27629                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27630             },
27631             pfin = function() {
27632                 this.sector.stop();
27633                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27634
27635                 if (this.label) {
27636                     this.label[0].stop();
27637                     this.label[0].attr({ r: 7.5 });
27638                     this.label[1].attr({ "font-weight": 800 });
27639                 }
27640             },
27641             pfout = function() {
27642                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27643
27644                 if (this.label) {
27645                     this.label[0].animate({ r: 5 }, 500, "bounce");
27646                     this.label[1].attr({ "font-weight": 400 });
27647                 }
27648             };
27649
27650         switch(graphtype){
27651             case 'bar':
27652                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27653                 break;
27654             case 'hbar':
27655                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27656                 break;
27657             case 'pie':
27658 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27659 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27660 //            
27661                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27662                 
27663                 break;
27664
27665         }
27666         
27667         if(this.title){
27668             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27669         }
27670         
27671     },
27672     
27673     setTitle: function(o)
27674     {
27675         this.title = o;
27676     },
27677     
27678     initEvents: function() {
27679         
27680         if(!this.href){
27681             this.el.on('click', this.onClick, this);
27682         }
27683     },
27684     
27685     onClick : function(e)
27686     {
27687         Roo.log('img onclick');
27688         this.fireEvent('click', this, e);
27689     }
27690    
27691 });
27692
27693  
27694 /*
27695  * - LGPL
27696  *
27697  * numberBox
27698  * 
27699  */
27700 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27701
27702 /**
27703  * @class Roo.bootstrap.dash.NumberBox
27704  * @extends Roo.bootstrap.Component
27705  * Bootstrap NumberBox class
27706  * @cfg {String} headline Box headline
27707  * @cfg {String} content Box content
27708  * @cfg {String} icon Box icon
27709  * @cfg {String} footer Footer text
27710  * @cfg {String} fhref Footer href
27711  * 
27712  * @constructor
27713  * Create a new NumberBox
27714  * @param {Object} config The config object
27715  */
27716
27717
27718 Roo.bootstrap.dash.NumberBox = function(config){
27719     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27720     
27721 };
27722
27723 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27724     
27725     headline : '',
27726     content : '',
27727     icon : '',
27728     footer : '',
27729     fhref : '',
27730     ficon : '',
27731     
27732     getAutoCreate : function(){
27733         
27734         var cfg = {
27735             tag : 'div',
27736             cls : 'small-box ',
27737             cn : [
27738                 {
27739                     tag : 'div',
27740                     cls : 'inner',
27741                     cn :[
27742                         {
27743                             tag : 'h3',
27744                             cls : 'roo-headline',
27745                             html : this.headline
27746                         },
27747                         {
27748                             tag : 'p',
27749                             cls : 'roo-content',
27750                             html : this.content
27751                         }
27752                     ]
27753                 }
27754             ]
27755         };
27756         
27757         if(this.icon){
27758             cfg.cn.push({
27759                 tag : 'div',
27760                 cls : 'icon',
27761                 cn :[
27762                     {
27763                         tag : 'i',
27764                         cls : 'ion ' + this.icon
27765                     }
27766                 ]
27767             });
27768         }
27769         
27770         if(this.footer){
27771             var footer = {
27772                 tag : 'a',
27773                 cls : 'small-box-footer',
27774                 href : this.fhref || '#',
27775                 html : this.footer
27776             };
27777             
27778             cfg.cn.push(footer);
27779             
27780         }
27781         
27782         return  cfg;
27783     },
27784
27785     onRender : function(ct,position){
27786         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27787
27788
27789        
27790                 
27791     },
27792
27793     setHeadline: function (value)
27794     {
27795         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27796     },
27797     
27798     setFooter: function (value, href)
27799     {
27800         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27801         
27802         if(href){
27803             this.el.select('a.small-box-footer',true).first().attr('href', href);
27804         }
27805         
27806     },
27807
27808     setContent: function (value)
27809     {
27810         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27811     },
27812
27813     initEvents: function() 
27814     {   
27815         
27816     }
27817     
27818 });
27819
27820  
27821 /*
27822  * - LGPL
27823  *
27824  * TabBox
27825  * 
27826  */
27827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27828
27829 /**
27830  * @class Roo.bootstrap.dash.TabBox
27831  * @extends Roo.bootstrap.Component
27832  * Bootstrap TabBox class
27833  * @cfg {String} title Title of the TabBox
27834  * @cfg {String} icon Icon of the TabBox
27835  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27836  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27837  * 
27838  * @constructor
27839  * Create a new TabBox
27840  * @param {Object} config The config object
27841  */
27842
27843
27844 Roo.bootstrap.dash.TabBox = function(config){
27845     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27846     this.addEvents({
27847         // raw events
27848         /**
27849          * @event addpane
27850          * When a pane is added
27851          * @param {Roo.bootstrap.dash.TabPane} pane
27852          */
27853         "addpane" : true,
27854         /**
27855          * @event activatepane
27856          * When a pane is activated
27857          * @param {Roo.bootstrap.dash.TabPane} pane
27858          */
27859         "activatepane" : true
27860         
27861          
27862     });
27863     
27864     this.panes = [];
27865 };
27866
27867 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27868
27869     title : '',
27870     icon : false,
27871     showtabs : true,
27872     tabScrollable : false,
27873     
27874     getChildContainer : function()
27875     {
27876         return this.el.select('.tab-content', true).first();
27877     },
27878     
27879     getAutoCreate : function(){
27880         
27881         var header = {
27882             tag: 'li',
27883             cls: 'pull-left header',
27884             html: this.title,
27885             cn : []
27886         };
27887         
27888         if(this.icon){
27889             header.cn.push({
27890                 tag: 'i',
27891                 cls: 'fa ' + this.icon
27892             });
27893         }
27894         
27895         var h = {
27896             tag: 'ul',
27897             cls: 'nav nav-tabs pull-right',
27898             cn: [
27899                 header
27900             ]
27901         };
27902         
27903         if(this.tabScrollable){
27904             h = {
27905                 tag: 'div',
27906                 cls: 'tab-header',
27907                 cn: [
27908                     {
27909                         tag: 'ul',
27910                         cls: 'nav nav-tabs pull-right',
27911                         cn: [
27912                             header
27913                         ]
27914                     }
27915                 ]
27916             };
27917         }
27918         
27919         var cfg = {
27920             tag: 'div',
27921             cls: 'nav-tabs-custom',
27922             cn: [
27923                 h,
27924                 {
27925                     tag: 'div',
27926                     cls: 'tab-content no-padding',
27927                     cn: []
27928                 }
27929             ]
27930         };
27931
27932         return  cfg;
27933     },
27934     initEvents : function()
27935     {
27936         //Roo.log('add add pane handler');
27937         this.on('addpane', this.onAddPane, this);
27938     },
27939      /**
27940      * Updates the box title
27941      * @param {String} html to set the title to.
27942      */
27943     setTitle : function(value)
27944     {
27945         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27946     },
27947     onAddPane : function(pane)
27948     {
27949         this.panes.push(pane);
27950         //Roo.log('addpane');
27951         //Roo.log(pane);
27952         // tabs are rendere left to right..
27953         if(!this.showtabs){
27954             return;
27955         }
27956         
27957         var ctr = this.el.select('.nav-tabs', true).first();
27958          
27959          
27960         var existing = ctr.select('.nav-tab',true);
27961         var qty = existing.getCount();;
27962         
27963         
27964         var tab = ctr.createChild({
27965             tag : 'li',
27966             cls : 'nav-tab' + (qty ? '' : ' active'),
27967             cn : [
27968                 {
27969                     tag : 'a',
27970                     href:'#',
27971                     html : pane.title
27972                 }
27973             ]
27974         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27975         pane.tab = tab;
27976         
27977         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27978         if (!qty) {
27979             pane.el.addClass('active');
27980         }
27981         
27982                 
27983     },
27984     onTabClick : function(ev,un,ob,pane)
27985     {
27986         //Roo.log('tab - prev default');
27987         ev.preventDefault();
27988         
27989         
27990         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27991         pane.tab.addClass('active');
27992         //Roo.log(pane.title);
27993         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27994         // technically we should have a deactivate event.. but maybe add later.
27995         // and it should not de-activate the selected tab...
27996         this.fireEvent('activatepane', pane);
27997         pane.el.addClass('active');
27998         pane.fireEvent('activate');
27999         
28000         
28001     },
28002     
28003     getActivePane : function()
28004     {
28005         var r = false;
28006         Roo.each(this.panes, function(p) {
28007             if(p.el.hasClass('active')){
28008                 r = p;
28009                 return false;
28010             }
28011             
28012             return;
28013         });
28014         
28015         return r;
28016     }
28017     
28018     
28019 });
28020
28021  
28022 /*
28023  * - LGPL
28024  *
28025  * Tab pane
28026  * 
28027  */
28028 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28029 /**
28030  * @class Roo.bootstrap.TabPane
28031  * @extends Roo.bootstrap.Component
28032  * Bootstrap TabPane class
28033  * @cfg {Boolean} active (false | true) Default false
28034  * @cfg {String} title title of panel
28035
28036  * 
28037  * @constructor
28038  * Create a new TabPane
28039  * @param {Object} config The config object
28040  */
28041
28042 Roo.bootstrap.dash.TabPane = function(config){
28043     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28044     
28045     this.addEvents({
28046         // raw events
28047         /**
28048          * @event activate
28049          * When a pane is activated
28050          * @param {Roo.bootstrap.dash.TabPane} pane
28051          */
28052         "activate" : true
28053          
28054     });
28055 };
28056
28057 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28058     
28059     active : false,
28060     title : '',
28061     
28062     // the tabBox that this is attached to.
28063     tab : false,
28064      
28065     getAutoCreate : function() 
28066     {
28067         var cfg = {
28068             tag: 'div',
28069             cls: 'tab-pane'
28070         };
28071         
28072         if(this.active){
28073             cfg.cls += ' active';
28074         }
28075         
28076         return cfg;
28077     },
28078     initEvents  : function()
28079     {
28080         //Roo.log('trigger add pane handler');
28081         this.parent().fireEvent('addpane', this)
28082     },
28083     
28084      /**
28085      * Updates the tab title 
28086      * @param {String} html to set the title to.
28087      */
28088     setTitle: function(str)
28089     {
28090         if (!this.tab) {
28091             return;
28092         }
28093         this.title = str;
28094         this.tab.select('a', true).first().dom.innerHTML = str;
28095         
28096     }
28097     
28098     
28099     
28100 });
28101
28102  
28103
28104
28105  /*
28106  * - LGPL
28107  *
28108  * menu
28109  * 
28110  */
28111 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28112
28113 /**
28114  * @class Roo.bootstrap.menu.Menu
28115  * @extends Roo.bootstrap.Component
28116  * Bootstrap Menu class - container for Menu
28117  * @cfg {String} html Text of the menu
28118  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28119  * @cfg {String} icon Font awesome icon
28120  * @cfg {String} pos Menu align to (top | bottom) default bottom
28121  * 
28122  * 
28123  * @constructor
28124  * Create a new Menu
28125  * @param {Object} config The config object
28126  */
28127
28128
28129 Roo.bootstrap.menu.Menu = function(config){
28130     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28131     
28132     this.addEvents({
28133         /**
28134          * @event beforeshow
28135          * Fires before this menu is displayed
28136          * @param {Roo.bootstrap.menu.Menu} this
28137          */
28138         beforeshow : true,
28139         /**
28140          * @event beforehide
28141          * Fires before this menu is hidden
28142          * @param {Roo.bootstrap.menu.Menu} this
28143          */
28144         beforehide : true,
28145         /**
28146          * @event show
28147          * Fires after this menu is displayed
28148          * @param {Roo.bootstrap.menu.Menu} this
28149          */
28150         show : true,
28151         /**
28152          * @event hide
28153          * Fires after this menu is hidden
28154          * @param {Roo.bootstrap.menu.Menu} this
28155          */
28156         hide : true,
28157         /**
28158          * @event click
28159          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28160          * @param {Roo.bootstrap.menu.Menu} this
28161          * @param {Roo.EventObject} e
28162          */
28163         click : true
28164     });
28165     
28166 };
28167
28168 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28169     
28170     submenu : false,
28171     html : '',
28172     weight : 'default',
28173     icon : false,
28174     pos : 'bottom',
28175     
28176     
28177     getChildContainer : function() {
28178         if(this.isSubMenu){
28179             return this.el;
28180         }
28181         
28182         return this.el.select('ul.dropdown-menu', true).first();  
28183     },
28184     
28185     getAutoCreate : function()
28186     {
28187         var text = [
28188             {
28189                 tag : 'span',
28190                 cls : 'roo-menu-text',
28191                 html : this.html
28192             }
28193         ];
28194         
28195         if(this.icon){
28196             text.unshift({
28197                 tag : 'i',
28198                 cls : 'fa ' + this.icon
28199             })
28200         }
28201         
28202         
28203         var cfg = {
28204             tag : 'div',
28205             cls : 'btn-group',
28206             cn : [
28207                 {
28208                     tag : 'button',
28209                     cls : 'dropdown-button btn btn-' + this.weight,
28210                     cn : text
28211                 },
28212                 {
28213                     tag : 'button',
28214                     cls : 'dropdown-toggle btn btn-' + this.weight,
28215                     cn : [
28216                         {
28217                             tag : 'span',
28218                             cls : 'caret'
28219                         }
28220                     ]
28221                 },
28222                 {
28223                     tag : 'ul',
28224                     cls : 'dropdown-menu'
28225                 }
28226             ]
28227             
28228         };
28229         
28230         if(this.pos == 'top'){
28231             cfg.cls += ' dropup';
28232         }
28233         
28234         if(this.isSubMenu){
28235             cfg = {
28236                 tag : 'ul',
28237                 cls : 'dropdown-menu'
28238             }
28239         }
28240         
28241         return cfg;
28242     },
28243     
28244     onRender : function(ct, position)
28245     {
28246         this.isSubMenu = ct.hasClass('dropdown-submenu');
28247         
28248         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28249     },
28250     
28251     initEvents : function() 
28252     {
28253         if(this.isSubMenu){
28254             return;
28255         }
28256         
28257         this.hidden = true;
28258         
28259         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28260         this.triggerEl.on('click', this.onTriggerPress, this);
28261         
28262         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28263         this.buttonEl.on('click', this.onClick, this);
28264         
28265     },
28266     
28267     list : function()
28268     {
28269         if(this.isSubMenu){
28270             return this.el;
28271         }
28272         
28273         return this.el.select('ul.dropdown-menu', true).first();
28274     },
28275     
28276     onClick : function(e)
28277     {
28278         this.fireEvent("click", this, e);
28279     },
28280     
28281     onTriggerPress  : function(e)
28282     {   
28283         if (this.isVisible()) {
28284             this.hide();
28285         } else {
28286             this.show();
28287         }
28288     },
28289     
28290     isVisible : function(){
28291         return !this.hidden;
28292     },
28293     
28294     show : function()
28295     {
28296         this.fireEvent("beforeshow", this);
28297         
28298         this.hidden = false;
28299         this.el.addClass('open');
28300         
28301         Roo.get(document).on("mouseup", this.onMouseUp, this);
28302         
28303         this.fireEvent("show", this);
28304         
28305         
28306     },
28307     
28308     hide : function()
28309     {
28310         this.fireEvent("beforehide", this);
28311         
28312         this.hidden = true;
28313         this.el.removeClass('open');
28314         
28315         Roo.get(document).un("mouseup", this.onMouseUp);
28316         
28317         this.fireEvent("hide", this);
28318     },
28319     
28320     onMouseUp : function()
28321     {
28322         this.hide();
28323     }
28324     
28325 });
28326
28327  
28328  /*
28329  * - LGPL
28330  *
28331  * menu item
28332  * 
28333  */
28334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28335
28336 /**
28337  * @class Roo.bootstrap.menu.Item
28338  * @extends Roo.bootstrap.Component
28339  * Bootstrap MenuItem class
28340  * @cfg {Boolean} submenu (true | false) default false
28341  * @cfg {String} html text of the item
28342  * @cfg {String} href the link
28343  * @cfg {Boolean} disable (true | false) default false
28344  * @cfg {Boolean} preventDefault (true | false) default true
28345  * @cfg {String} icon Font awesome icon
28346  * @cfg {String} pos Submenu align to (left | right) default right 
28347  * 
28348  * 
28349  * @constructor
28350  * Create a new Item
28351  * @param {Object} config The config object
28352  */
28353
28354
28355 Roo.bootstrap.menu.Item = function(config){
28356     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28357     this.addEvents({
28358         /**
28359          * @event mouseover
28360          * Fires when the mouse is hovering over this menu
28361          * @param {Roo.bootstrap.menu.Item} this
28362          * @param {Roo.EventObject} e
28363          */
28364         mouseover : true,
28365         /**
28366          * @event mouseout
28367          * Fires when the mouse exits this menu
28368          * @param {Roo.bootstrap.menu.Item} this
28369          * @param {Roo.EventObject} e
28370          */
28371         mouseout : true,
28372         // raw events
28373         /**
28374          * @event click
28375          * The raw click event for the entire grid.
28376          * @param {Roo.EventObject} e
28377          */
28378         click : true
28379     });
28380 };
28381
28382 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28383     
28384     submenu : false,
28385     href : '',
28386     html : '',
28387     preventDefault: true,
28388     disable : false,
28389     icon : false,
28390     pos : 'right',
28391     
28392     getAutoCreate : function()
28393     {
28394         var text = [
28395             {
28396                 tag : 'span',
28397                 cls : 'roo-menu-item-text',
28398                 html : this.html
28399             }
28400         ];
28401         
28402         if(this.icon){
28403             text.unshift({
28404                 tag : 'i',
28405                 cls : 'fa ' + this.icon
28406             })
28407         }
28408         
28409         var cfg = {
28410             tag : 'li',
28411             cn : [
28412                 {
28413                     tag : 'a',
28414                     href : this.href || '#',
28415                     cn : text
28416                 }
28417             ]
28418         };
28419         
28420         if(this.disable){
28421             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28422         }
28423         
28424         if(this.submenu){
28425             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28426             
28427             if(this.pos == 'left'){
28428                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28429             }
28430         }
28431         
28432         return cfg;
28433     },
28434     
28435     initEvents : function() 
28436     {
28437         this.el.on('mouseover', this.onMouseOver, this);
28438         this.el.on('mouseout', this.onMouseOut, this);
28439         
28440         this.el.select('a', true).first().on('click', this.onClick, this);
28441         
28442     },
28443     
28444     onClick : function(e)
28445     {
28446         if(this.preventDefault){
28447             e.preventDefault();
28448         }
28449         
28450         this.fireEvent("click", this, e);
28451     },
28452     
28453     onMouseOver : function(e)
28454     {
28455         if(this.submenu && this.pos == 'left'){
28456             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28457         }
28458         
28459         this.fireEvent("mouseover", this, e);
28460     },
28461     
28462     onMouseOut : function(e)
28463     {
28464         this.fireEvent("mouseout", this, e);
28465     }
28466 });
28467
28468  
28469
28470  /*
28471  * - LGPL
28472  *
28473  * menu separator
28474  * 
28475  */
28476 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28477
28478 /**
28479  * @class Roo.bootstrap.menu.Separator
28480  * @extends Roo.bootstrap.Component
28481  * Bootstrap Separator class
28482  * 
28483  * @constructor
28484  * Create a new Separator
28485  * @param {Object} config The config object
28486  */
28487
28488
28489 Roo.bootstrap.menu.Separator = function(config){
28490     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28491 };
28492
28493 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28494     
28495     getAutoCreate : function(){
28496         var cfg = {
28497             tag : 'li',
28498             cls: 'divider'
28499         };
28500         
28501         return cfg;
28502     }
28503    
28504 });
28505
28506  
28507
28508  /*
28509  * - LGPL
28510  *
28511  * Tooltip
28512  * 
28513  */
28514
28515 /**
28516  * @class Roo.bootstrap.Tooltip
28517  * Bootstrap Tooltip class
28518  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28519  * to determine which dom element triggers the tooltip.
28520  * 
28521  * It needs to add support for additional attributes like tooltip-position
28522  * 
28523  * @constructor
28524  * Create a new Toolti
28525  * @param {Object} config The config object
28526  */
28527
28528 Roo.bootstrap.Tooltip = function(config){
28529     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28530     
28531     this.alignment = Roo.bootstrap.Tooltip.alignment;
28532     
28533     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28534         this.alignment = config.alignment;
28535     }
28536     
28537 };
28538
28539 Roo.apply(Roo.bootstrap.Tooltip, {
28540     /**
28541      * @function init initialize tooltip monitoring.
28542      * @static
28543      */
28544     currentEl : false,
28545     currentTip : false,
28546     currentRegion : false,
28547     
28548     //  init : delay?
28549     
28550     init : function()
28551     {
28552         Roo.get(document).on('mouseover', this.enter ,this);
28553         Roo.get(document).on('mouseout', this.leave, this);
28554          
28555         
28556         this.currentTip = new Roo.bootstrap.Tooltip();
28557     },
28558     
28559     enter : function(ev)
28560     {
28561         var dom = ev.getTarget();
28562         
28563         //Roo.log(['enter',dom]);
28564         var el = Roo.fly(dom);
28565         if (this.currentEl) {
28566             //Roo.log(dom);
28567             //Roo.log(this.currentEl);
28568             //Roo.log(this.currentEl.contains(dom));
28569             if (this.currentEl == el) {
28570                 return;
28571             }
28572             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28573                 return;
28574             }
28575
28576         }
28577         
28578         if (this.currentTip.el) {
28579             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28580         }    
28581         //Roo.log(ev);
28582         
28583         if(!el || el.dom == document){
28584             return;
28585         }
28586         
28587         var bindEl = el;
28588         
28589         // you can not look for children, as if el is the body.. then everythign is the child..
28590         if (!el.attr('tooltip')) { //
28591             if (!el.select("[tooltip]").elements.length) {
28592                 return;
28593             }
28594             // is the mouse over this child...?
28595             bindEl = el.select("[tooltip]").first();
28596             var xy = ev.getXY();
28597             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28598                 //Roo.log("not in region.");
28599                 return;
28600             }
28601             //Roo.log("child element over..");
28602             
28603         }
28604         this.currentEl = bindEl;
28605         this.currentTip.bind(bindEl);
28606         this.currentRegion = Roo.lib.Region.getRegion(dom);
28607         this.currentTip.enter();
28608         
28609     },
28610     leave : function(ev)
28611     {
28612         var dom = ev.getTarget();
28613         //Roo.log(['leave',dom]);
28614         if (!this.currentEl) {
28615             return;
28616         }
28617         
28618         
28619         if (dom != this.currentEl.dom) {
28620             return;
28621         }
28622         var xy = ev.getXY();
28623         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28624             return;
28625         }
28626         // only activate leave if mouse cursor is outside... bounding box..
28627         
28628         
28629         
28630         
28631         if (this.currentTip) {
28632             this.currentTip.leave();
28633         }
28634         //Roo.log('clear currentEl');
28635         this.currentEl = false;
28636         
28637         
28638     },
28639     alignment : {
28640         'left' : ['r-l', [-2,0], 'right'],
28641         'right' : ['l-r', [2,0], 'left'],
28642         'bottom' : ['t-b', [0,2], 'top'],
28643         'top' : [ 'b-t', [0,-2], 'bottom']
28644     }
28645     
28646 });
28647
28648
28649 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28650     
28651     
28652     bindEl : false,
28653     
28654     delay : null, // can be { show : 300 , hide: 500}
28655     
28656     timeout : null,
28657     
28658     hoverState : null, //???
28659     
28660     placement : 'bottom', 
28661     
28662     alignment : false,
28663     
28664     getAutoCreate : function(){
28665     
28666         var cfg = {
28667            cls : 'tooltip',   
28668            role : 'tooltip',
28669            cn : [
28670                 {
28671                     cls : 'tooltip-arrow arrow'
28672                 },
28673                 {
28674                     cls : 'tooltip-inner'
28675                 }
28676            ]
28677         };
28678         
28679         return cfg;
28680     },
28681     bind : function(el)
28682     {
28683         this.bindEl = el;
28684     },
28685     
28686     initEvents : function()
28687     {
28688         this.arrowEl = this.el.select('.arrow', true).first();
28689         this.innerEl = this.el.select('.tooltip-inner', true).first();
28690     },
28691     
28692     enter : function () {
28693        
28694         if (this.timeout != null) {
28695             clearTimeout(this.timeout);
28696         }
28697         
28698         this.hoverState = 'in';
28699          //Roo.log("enter - show");
28700         if (!this.delay || !this.delay.show) {
28701             this.show();
28702             return;
28703         }
28704         var _t = this;
28705         this.timeout = setTimeout(function () {
28706             if (_t.hoverState == 'in') {
28707                 _t.show();
28708             }
28709         }, this.delay.show);
28710     },
28711     leave : function()
28712     {
28713         clearTimeout(this.timeout);
28714     
28715         this.hoverState = 'out';
28716          if (!this.delay || !this.delay.hide) {
28717             this.hide();
28718             return;
28719         }
28720        
28721         var _t = this;
28722         this.timeout = setTimeout(function () {
28723             //Roo.log("leave - timeout");
28724             
28725             if (_t.hoverState == 'out') {
28726                 _t.hide();
28727                 Roo.bootstrap.Tooltip.currentEl = false;
28728             }
28729         }, delay);
28730     },
28731     
28732     show : function (msg)
28733     {
28734         if (!this.el) {
28735             this.render(document.body);
28736         }
28737         // set content.
28738         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28739         
28740         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28741         
28742         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28743         
28744         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28745                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28746         
28747         var placement = typeof this.placement == 'function' ?
28748             this.placement.call(this, this.el, on_el) :
28749             this.placement;
28750             
28751         var autoToken = /\s?auto?\s?/i;
28752         var autoPlace = autoToken.test(placement);
28753         if (autoPlace) {
28754             placement = placement.replace(autoToken, '') || 'top';
28755         }
28756         
28757         //this.el.detach()
28758         //this.el.setXY([0,0]);
28759         this.el.show();
28760         //this.el.dom.style.display='block';
28761         
28762         //this.el.appendTo(on_el);
28763         
28764         var p = this.getPosition();
28765         var box = this.el.getBox();
28766         
28767         if (autoPlace) {
28768             // fixme..
28769         }
28770         
28771         var align = this.alignment[placement];
28772         
28773         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28774         
28775         if(placement == 'top' || placement == 'bottom'){
28776             if(xy[0] < 0){
28777                 placement = 'right';
28778             }
28779             
28780             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28781                 placement = 'left';
28782             }
28783             
28784             var scroll = Roo.select('body', true).first().getScroll();
28785             
28786             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28787                 placement = 'top';
28788             }
28789             
28790             align = this.alignment[placement];
28791             
28792             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28793             
28794         }
28795         
28796         this.el.alignTo(this.bindEl, align[0],align[1]);
28797         //var arrow = this.el.select('.arrow',true).first();
28798         //arrow.set(align[2], 
28799         
28800         this.el.addClass(placement);
28801         this.el.addClass("bs-tooltip-"+ placement);
28802         
28803         this.el.addClass('in fade show');
28804         
28805         this.hoverState = null;
28806         
28807         if (this.el.hasClass('fade')) {
28808             // fade it?
28809         }
28810         
28811         
28812         
28813         
28814         
28815     },
28816     hide : function()
28817     {
28818          
28819         if (!this.el) {
28820             return;
28821         }
28822         //this.el.setXY([0,0]);
28823         this.el.removeClass(['show', 'in']);
28824         //this.el.hide();
28825         
28826     }
28827     
28828 });
28829  
28830
28831  /*
28832  * - LGPL
28833  *
28834  * Location Picker
28835  * 
28836  */
28837
28838 /**
28839  * @class Roo.bootstrap.LocationPicker
28840  * @extends Roo.bootstrap.Component
28841  * Bootstrap LocationPicker class
28842  * @cfg {Number} latitude Position when init default 0
28843  * @cfg {Number} longitude Position when init default 0
28844  * @cfg {Number} zoom default 15
28845  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28846  * @cfg {Boolean} mapTypeControl default false
28847  * @cfg {Boolean} disableDoubleClickZoom default false
28848  * @cfg {Boolean} scrollwheel default true
28849  * @cfg {Boolean} streetViewControl default false
28850  * @cfg {Number} radius default 0
28851  * @cfg {String} locationName
28852  * @cfg {Boolean} draggable default true
28853  * @cfg {Boolean} enableAutocomplete default false
28854  * @cfg {Boolean} enableReverseGeocode default true
28855  * @cfg {String} markerTitle
28856  * 
28857  * @constructor
28858  * Create a new LocationPicker
28859  * @param {Object} config The config object
28860  */
28861
28862
28863 Roo.bootstrap.LocationPicker = function(config){
28864     
28865     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28866     
28867     this.addEvents({
28868         /**
28869          * @event initial
28870          * Fires when the picker initialized.
28871          * @param {Roo.bootstrap.LocationPicker} this
28872          * @param {Google Location} location
28873          */
28874         initial : true,
28875         /**
28876          * @event positionchanged
28877          * Fires when the picker position changed.
28878          * @param {Roo.bootstrap.LocationPicker} this
28879          * @param {Google Location} location
28880          */
28881         positionchanged : true,
28882         /**
28883          * @event resize
28884          * Fires when the map resize.
28885          * @param {Roo.bootstrap.LocationPicker} this
28886          */
28887         resize : true,
28888         /**
28889          * @event show
28890          * Fires when the map show.
28891          * @param {Roo.bootstrap.LocationPicker} this
28892          */
28893         show : true,
28894         /**
28895          * @event hide
28896          * Fires when the map hide.
28897          * @param {Roo.bootstrap.LocationPicker} this
28898          */
28899         hide : true,
28900         /**
28901          * @event mapClick
28902          * Fires when click the map.
28903          * @param {Roo.bootstrap.LocationPicker} this
28904          * @param {Map event} e
28905          */
28906         mapClick : true,
28907         /**
28908          * @event mapRightClick
28909          * Fires when right click the map.
28910          * @param {Roo.bootstrap.LocationPicker} this
28911          * @param {Map event} e
28912          */
28913         mapRightClick : true,
28914         /**
28915          * @event markerClick
28916          * Fires when click the marker.
28917          * @param {Roo.bootstrap.LocationPicker} this
28918          * @param {Map event} e
28919          */
28920         markerClick : true,
28921         /**
28922          * @event markerRightClick
28923          * Fires when right click the marker.
28924          * @param {Roo.bootstrap.LocationPicker} this
28925          * @param {Map event} e
28926          */
28927         markerRightClick : true,
28928         /**
28929          * @event OverlayViewDraw
28930          * Fires when OverlayView Draw
28931          * @param {Roo.bootstrap.LocationPicker} this
28932          */
28933         OverlayViewDraw : true,
28934         /**
28935          * @event OverlayViewOnAdd
28936          * Fires when OverlayView Draw
28937          * @param {Roo.bootstrap.LocationPicker} this
28938          */
28939         OverlayViewOnAdd : true,
28940         /**
28941          * @event OverlayViewOnRemove
28942          * Fires when OverlayView Draw
28943          * @param {Roo.bootstrap.LocationPicker} this
28944          */
28945         OverlayViewOnRemove : true,
28946         /**
28947          * @event OverlayViewShow
28948          * Fires when OverlayView Draw
28949          * @param {Roo.bootstrap.LocationPicker} this
28950          * @param {Pixel} cpx
28951          */
28952         OverlayViewShow : true,
28953         /**
28954          * @event OverlayViewHide
28955          * Fires when OverlayView Draw
28956          * @param {Roo.bootstrap.LocationPicker} this
28957          */
28958         OverlayViewHide : true,
28959         /**
28960          * @event loadexception
28961          * Fires when load google lib failed.
28962          * @param {Roo.bootstrap.LocationPicker} this
28963          */
28964         loadexception : true
28965     });
28966         
28967 };
28968
28969 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28970     
28971     gMapContext: false,
28972     
28973     latitude: 0,
28974     longitude: 0,
28975     zoom: 15,
28976     mapTypeId: false,
28977     mapTypeControl: false,
28978     disableDoubleClickZoom: false,
28979     scrollwheel: true,
28980     streetViewControl: false,
28981     radius: 0,
28982     locationName: '',
28983     draggable: true,
28984     enableAutocomplete: false,
28985     enableReverseGeocode: true,
28986     markerTitle: '',
28987     
28988     getAutoCreate: function()
28989     {
28990
28991         var cfg = {
28992             tag: 'div',
28993             cls: 'roo-location-picker'
28994         };
28995         
28996         return cfg
28997     },
28998     
28999     initEvents: function(ct, position)
29000     {       
29001         if(!this.el.getWidth() || this.isApplied()){
29002             return;
29003         }
29004         
29005         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29006         
29007         this.initial();
29008     },
29009     
29010     initial: function()
29011     {
29012         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29013             this.fireEvent('loadexception', this);
29014             return;
29015         }
29016         
29017         if(!this.mapTypeId){
29018             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29019         }
29020         
29021         this.gMapContext = this.GMapContext();
29022         
29023         this.initOverlayView();
29024         
29025         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29026         
29027         var _this = this;
29028                 
29029         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29030             _this.setPosition(_this.gMapContext.marker.position);
29031         });
29032         
29033         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29034             _this.fireEvent('mapClick', this, event);
29035             
29036         });
29037
29038         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29039             _this.fireEvent('mapRightClick', this, event);
29040             
29041         });
29042         
29043         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29044             _this.fireEvent('markerClick', this, event);
29045             
29046         });
29047
29048         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29049             _this.fireEvent('markerRightClick', this, event);
29050             
29051         });
29052         
29053         this.setPosition(this.gMapContext.location);
29054         
29055         this.fireEvent('initial', this, this.gMapContext.location);
29056     },
29057     
29058     initOverlayView: function()
29059     {
29060         var _this = this;
29061         
29062         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29063             
29064             draw: function()
29065             {
29066                 _this.fireEvent('OverlayViewDraw', _this);
29067             },
29068             
29069             onAdd: function()
29070             {
29071                 _this.fireEvent('OverlayViewOnAdd', _this);
29072             },
29073             
29074             onRemove: function()
29075             {
29076                 _this.fireEvent('OverlayViewOnRemove', _this);
29077             },
29078             
29079             show: function(cpx)
29080             {
29081                 _this.fireEvent('OverlayViewShow', _this, cpx);
29082             },
29083             
29084             hide: function()
29085             {
29086                 _this.fireEvent('OverlayViewHide', _this);
29087             }
29088             
29089         });
29090     },
29091     
29092     fromLatLngToContainerPixel: function(event)
29093     {
29094         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29095     },
29096     
29097     isApplied: function() 
29098     {
29099         return this.getGmapContext() == false ? false : true;
29100     },
29101     
29102     getGmapContext: function() 
29103     {
29104         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29105     },
29106     
29107     GMapContext: function() 
29108     {
29109         var position = new google.maps.LatLng(this.latitude, this.longitude);
29110         
29111         var _map = new google.maps.Map(this.el.dom, {
29112             center: position,
29113             zoom: this.zoom,
29114             mapTypeId: this.mapTypeId,
29115             mapTypeControl: this.mapTypeControl,
29116             disableDoubleClickZoom: this.disableDoubleClickZoom,
29117             scrollwheel: this.scrollwheel,
29118             streetViewControl: this.streetViewControl,
29119             locationName: this.locationName,
29120             draggable: this.draggable,
29121             enableAutocomplete: this.enableAutocomplete,
29122             enableReverseGeocode: this.enableReverseGeocode
29123         });
29124         
29125         var _marker = new google.maps.Marker({
29126             position: position,
29127             map: _map,
29128             title: this.markerTitle,
29129             draggable: this.draggable
29130         });
29131         
29132         return {
29133             map: _map,
29134             marker: _marker,
29135             circle: null,
29136             location: position,
29137             radius: this.radius,
29138             locationName: this.locationName,
29139             addressComponents: {
29140                 formatted_address: null,
29141                 addressLine1: null,
29142                 addressLine2: null,
29143                 streetName: null,
29144                 streetNumber: null,
29145                 city: null,
29146                 district: null,
29147                 state: null,
29148                 stateOrProvince: null
29149             },
29150             settings: this,
29151             domContainer: this.el.dom,
29152             geodecoder: new google.maps.Geocoder()
29153         };
29154     },
29155     
29156     drawCircle: function(center, radius, options) 
29157     {
29158         if (this.gMapContext.circle != null) {
29159             this.gMapContext.circle.setMap(null);
29160         }
29161         if (radius > 0) {
29162             radius *= 1;
29163             options = Roo.apply({}, options, {
29164                 strokeColor: "#0000FF",
29165                 strokeOpacity: .35,
29166                 strokeWeight: 2,
29167                 fillColor: "#0000FF",
29168                 fillOpacity: .2
29169             });
29170             
29171             options.map = this.gMapContext.map;
29172             options.radius = radius;
29173             options.center = center;
29174             this.gMapContext.circle = new google.maps.Circle(options);
29175             return this.gMapContext.circle;
29176         }
29177         
29178         return null;
29179     },
29180     
29181     setPosition: function(location) 
29182     {
29183         this.gMapContext.location = location;
29184         this.gMapContext.marker.setPosition(location);
29185         this.gMapContext.map.panTo(location);
29186         this.drawCircle(location, this.gMapContext.radius, {});
29187         
29188         var _this = this;
29189         
29190         if (this.gMapContext.settings.enableReverseGeocode) {
29191             this.gMapContext.geodecoder.geocode({
29192                 latLng: this.gMapContext.location
29193             }, function(results, status) {
29194                 
29195                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29196                     _this.gMapContext.locationName = results[0].formatted_address;
29197                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29198                     
29199                     _this.fireEvent('positionchanged', this, location);
29200                 }
29201             });
29202             
29203             return;
29204         }
29205         
29206         this.fireEvent('positionchanged', this, location);
29207     },
29208     
29209     resize: function()
29210     {
29211         google.maps.event.trigger(this.gMapContext.map, "resize");
29212         
29213         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29214         
29215         this.fireEvent('resize', this);
29216     },
29217     
29218     setPositionByLatLng: function(latitude, longitude)
29219     {
29220         this.setPosition(new google.maps.LatLng(latitude, longitude));
29221     },
29222     
29223     getCurrentPosition: function() 
29224     {
29225         return {
29226             latitude: this.gMapContext.location.lat(),
29227             longitude: this.gMapContext.location.lng()
29228         };
29229     },
29230     
29231     getAddressName: function() 
29232     {
29233         return this.gMapContext.locationName;
29234     },
29235     
29236     getAddressComponents: function() 
29237     {
29238         return this.gMapContext.addressComponents;
29239     },
29240     
29241     address_component_from_google_geocode: function(address_components) 
29242     {
29243         var result = {};
29244         
29245         for (var i = 0; i < address_components.length; i++) {
29246             var component = address_components[i];
29247             if (component.types.indexOf("postal_code") >= 0) {
29248                 result.postalCode = component.short_name;
29249             } else if (component.types.indexOf("street_number") >= 0) {
29250                 result.streetNumber = component.short_name;
29251             } else if (component.types.indexOf("route") >= 0) {
29252                 result.streetName = component.short_name;
29253             } else if (component.types.indexOf("neighborhood") >= 0) {
29254                 result.city = component.short_name;
29255             } else if (component.types.indexOf("locality") >= 0) {
29256                 result.city = component.short_name;
29257             } else if (component.types.indexOf("sublocality") >= 0) {
29258                 result.district = component.short_name;
29259             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29260                 result.stateOrProvince = component.short_name;
29261             } else if (component.types.indexOf("country") >= 0) {
29262                 result.country = component.short_name;
29263             }
29264         }
29265         
29266         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29267         result.addressLine2 = "";
29268         return result;
29269     },
29270     
29271     setZoomLevel: function(zoom)
29272     {
29273         this.gMapContext.map.setZoom(zoom);
29274     },
29275     
29276     show: function()
29277     {
29278         if(!this.el){
29279             return;
29280         }
29281         
29282         this.el.show();
29283         
29284         this.resize();
29285         
29286         this.fireEvent('show', this);
29287     },
29288     
29289     hide: function()
29290     {
29291         if(!this.el){
29292             return;
29293         }
29294         
29295         this.el.hide();
29296         
29297         this.fireEvent('hide', this);
29298     }
29299     
29300 });
29301
29302 Roo.apply(Roo.bootstrap.LocationPicker, {
29303     
29304     OverlayView : function(map, options)
29305     {
29306         options = options || {};
29307         
29308         this.setMap(map);
29309     }
29310     
29311     
29312 });/**
29313  * @class Roo.bootstrap.Alert
29314  * @extends Roo.bootstrap.Component
29315  * Bootstrap Alert class - shows an alert area box
29316  * eg
29317  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29318   Enter a valid email address
29319 </div>
29320  * @licence LGPL
29321  * @cfg {String} title The title of alert
29322  * @cfg {String} html The content of alert
29323  * @cfg {String} weight (  success | info | warning | danger )
29324  * @cfg {String} faicon font-awesomeicon
29325  * 
29326  * @constructor
29327  * Create a new alert
29328  * @param {Object} config The config object
29329  */
29330
29331
29332 Roo.bootstrap.Alert = function(config){
29333     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29334     
29335 };
29336
29337 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29338     
29339     title: '',
29340     html: '',
29341     weight: false,
29342     faicon: false,
29343     
29344     getAutoCreate : function()
29345     {
29346         
29347         var cfg = {
29348             tag : 'div',
29349             cls : 'alert',
29350             cn : [
29351                 {
29352                     tag : 'i',
29353                     cls : 'roo-alert-icon'
29354                     
29355                 },
29356                 {
29357                     tag : 'b',
29358                     cls : 'roo-alert-title',
29359                     html : this.title
29360                 },
29361                 {
29362                     tag : 'span',
29363                     cls : 'roo-alert-text',
29364                     html : this.html
29365                 }
29366             ]
29367         };
29368         
29369         if(this.faicon){
29370             cfg.cn[0].cls += ' fa ' + this.faicon;
29371         }
29372         
29373         if(this.weight){
29374             cfg.cls += ' alert-' + this.weight;
29375         }
29376         
29377         return cfg;
29378     },
29379     
29380     initEvents: function() 
29381     {
29382         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29383     },
29384     
29385     setTitle : function(str)
29386     {
29387         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29388     },
29389     
29390     setText : function(str)
29391     {
29392         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29393     },
29394     
29395     setWeight : function(weight)
29396     {
29397         if(this.weight){
29398             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29399         }
29400         
29401         this.weight = weight;
29402         
29403         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29404     },
29405     
29406     setIcon : function(icon)
29407     {
29408         if(this.faicon){
29409             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29410         }
29411         
29412         this.faicon = icon;
29413         
29414         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29415     },
29416     
29417     hide: function() 
29418     {
29419         this.el.hide();   
29420     },
29421     
29422     show: function() 
29423     {  
29424         this.el.show();   
29425     }
29426     
29427 });
29428
29429  
29430 /*
29431 * Licence: LGPL
29432 */
29433
29434 /**
29435  * @class Roo.bootstrap.UploadCropbox
29436  * @extends Roo.bootstrap.Component
29437  * Bootstrap UploadCropbox class
29438  * @cfg {String} emptyText show when image has been loaded
29439  * @cfg {String} rotateNotify show when image too small to rotate
29440  * @cfg {Number} errorTimeout default 3000
29441  * @cfg {Number} minWidth default 300
29442  * @cfg {Number} minHeight default 300
29443  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29444  * @cfg {Boolean} isDocument (true|false) default false
29445  * @cfg {String} url action url
29446  * @cfg {String} paramName default 'imageUpload'
29447  * @cfg {String} method default POST
29448  * @cfg {Boolean} loadMask (true|false) default true
29449  * @cfg {Boolean} loadingText default 'Loading...'
29450  * 
29451  * @constructor
29452  * Create a new UploadCropbox
29453  * @param {Object} config The config object
29454  */
29455
29456 Roo.bootstrap.UploadCropbox = function(config){
29457     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29458     
29459     this.addEvents({
29460         /**
29461          * @event beforeselectfile
29462          * Fire before select file
29463          * @param {Roo.bootstrap.UploadCropbox} this
29464          */
29465         "beforeselectfile" : true,
29466         /**
29467          * @event initial
29468          * Fire after initEvent
29469          * @param {Roo.bootstrap.UploadCropbox} this
29470          */
29471         "initial" : true,
29472         /**
29473          * @event crop
29474          * Fire after initEvent
29475          * @param {Roo.bootstrap.UploadCropbox} this
29476          * @param {String} data
29477          */
29478         "crop" : true,
29479         /**
29480          * @event prepare
29481          * Fire when preparing the file data
29482          * @param {Roo.bootstrap.UploadCropbox} this
29483          * @param {Object} file
29484          */
29485         "prepare" : true,
29486         /**
29487          * @event exception
29488          * Fire when get exception
29489          * @param {Roo.bootstrap.UploadCropbox} this
29490          * @param {XMLHttpRequest} xhr
29491          */
29492         "exception" : true,
29493         /**
29494          * @event beforeloadcanvas
29495          * Fire before load the canvas
29496          * @param {Roo.bootstrap.UploadCropbox} this
29497          * @param {String} src
29498          */
29499         "beforeloadcanvas" : true,
29500         /**
29501          * @event trash
29502          * Fire when trash image
29503          * @param {Roo.bootstrap.UploadCropbox} this
29504          */
29505         "trash" : true,
29506         /**
29507          * @event download
29508          * Fire when download the image
29509          * @param {Roo.bootstrap.UploadCropbox} this
29510          */
29511         "download" : true,
29512         /**
29513          * @event footerbuttonclick
29514          * Fire when footerbuttonclick
29515          * @param {Roo.bootstrap.UploadCropbox} this
29516          * @param {String} type
29517          */
29518         "footerbuttonclick" : true,
29519         /**
29520          * @event resize
29521          * Fire when resize
29522          * @param {Roo.bootstrap.UploadCropbox} this
29523          */
29524         "resize" : true,
29525         /**
29526          * @event rotate
29527          * Fire when rotate the image
29528          * @param {Roo.bootstrap.UploadCropbox} this
29529          * @param {String} pos
29530          */
29531         "rotate" : true,
29532         /**
29533          * @event inspect
29534          * Fire when inspect the file
29535          * @param {Roo.bootstrap.UploadCropbox} this
29536          * @param {Object} file
29537          */
29538         "inspect" : true,
29539         /**
29540          * @event upload
29541          * Fire when xhr upload the file
29542          * @param {Roo.bootstrap.UploadCropbox} this
29543          * @param {Object} data
29544          */
29545         "upload" : true,
29546         /**
29547          * @event arrange
29548          * Fire when arrange the file data
29549          * @param {Roo.bootstrap.UploadCropbox} this
29550          * @param {Object} formData
29551          */
29552         "arrange" : true
29553     });
29554     
29555     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29556 };
29557
29558 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29559     
29560     emptyText : 'Click to upload image',
29561     rotateNotify : 'Image is too small to rotate',
29562     errorTimeout : 3000,
29563     scale : 0,
29564     baseScale : 1,
29565     rotate : 0,
29566     dragable : false,
29567     pinching : false,
29568     mouseX : 0,
29569     mouseY : 0,
29570     cropData : false,
29571     minWidth : 300,
29572     minHeight : 300,
29573     file : false,
29574     exif : {},
29575     baseRotate : 1,
29576     cropType : 'image/jpeg',
29577     buttons : false,
29578     canvasLoaded : false,
29579     isDocument : false,
29580     method : 'POST',
29581     paramName : 'imageUpload',
29582     loadMask : true,
29583     loadingText : 'Loading...',
29584     maskEl : false,
29585     
29586     getAutoCreate : function()
29587     {
29588         var cfg = {
29589             tag : 'div',
29590             cls : 'roo-upload-cropbox',
29591             cn : [
29592                 {
29593                     tag : 'input',
29594                     cls : 'roo-upload-cropbox-selector',
29595                     type : 'file'
29596                 },
29597                 {
29598                     tag : 'div',
29599                     cls : 'roo-upload-cropbox-body',
29600                     style : 'cursor:pointer',
29601                     cn : [
29602                         {
29603                             tag : 'div',
29604                             cls : 'roo-upload-cropbox-preview'
29605                         },
29606                         {
29607                             tag : 'div',
29608                             cls : 'roo-upload-cropbox-thumb'
29609                         },
29610                         {
29611                             tag : 'div',
29612                             cls : 'roo-upload-cropbox-empty-notify',
29613                             html : this.emptyText
29614                         },
29615                         {
29616                             tag : 'div',
29617                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29618                             html : this.rotateNotify
29619                         }
29620                     ]
29621                 },
29622                 {
29623                     tag : 'div',
29624                     cls : 'roo-upload-cropbox-footer',
29625                     cn : {
29626                         tag : 'div',
29627                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29628                         cn : []
29629                     }
29630                 }
29631             ]
29632         };
29633         
29634         return cfg;
29635     },
29636     
29637     onRender : function(ct, position)
29638     {
29639         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29640         
29641         if (this.buttons.length) {
29642             
29643             Roo.each(this.buttons, function(bb) {
29644                 
29645                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29646                 
29647                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29648                 
29649             }, this);
29650         }
29651         
29652         if(this.loadMask){
29653             this.maskEl = this.el;
29654         }
29655     },
29656     
29657     initEvents : function()
29658     {
29659         this.urlAPI = (window.createObjectURL && window) || 
29660                                 (window.URL && URL.revokeObjectURL && URL) || 
29661                                 (window.webkitURL && webkitURL);
29662                         
29663         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29664         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29665         
29666         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29667         this.selectorEl.hide();
29668         
29669         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29670         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29671         
29672         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29673         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29674         this.thumbEl.hide();
29675         
29676         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29677         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29678         
29679         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29680         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29681         this.errorEl.hide();
29682         
29683         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29684         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29685         this.footerEl.hide();
29686         
29687         this.setThumbBoxSize();
29688         
29689         this.bind();
29690         
29691         this.resize();
29692         
29693         this.fireEvent('initial', this);
29694     },
29695
29696     bind : function()
29697     {
29698         var _this = this;
29699         
29700         window.addEventListener("resize", function() { _this.resize(); } );
29701         
29702         this.bodyEl.on('click', this.beforeSelectFile, this);
29703         
29704         if(Roo.isTouch){
29705             this.bodyEl.on('touchstart', this.onTouchStart, this);
29706             this.bodyEl.on('touchmove', this.onTouchMove, this);
29707             this.bodyEl.on('touchend', this.onTouchEnd, this);
29708         }
29709         
29710         if(!Roo.isTouch){
29711             this.bodyEl.on('mousedown', this.onMouseDown, this);
29712             this.bodyEl.on('mousemove', this.onMouseMove, this);
29713             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29714             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29715             Roo.get(document).on('mouseup', this.onMouseUp, this);
29716         }
29717         
29718         this.selectorEl.on('change', this.onFileSelected, this);
29719     },
29720     
29721     reset : function()
29722     {    
29723         this.scale = 0;
29724         this.baseScale = 1;
29725         this.rotate = 0;
29726         this.baseRotate = 1;
29727         this.dragable = false;
29728         this.pinching = false;
29729         this.mouseX = 0;
29730         this.mouseY = 0;
29731         this.cropData = false;
29732         this.notifyEl.dom.innerHTML = this.emptyText;
29733         
29734         this.selectorEl.dom.value = '';
29735         
29736     },
29737     
29738     resize : function()
29739     {
29740         if(this.fireEvent('resize', this) != false){
29741             this.setThumbBoxPosition();
29742             this.setCanvasPosition();
29743         }
29744     },
29745     
29746     onFooterButtonClick : function(e, el, o, type)
29747     {
29748         switch (type) {
29749             case 'rotate-left' :
29750                 this.onRotateLeft(e);
29751                 break;
29752             case 'rotate-right' :
29753                 this.onRotateRight(e);
29754                 break;
29755             case 'picture' :
29756                 this.beforeSelectFile(e);
29757                 break;
29758             case 'trash' :
29759                 this.trash(e);
29760                 break;
29761             case 'crop' :
29762                 this.crop(e);
29763                 break;
29764             case 'download' :
29765                 this.download(e);
29766                 break;
29767             default :
29768                 break;
29769         }
29770         
29771         this.fireEvent('footerbuttonclick', this, type);
29772     },
29773     
29774     beforeSelectFile : function(e)
29775     {
29776         e.preventDefault();
29777         
29778         if(this.fireEvent('beforeselectfile', this) != false){
29779             this.selectorEl.dom.click();
29780         }
29781     },
29782     
29783     onFileSelected : function(e)
29784     {
29785         e.preventDefault();
29786         
29787         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29788             return;
29789         }
29790         
29791         var file = this.selectorEl.dom.files[0];
29792         
29793         if(this.fireEvent('inspect', this, file) != false){
29794             this.prepare(file);
29795         }
29796         
29797     },
29798     
29799     trash : function(e)
29800     {
29801         this.fireEvent('trash', this);
29802     },
29803     
29804     download : function(e)
29805     {
29806         this.fireEvent('download', this);
29807     },
29808     
29809     loadCanvas : function(src)
29810     {   
29811         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29812             
29813             this.reset();
29814             
29815             this.imageEl = document.createElement('img');
29816             
29817             var _this = this;
29818             
29819             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29820             
29821             this.imageEl.src = src;
29822         }
29823     },
29824     
29825     onLoadCanvas : function()
29826     {   
29827         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29828         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29829         
29830         this.bodyEl.un('click', this.beforeSelectFile, this);
29831         
29832         this.notifyEl.hide();
29833         this.thumbEl.show();
29834         this.footerEl.show();
29835         
29836         this.baseRotateLevel();
29837         
29838         if(this.isDocument){
29839             this.setThumbBoxSize();
29840         }
29841         
29842         this.setThumbBoxPosition();
29843         
29844         this.baseScaleLevel();
29845         
29846         this.draw();
29847         
29848         this.resize();
29849         
29850         this.canvasLoaded = true;
29851         
29852         if(this.loadMask){
29853             this.maskEl.unmask();
29854         }
29855         
29856     },
29857     
29858     setCanvasPosition : function()
29859     {   
29860         if(!this.canvasEl){
29861             return;
29862         }
29863         
29864         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29865         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29866         
29867         this.previewEl.setLeft(pw);
29868         this.previewEl.setTop(ph);
29869         
29870     },
29871     
29872     onMouseDown : function(e)
29873     {   
29874         e.stopEvent();
29875         
29876         this.dragable = true;
29877         this.pinching = false;
29878         
29879         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29880             this.dragable = false;
29881             return;
29882         }
29883         
29884         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29885         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29886         
29887     },
29888     
29889     onMouseMove : function(e)
29890     {   
29891         e.stopEvent();
29892         
29893         if(!this.canvasLoaded){
29894             return;
29895         }
29896         
29897         if (!this.dragable){
29898             return;
29899         }
29900         
29901         var minX = Math.ceil(this.thumbEl.getLeft(true));
29902         var minY = Math.ceil(this.thumbEl.getTop(true));
29903         
29904         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29905         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29906         
29907         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29908         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29909         
29910         x = x - this.mouseX;
29911         y = y - this.mouseY;
29912         
29913         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29914         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29915         
29916         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29917         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29918         
29919         this.previewEl.setLeft(bgX);
29920         this.previewEl.setTop(bgY);
29921         
29922         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29923         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29924     },
29925     
29926     onMouseUp : function(e)
29927     {   
29928         e.stopEvent();
29929         
29930         this.dragable = false;
29931     },
29932     
29933     onMouseWheel : function(e)
29934     {   
29935         e.stopEvent();
29936         
29937         this.startScale = this.scale;
29938         
29939         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29940         
29941         if(!this.zoomable()){
29942             this.scale = this.startScale;
29943             return;
29944         }
29945         
29946         this.draw();
29947         
29948         return;
29949     },
29950     
29951     zoomable : function()
29952     {
29953         var minScale = this.thumbEl.getWidth() / this.minWidth;
29954         
29955         if(this.minWidth < this.minHeight){
29956             minScale = this.thumbEl.getHeight() / this.minHeight;
29957         }
29958         
29959         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29960         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29961         
29962         if(
29963                 this.isDocument &&
29964                 (this.rotate == 0 || this.rotate == 180) && 
29965                 (
29966                     width > this.imageEl.OriginWidth || 
29967                     height > this.imageEl.OriginHeight ||
29968                     (width < this.minWidth && height < this.minHeight)
29969                 )
29970         ){
29971             return false;
29972         }
29973         
29974         if(
29975                 this.isDocument &&
29976                 (this.rotate == 90 || this.rotate == 270) && 
29977                 (
29978                     width > this.imageEl.OriginWidth || 
29979                     height > this.imageEl.OriginHeight ||
29980                     (width < this.minHeight && height < this.minWidth)
29981                 )
29982         ){
29983             return false;
29984         }
29985         
29986         if(
29987                 !this.isDocument &&
29988                 (this.rotate == 0 || this.rotate == 180) && 
29989                 (
29990                     width < this.minWidth || 
29991                     width > this.imageEl.OriginWidth || 
29992                     height < this.minHeight || 
29993                     height > this.imageEl.OriginHeight
29994                 )
29995         ){
29996             return false;
29997         }
29998         
29999         if(
30000                 !this.isDocument &&
30001                 (this.rotate == 90 || this.rotate == 270) && 
30002                 (
30003                     width < this.minHeight || 
30004                     width > this.imageEl.OriginWidth || 
30005                     height < this.minWidth || 
30006                     height > this.imageEl.OriginHeight
30007                 )
30008         ){
30009             return false;
30010         }
30011         
30012         return true;
30013         
30014     },
30015     
30016     onRotateLeft : function(e)
30017     {   
30018         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30019             
30020             var minScale = this.thumbEl.getWidth() / this.minWidth;
30021             
30022             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30023             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30024             
30025             this.startScale = this.scale;
30026             
30027             while (this.getScaleLevel() < minScale){
30028             
30029                 this.scale = this.scale + 1;
30030                 
30031                 if(!this.zoomable()){
30032                     break;
30033                 }
30034                 
30035                 if(
30036                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30037                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30038                 ){
30039                     continue;
30040                 }
30041                 
30042                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30043
30044                 this.draw();
30045                 
30046                 return;
30047             }
30048             
30049             this.scale = this.startScale;
30050             
30051             this.onRotateFail();
30052             
30053             return false;
30054         }
30055         
30056         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30057
30058         if(this.isDocument){
30059             this.setThumbBoxSize();
30060             this.setThumbBoxPosition();
30061             this.setCanvasPosition();
30062         }
30063         
30064         this.draw();
30065         
30066         this.fireEvent('rotate', this, 'left');
30067         
30068     },
30069     
30070     onRotateRight : function(e)
30071     {
30072         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30073             
30074             var minScale = this.thumbEl.getWidth() / this.minWidth;
30075         
30076             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30077             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30078             
30079             this.startScale = this.scale;
30080             
30081             while (this.getScaleLevel() < minScale){
30082             
30083                 this.scale = this.scale + 1;
30084                 
30085                 if(!this.zoomable()){
30086                     break;
30087                 }
30088                 
30089                 if(
30090                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30091                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30092                 ){
30093                     continue;
30094                 }
30095                 
30096                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30097
30098                 this.draw();
30099                 
30100                 return;
30101             }
30102             
30103             this.scale = this.startScale;
30104             
30105             this.onRotateFail();
30106             
30107             return false;
30108         }
30109         
30110         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30111
30112         if(this.isDocument){
30113             this.setThumbBoxSize();
30114             this.setThumbBoxPosition();
30115             this.setCanvasPosition();
30116         }
30117         
30118         this.draw();
30119         
30120         this.fireEvent('rotate', this, 'right');
30121     },
30122     
30123     onRotateFail : function()
30124     {
30125         this.errorEl.show(true);
30126         
30127         var _this = this;
30128         
30129         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30130     },
30131     
30132     draw : function()
30133     {
30134         this.previewEl.dom.innerHTML = '';
30135         
30136         var canvasEl = document.createElement("canvas");
30137         
30138         var contextEl = canvasEl.getContext("2d");
30139         
30140         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30141         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30142         var center = this.imageEl.OriginWidth / 2;
30143         
30144         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30145             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30146             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30147             center = this.imageEl.OriginHeight / 2;
30148         }
30149         
30150         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30151         
30152         contextEl.translate(center, center);
30153         contextEl.rotate(this.rotate * Math.PI / 180);
30154
30155         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30156         
30157         this.canvasEl = document.createElement("canvas");
30158         
30159         this.contextEl = this.canvasEl.getContext("2d");
30160         
30161         switch (this.rotate) {
30162             case 0 :
30163                 
30164                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30165                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30166                 
30167                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30168                 
30169                 break;
30170             case 90 : 
30171                 
30172                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30173                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30174                 
30175                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30176                     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);
30177                     break;
30178                 }
30179                 
30180                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30181                 
30182                 break;
30183             case 180 :
30184                 
30185                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30186                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30187                 
30188                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30189                     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);
30190                     break;
30191                 }
30192                 
30193                 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);
30194                 
30195                 break;
30196             case 270 :
30197                 
30198                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30199                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30200         
30201                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30202                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30203                     break;
30204                 }
30205                 
30206                 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);
30207                 
30208                 break;
30209             default : 
30210                 break;
30211         }
30212         
30213         this.previewEl.appendChild(this.canvasEl);
30214         
30215         this.setCanvasPosition();
30216     },
30217     
30218     crop : function()
30219     {
30220         if(!this.canvasLoaded){
30221             return;
30222         }
30223         
30224         var imageCanvas = document.createElement("canvas");
30225         
30226         var imageContext = imageCanvas.getContext("2d");
30227         
30228         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30229         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30230         
30231         var center = imageCanvas.width / 2;
30232         
30233         imageContext.translate(center, center);
30234         
30235         imageContext.rotate(this.rotate * Math.PI / 180);
30236         
30237         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30238         
30239         var canvas = document.createElement("canvas");
30240         
30241         var context = canvas.getContext("2d");
30242                 
30243         canvas.width = this.minWidth;
30244         canvas.height = this.minHeight;
30245
30246         switch (this.rotate) {
30247             case 0 :
30248                 
30249                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30250                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30251                 
30252                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30253                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30254                 
30255                 var targetWidth = this.minWidth - 2 * x;
30256                 var targetHeight = this.minHeight - 2 * y;
30257                 
30258                 var scale = 1;
30259                 
30260                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30261                     scale = targetWidth / width;
30262                 }
30263                 
30264                 if(x > 0 && y == 0){
30265                     scale = targetHeight / height;
30266                 }
30267                 
30268                 if(x > 0 && y > 0){
30269                     scale = targetWidth / width;
30270                     
30271                     if(width < height){
30272                         scale = targetHeight / height;
30273                     }
30274                 }
30275                 
30276                 context.scale(scale, scale);
30277                 
30278                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30279                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30280
30281                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30282                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30283
30284                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30285                 
30286                 break;
30287             case 90 : 
30288                 
30289                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30290                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30291                 
30292                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30293                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30294                 
30295                 var targetWidth = this.minWidth - 2 * x;
30296                 var targetHeight = this.minHeight - 2 * y;
30297                 
30298                 var scale = 1;
30299                 
30300                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30301                     scale = targetWidth / width;
30302                 }
30303                 
30304                 if(x > 0 && y == 0){
30305                     scale = targetHeight / height;
30306                 }
30307                 
30308                 if(x > 0 && y > 0){
30309                     scale = targetWidth / width;
30310                     
30311                     if(width < height){
30312                         scale = targetHeight / height;
30313                     }
30314                 }
30315                 
30316                 context.scale(scale, scale);
30317                 
30318                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30319                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30320
30321                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30322                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30323                 
30324                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30325                 
30326                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30327                 
30328                 break;
30329             case 180 :
30330                 
30331                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30332                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30333                 
30334                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30335                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30336                 
30337                 var targetWidth = this.minWidth - 2 * x;
30338                 var targetHeight = this.minHeight - 2 * y;
30339                 
30340                 var scale = 1;
30341                 
30342                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30343                     scale = targetWidth / width;
30344                 }
30345                 
30346                 if(x > 0 && y == 0){
30347                     scale = targetHeight / height;
30348                 }
30349                 
30350                 if(x > 0 && y > 0){
30351                     scale = targetWidth / width;
30352                     
30353                     if(width < height){
30354                         scale = targetHeight / height;
30355                     }
30356                 }
30357                 
30358                 context.scale(scale, scale);
30359                 
30360                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30361                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30362
30363                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30364                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30365
30366                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30367                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30368                 
30369                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30370                 
30371                 break;
30372             case 270 :
30373                 
30374                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30375                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30376                 
30377                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30378                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30379                 
30380                 var targetWidth = this.minWidth - 2 * x;
30381                 var targetHeight = this.minHeight - 2 * y;
30382                 
30383                 var scale = 1;
30384                 
30385                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30386                     scale = targetWidth / width;
30387                 }
30388                 
30389                 if(x > 0 && y == 0){
30390                     scale = targetHeight / height;
30391                 }
30392                 
30393                 if(x > 0 && y > 0){
30394                     scale = targetWidth / width;
30395                     
30396                     if(width < height){
30397                         scale = targetHeight / height;
30398                     }
30399                 }
30400                 
30401                 context.scale(scale, scale);
30402                 
30403                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30404                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30405
30406                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30407                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30408                 
30409                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30410                 
30411                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30412                 
30413                 break;
30414             default : 
30415                 break;
30416         }
30417         
30418         this.cropData = canvas.toDataURL(this.cropType);
30419         
30420         if(this.fireEvent('crop', this, this.cropData) !== false){
30421             this.process(this.file, this.cropData);
30422         }
30423         
30424         return;
30425         
30426     },
30427     
30428     setThumbBoxSize : function()
30429     {
30430         var width, height;
30431         
30432         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30433             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30434             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30435             
30436             this.minWidth = width;
30437             this.minHeight = height;
30438             
30439             if(this.rotate == 90 || this.rotate == 270){
30440                 this.minWidth = height;
30441                 this.minHeight = width;
30442             }
30443         }
30444         
30445         height = 300;
30446         width = Math.ceil(this.minWidth * height / this.minHeight);
30447         
30448         if(this.minWidth > this.minHeight){
30449             width = 300;
30450             height = Math.ceil(this.minHeight * width / this.minWidth);
30451         }
30452         
30453         this.thumbEl.setStyle({
30454             width : width + 'px',
30455             height : height + 'px'
30456         });
30457
30458         return;
30459             
30460     },
30461     
30462     setThumbBoxPosition : function()
30463     {
30464         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30465         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30466         
30467         this.thumbEl.setLeft(x);
30468         this.thumbEl.setTop(y);
30469         
30470     },
30471     
30472     baseRotateLevel : function()
30473     {
30474         this.baseRotate = 1;
30475         
30476         if(
30477                 typeof(this.exif) != 'undefined' &&
30478                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30479                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30480         ){
30481             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30482         }
30483         
30484         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30485         
30486     },
30487     
30488     baseScaleLevel : function()
30489     {
30490         var width, height;
30491         
30492         if(this.isDocument){
30493             
30494             if(this.baseRotate == 6 || this.baseRotate == 8){
30495             
30496                 height = this.thumbEl.getHeight();
30497                 this.baseScale = height / this.imageEl.OriginWidth;
30498
30499                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30500                     width = this.thumbEl.getWidth();
30501                     this.baseScale = width / this.imageEl.OriginHeight;
30502                 }
30503
30504                 return;
30505             }
30506
30507             height = this.thumbEl.getHeight();
30508             this.baseScale = height / this.imageEl.OriginHeight;
30509
30510             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30511                 width = this.thumbEl.getWidth();
30512                 this.baseScale = width / this.imageEl.OriginWidth;
30513             }
30514
30515             return;
30516         }
30517         
30518         if(this.baseRotate == 6 || this.baseRotate == 8){
30519             
30520             width = this.thumbEl.getHeight();
30521             this.baseScale = width / this.imageEl.OriginHeight;
30522             
30523             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30524                 height = this.thumbEl.getWidth();
30525                 this.baseScale = height / this.imageEl.OriginHeight;
30526             }
30527             
30528             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30529                 height = this.thumbEl.getWidth();
30530                 this.baseScale = height / this.imageEl.OriginHeight;
30531                 
30532                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30533                     width = this.thumbEl.getHeight();
30534                     this.baseScale = width / this.imageEl.OriginWidth;
30535                 }
30536             }
30537             
30538             return;
30539         }
30540         
30541         width = this.thumbEl.getWidth();
30542         this.baseScale = width / this.imageEl.OriginWidth;
30543         
30544         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30545             height = this.thumbEl.getHeight();
30546             this.baseScale = height / this.imageEl.OriginHeight;
30547         }
30548         
30549         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30550             
30551             height = this.thumbEl.getHeight();
30552             this.baseScale = height / this.imageEl.OriginHeight;
30553             
30554             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30555                 width = this.thumbEl.getWidth();
30556                 this.baseScale = width / this.imageEl.OriginWidth;
30557             }
30558             
30559         }
30560         
30561         return;
30562     },
30563     
30564     getScaleLevel : function()
30565     {
30566         return this.baseScale * Math.pow(1.1, this.scale);
30567     },
30568     
30569     onTouchStart : function(e)
30570     {
30571         if(!this.canvasLoaded){
30572             this.beforeSelectFile(e);
30573             return;
30574         }
30575         
30576         var touches = e.browserEvent.touches;
30577         
30578         if(!touches){
30579             return;
30580         }
30581         
30582         if(touches.length == 1){
30583             this.onMouseDown(e);
30584             return;
30585         }
30586         
30587         if(touches.length != 2){
30588             return;
30589         }
30590         
30591         var coords = [];
30592         
30593         for(var i = 0, finger; finger = touches[i]; i++){
30594             coords.push(finger.pageX, finger.pageY);
30595         }
30596         
30597         var x = Math.pow(coords[0] - coords[2], 2);
30598         var y = Math.pow(coords[1] - coords[3], 2);
30599         
30600         this.startDistance = Math.sqrt(x + y);
30601         
30602         this.startScale = this.scale;
30603         
30604         this.pinching = true;
30605         this.dragable = false;
30606         
30607     },
30608     
30609     onTouchMove : function(e)
30610     {
30611         if(!this.pinching && !this.dragable){
30612             return;
30613         }
30614         
30615         var touches = e.browserEvent.touches;
30616         
30617         if(!touches){
30618             return;
30619         }
30620         
30621         if(this.dragable){
30622             this.onMouseMove(e);
30623             return;
30624         }
30625         
30626         var coords = [];
30627         
30628         for(var i = 0, finger; finger = touches[i]; i++){
30629             coords.push(finger.pageX, finger.pageY);
30630         }
30631         
30632         var x = Math.pow(coords[0] - coords[2], 2);
30633         var y = Math.pow(coords[1] - coords[3], 2);
30634         
30635         this.endDistance = Math.sqrt(x + y);
30636         
30637         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30638         
30639         if(!this.zoomable()){
30640             this.scale = this.startScale;
30641             return;
30642         }
30643         
30644         this.draw();
30645         
30646     },
30647     
30648     onTouchEnd : function(e)
30649     {
30650         this.pinching = false;
30651         this.dragable = false;
30652         
30653     },
30654     
30655     process : function(file, crop)
30656     {
30657         if(this.loadMask){
30658             this.maskEl.mask(this.loadingText);
30659         }
30660         
30661         this.xhr = new XMLHttpRequest();
30662         
30663         file.xhr = this.xhr;
30664
30665         this.xhr.open(this.method, this.url, true);
30666         
30667         var headers = {
30668             "Accept": "application/json",
30669             "Cache-Control": "no-cache",
30670             "X-Requested-With": "XMLHttpRequest"
30671         };
30672         
30673         for (var headerName in headers) {
30674             var headerValue = headers[headerName];
30675             if (headerValue) {
30676                 this.xhr.setRequestHeader(headerName, headerValue);
30677             }
30678         }
30679         
30680         var _this = this;
30681         
30682         this.xhr.onload = function()
30683         {
30684             _this.xhrOnLoad(_this.xhr);
30685         }
30686         
30687         this.xhr.onerror = function()
30688         {
30689             _this.xhrOnError(_this.xhr);
30690         }
30691         
30692         var formData = new FormData();
30693
30694         formData.append('returnHTML', 'NO');
30695         
30696         if(crop){
30697             formData.append('crop', crop);
30698         }
30699         
30700         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30701             formData.append(this.paramName, file, file.name);
30702         }
30703         
30704         if(typeof(file.filename) != 'undefined'){
30705             formData.append('filename', file.filename);
30706         }
30707         
30708         if(typeof(file.mimetype) != 'undefined'){
30709             formData.append('mimetype', file.mimetype);
30710         }
30711         
30712         if(this.fireEvent('arrange', this, formData) != false){
30713             this.xhr.send(formData);
30714         };
30715     },
30716     
30717     xhrOnLoad : function(xhr)
30718     {
30719         if(this.loadMask){
30720             this.maskEl.unmask();
30721         }
30722         
30723         if (xhr.readyState !== 4) {
30724             this.fireEvent('exception', this, xhr);
30725             return;
30726         }
30727
30728         var response = Roo.decode(xhr.responseText);
30729         
30730         if(!response.success){
30731             this.fireEvent('exception', this, xhr);
30732             return;
30733         }
30734         
30735         var response = Roo.decode(xhr.responseText);
30736         
30737         this.fireEvent('upload', this, response);
30738         
30739     },
30740     
30741     xhrOnError : function()
30742     {
30743         if(this.loadMask){
30744             this.maskEl.unmask();
30745         }
30746         
30747         Roo.log('xhr on error');
30748         
30749         var response = Roo.decode(xhr.responseText);
30750           
30751         Roo.log(response);
30752         
30753     },
30754     
30755     prepare : function(file)
30756     {   
30757         if(this.loadMask){
30758             this.maskEl.mask(this.loadingText);
30759         }
30760         
30761         this.file = false;
30762         this.exif = {};
30763         
30764         if(typeof(file) === 'string'){
30765             this.loadCanvas(file);
30766             return;
30767         }
30768         
30769         if(!file || !this.urlAPI){
30770             return;
30771         }
30772         
30773         this.file = file;
30774         this.cropType = file.type;
30775         
30776         var _this = this;
30777         
30778         if(this.fireEvent('prepare', this, this.file) != false){
30779             
30780             var reader = new FileReader();
30781             
30782             reader.onload = function (e) {
30783                 if (e.target.error) {
30784                     Roo.log(e.target.error);
30785                     return;
30786                 }
30787                 
30788                 var buffer = e.target.result,
30789                     dataView = new DataView(buffer),
30790                     offset = 2,
30791                     maxOffset = dataView.byteLength - 4,
30792                     markerBytes,
30793                     markerLength;
30794                 
30795                 if (dataView.getUint16(0) === 0xffd8) {
30796                     while (offset < maxOffset) {
30797                         markerBytes = dataView.getUint16(offset);
30798                         
30799                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30800                             markerLength = dataView.getUint16(offset + 2) + 2;
30801                             if (offset + markerLength > dataView.byteLength) {
30802                                 Roo.log('Invalid meta data: Invalid segment size.');
30803                                 break;
30804                             }
30805                             
30806                             if(markerBytes == 0xffe1){
30807                                 _this.parseExifData(
30808                                     dataView,
30809                                     offset,
30810                                     markerLength
30811                                 );
30812                             }
30813                             
30814                             offset += markerLength;
30815                             
30816                             continue;
30817                         }
30818                         
30819                         break;
30820                     }
30821                     
30822                 }
30823                 
30824                 var url = _this.urlAPI.createObjectURL(_this.file);
30825                 
30826                 _this.loadCanvas(url);
30827                 
30828                 return;
30829             }
30830             
30831             reader.readAsArrayBuffer(this.file);
30832             
30833         }
30834         
30835     },
30836     
30837     parseExifData : function(dataView, offset, length)
30838     {
30839         var tiffOffset = offset + 10,
30840             littleEndian,
30841             dirOffset;
30842     
30843         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30844             // No Exif data, might be XMP data instead
30845             return;
30846         }
30847         
30848         // Check for the ASCII code for "Exif" (0x45786966):
30849         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30850             // No Exif data, might be XMP data instead
30851             return;
30852         }
30853         if (tiffOffset + 8 > dataView.byteLength) {
30854             Roo.log('Invalid Exif data: Invalid segment size.');
30855             return;
30856         }
30857         // Check for the two null bytes:
30858         if (dataView.getUint16(offset + 8) !== 0x0000) {
30859             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30860             return;
30861         }
30862         // Check the byte alignment:
30863         switch (dataView.getUint16(tiffOffset)) {
30864         case 0x4949:
30865             littleEndian = true;
30866             break;
30867         case 0x4D4D:
30868             littleEndian = false;
30869             break;
30870         default:
30871             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30872             return;
30873         }
30874         // Check for the TIFF tag marker (0x002A):
30875         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30876             Roo.log('Invalid Exif data: Missing TIFF marker.');
30877             return;
30878         }
30879         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30880         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30881         
30882         this.parseExifTags(
30883             dataView,
30884             tiffOffset,
30885             tiffOffset + dirOffset,
30886             littleEndian
30887         );
30888     },
30889     
30890     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30891     {
30892         var tagsNumber,
30893             dirEndOffset,
30894             i;
30895         if (dirOffset + 6 > dataView.byteLength) {
30896             Roo.log('Invalid Exif data: Invalid directory offset.');
30897             return;
30898         }
30899         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30900         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30901         if (dirEndOffset + 4 > dataView.byteLength) {
30902             Roo.log('Invalid Exif data: Invalid directory size.');
30903             return;
30904         }
30905         for (i = 0; i < tagsNumber; i += 1) {
30906             this.parseExifTag(
30907                 dataView,
30908                 tiffOffset,
30909                 dirOffset + 2 + 12 * i, // tag offset
30910                 littleEndian
30911             );
30912         }
30913         // Return the offset to the next directory:
30914         return dataView.getUint32(dirEndOffset, littleEndian);
30915     },
30916     
30917     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30918     {
30919         var tag = dataView.getUint16(offset, littleEndian);
30920         
30921         this.exif[tag] = this.getExifValue(
30922             dataView,
30923             tiffOffset,
30924             offset,
30925             dataView.getUint16(offset + 2, littleEndian), // tag type
30926             dataView.getUint32(offset + 4, littleEndian), // tag length
30927             littleEndian
30928         );
30929     },
30930     
30931     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30932     {
30933         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30934             tagSize,
30935             dataOffset,
30936             values,
30937             i,
30938             str,
30939             c;
30940     
30941         if (!tagType) {
30942             Roo.log('Invalid Exif data: Invalid tag type.');
30943             return;
30944         }
30945         
30946         tagSize = tagType.size * length;
30947         // Determine if the value is contained in the dataOffset bytes,
30948         // or if the value at the dataOffset is a pointer to the actual data:
30949         dataOffset = tagSize > 4 ?
30950                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30951         if (dataOffset + tagSize > dataView.byteLength) {
30952             Roo.log('Invalid Exif data: Invalid data offset.');
30953             return;
30954         }
30955         if (length === 1) {
30956             return tagType.getValue(dataView, dataOffset, littleEndian);
30957         }
30958         values = [];
30959         for (i = 0; i < length; i += 1) {
30960             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30961         }
30962         
30963         if (tagType.ascii) {
30964             str = '';
30965             // Concatenate the chars:
30966             for (i = 0; i < values.length; i += 1) {
30967                 c = values[i];
30968                 // Ignore the terminating NULL byte(s):
30969                 if (c === '\u0000') {
30970                     break;
30971                 }
30972                 str += c;
30973             }
30974             return str;
30975         }
30976         return values;
30977     }
30978     
30979 });
30980
30981 Roo.apply(Roo.bootstrap.UploadCropbox, {
30982     tags : {
30983         'Orientation': 0x0112
30984     },
30985     
30986     Orientation: {
30987             1: 0, //'top-left',
30988 //            2: 'top-right',
30989             3: 180, //'bottom-right',
30990 //            4: 'bottom-left',
30991 //            5: 'left-top',
30992             6: 90, //'right-top',
30993 //            7: 'right-bottom',
30994             8: 270 //'left-bottom'
30995     },
30996     
30997     exifTagTypes : {
30998         // byte, 8-bit unsigned int:
30999         1: {
31000             getValue: function (dataView, dataOffset) {
31001                 return dataView.getUint8(dataOffset);
31002             },
31003             size: 1
31004         },
31005         // ascii, 8-bit byte:
31006         2: {
31007             getValue: function (dataView, dataOffset) {
31008                 return String.fromCharCode(dataView.getUint8(dataOffset));
31009             },
31010             size: 1,
31011             ascii: true
31012         },
31013         // short, 16 bit int:
31014         3: {
31015             getValue: function (dataView, dataOffset, littleEndian) {
31016                 return dataView.getUint16(dataOffset, littleEndian);
31017             },
31018             size: 2
31019         },
31020         // long, 32 bit int:
31021         4: {
31022             getValue: function (dataView, dataOffset, littleEndian) {
31023                 return dataView.getUint32(dataOffset, littleEndian);
31024             },
31025             size: 4
31026         },
31027         // rational = two long values, first is numerator, second is denominator:
31028         5: {
31029             getValue: function (dataView, dataOffset, littleEndian) {
31030                 return dataView.getUint32(dataOffset, littleEndian) /
31031                     dataView.getUint32(dataOffset + 4, littleEndian);
31032             },
31033             size: 8
31034         },
31035         // slong, 32 bit signed int:
31036         9: {
31037             getValue: function (dataView, dataOffset, littleEndian) {
31038                 return dataView.getInt32(dataOffset, littleEndian);
31039             },
31040             size: 4
31041         },
31042         // srational, two slongs, first is numerator, second is denominator:
31043         10: {
31044             getValue: function (dataView, dataOffset, littleEndian) {
31045                 return dataView.getInt32(dataOffset, littleEndian) /
31046                     dataView.getInt32(dataOffset + 4, littleEndian);
31047             },
31048             size: 8
31049         }
31050     },
31051     
31052     footer : {
31053         STANDARD : [
31054             {
31055                 tag : 'div',
31056                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31057                 action : 'rotate-left',
31058                 cn : [
31059                     {
31060                         tag : 'button',
31061                         cls : 'btn btn-default',
31062                         html : '<i class="fa fa-undo"></i>'
31063                     }
31064                 ]
31065             },
31066             {
31067                 tag : 'div',
31068                 cls : 'btn-group roo-upload-cropbox-picture',
31069                 action : 'picture',
31070                 cn : [
31071                     {
31072                         tag : 'button',
31073                         cls : 'btn btn-default',
31074                         html : '<i class="fa fa-picture-o"></i>'
31075                     }
31076                 ]
31077             },
31078             {
31079                 tag : 'div',
31080                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31081                 action : 'rotate-right',
31082                 cn : [
31083                     {
31084                         tag : 'button',
31085                         cls : 'btn btn-default',
31086                         html : '<i class="fa fa-repeat"></i>'
31087                     }
31088                 ]
31089             }
31090         ],
31091         DOCUMENT : [
31092             {
31093                 tag : 'div',
31094                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31095                 action : 'rotate-left',
31096                 cn : [
31097                     {
31098                         tag : 'button',
31099                         cls : 'btn btn-default',
31100                         html : '<i class="fa fa-undo"></i>'
31101                     }
31102                 ]
31103             },
31104             {
31105                 tag : 'div',
31106                 cls : 'btn-group roo-upload-cropbox-download',
31107                 action : 'download',
31108                 cn : [
31109                     {
31110                         tag : 'button',
31111                         cls : 'btn btn-default',
31112                         html : '<i class="fa fa-download"></i>'
31113                     }
31114                 ]
31115             },
31116             {
31117                 tag : 'div',
31118                 cls : 'btn-group roo-upload-cropbox-crop',
31119                 action : 'crop',
31120                 cn : [
31121                     {
31122                         tag : 'button',
31123                         cls : 'btn btn-default',
31124                         html : '<i class="fa fa-crop"></i>'
31125                     }
31126                 ]
31127             },
31128             {
31129                 tag : 'div',
31130                 cls : 'btn-group roo-upload-cropbox-trash',
31131                 action : 'trash',
31132                 cn : [
31133                     {
31134                         tag : 'button',
31135                         cls : 'btn btn-default',
31136                         html : '<i class="fa fa-trash"></i>'
31137                     }
31138                 ]
31139             },
31140             {
31141                 tag : 'div',
31142                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31143                 action : 'rotate-right',
31144                 cn : [
31145                     {
31146                         tag : 'button',
31147                         cls : 'btn btn-default',
31148                         html : '<i class="fa fa-repeat"></i>'
31149                     }
31150                 ]
31151             }
31152         ],
31153         ROTATOR : [
31154             {
31155                 tag : 'div',
31156                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31157                 action : 'rotate-left',
31158                 cn : [
31159                     {
31160                         tag : 'button',
31161                         cls : 'btn btn-default',
31162                         html : '<i class="fa fa-undo"></i>'
31163                     }
31164                 ]
31165             },
31166             {
31167                 tag : 'div',
31168                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31169                 action : 'rotate-right',
31170                 cn : [
31171                     {
31172                         tag : 'button',
31173                         cls : 'btn btn-default',
31174                         html : '<i class="fa fa-repeat"></i>'
31175                     }
31176                 ]
31177             }
31178         ]
31179     }
31180 });
31181
31182 /*
31183 * Licence: LGPL
31184 */
31185
31186 /**
31187  * @class Roo.bootstrap.DocumentManager
31188  * @extends Roo.bootstrap.Component
31189  * Bootstrap DocumentManager class
31190  * @cfg {String} paramName default 'imageUpload'
31191  * @cfg {String} toolTipName default 'filename'
31192  * @cfg {String} method default POST
31193  * @cfg {String} url action url
31194  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31195  * @cfg {Boolean} multiple multiple upload default true
31196  * @cfg {Number} thumbSize default 300
31197  * @cfg {String} fieldLabel
31198  * @cfg {Number} labelWidth default 4
31199  * @cfg {String} labelAlign (left|top) default left
31200  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31201 * @cfg {Number} labellg set the width of label (1-12)
31202  * @cfg {Number} labelmd set the width of label (1-12)
31203  * @cfg {Number} labelsm set the width of label (1-12)
31204  * @cfg {Number} labelxs set the width of label (1-12)
31205  * 
31206  * @constructor
31207  * Create a new DocumentManager
31208  * @param {Object} config The config object
31209  */
31210
31211 Roo.bootstrap.DocumentManager = function(config){
31212     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31213     
31214     this.files = [];
31215     this.delegates = [];
31216     
31217     this.addEvents({
31218         /**
31219          * @event initial
31220          * Fire when initial the DocumentManager
31221          * @param {Roo.bootstrap.DocumentManager} this
31222          */
31223         "initial" : true,
31224         /**
31225          * @event inspect
31226          * inspect selected file
31227          * @param {Roo.bootstrap.DocumentManager} this
31228          * @param {File} file
31229          */
31230         "inspect" : true,
31231         /**
31232          * @event exception
31233          * Fire when xhr load exception
31234          * @param {Roo.bootstrap.DocumentManager} this
31235          * @param {XMLHttpRequest} xhr
31236          */
31237         "exception" : true,
31238         /**
31239          * @event afterupload
31240          * Fire when xhr load exception
31241          * @param {Roo.bootstrap.DocumentManager} this
31242          * @param {XMLHttpRequest} xhr
31243          */
31244         "afterupload" : true,
31245         /**
31246          * @event prepare
31247          * prepare the form data
31248          * @param {Roo.bootstrap.DocumentManager} this
31249          * @param {Object} formData
31250          */
31251         "prepare" : true,
31252         /**
31253          * @event remove
31254          * Fire when remove the file
31255          * @param {Roo.bootstrap.DocumentManager} this
31256          * @param {Object} file
31257          */
31258         "remove" : true,
31259         /**
31260          * @event refresh
31261          * Fire after refresh the file
31262          * @param {Roo.bootstrap.DocumentManager} this
31263          */
31264         "refresh" : true,
31265         /**
31266          * @event click
31267          * Fire after click the image
31268          * @param {Roo.bootstrap.DocumentManager} this
31269          * @param {Object} file
31270          */
31271         "click" : true,
31272         /**
31273          * @event edit
31274          * Fire when upload a image and editable set to true
31275          * @param {Roo.bootstrap.DocumentManager} this
31276          * @param {Object} file
31277          */
31278         "edit" : true,
31279         /**
31280          * @event beforeselectfile
31281          * Fire before select file
31282          * @param {Roo.bootstrap.DocumentManager} this
31283          */
31284         "beforeselectfile" : true,
31285         /**
31286          * @event process
31287          * Fire before process file
31288          * @param {Roo.bootstrap.DocumentManager} this
31289          * @param {Object} file
31290          */
31291         "process" : true,
31292         /**
31293          * @event previewrendered
31294          * Fire when preview rendered
31295          * @param {Roo.bootstrap.DocumentManager} this
31296          * @param {Object} file
31297          */
31298         "previewrendered" : true,
31299         /**
31300          */
31301         "previewResize" : true
31302         
31303     });
31304 };
31305
31306 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31307     
31308     boxes : 0,
31309     inputName : '',
31310     thumbSize : 300,
31311     multiple : true,
31312     files : false,
31313     method : 'POST',
31314     url : '',
31315     paramName : 'imageUpload',
31316     toolTipName : 'filename',
31317     fieldLabel : '',
31318     labelWidth : 4,
31319     labelAlign : 'left',
31320     editable : true,
31321     delegates : false,
31322     xhr : false, 
31323     
31324     labellg : 0,
31325     labelmd : 0,
31326     labelsm : 0,
31327     labelxs : 0,
31328     
31329     getAutoCreate : function()
31330     {   
31331         var managerWidget = {
31332             tag : 'div',
31333             cls : 'roo-document-manager',
31334             cn : [
31335                 {
31336                     tag : 'input',
31337                     cls : 'roo-document-manager-selector',
31338                     type : 'file'
31339                 },
31340                 {
31341                     tag : 'div',
31342                     cls : 'roo-document-manager-uploader',
31343                     cn : [
31344                         {
31345                             tag : 'div',
31346                             cls : 'roo-document-manager-upload-btn',
31347                             html : '<i class="fa fa-plus"></i>'
31348                         }
31349                     ]
31350                     
31351                 }
31352             ]
31353         };
31354         
31355         var content = [
31356             {
31357                 tag : 'div',
31358                 cls : 'column col-md-12',
31359                 cn : managerWidget
31360             }
31361         ];
31362         
31363         if(this.fieldLabel.length){
31364             
31365             content = [
31366                 {
31367                     tag : 'div',
31368                     cls : 'column col-md-12',
31369                     html : this.fieldLabel
31370                 },
31371                 {
31372                     tag : 'div',
31373                     cls : 'column col-md-12',
31374                     cn : managerWidget
31375                 }
31376             ];
31377
31378             if(this.labelAlign == 'left'){
31379                 content = [
31380                     {
31381                         tag : 'div',
31382                         cls : 'column',
31383                         html : this.fieldLabel
31384                     },
31385                     {
31386                         tag : 'div',
31387                         cls : 'column',
31388                         cn : managerWidget
31389                     }
31390                 ];
31391                 
31392                 if(this.labelWidth > 12){
31393                     content[0].style = "width: " + this.labelWidth + 'px';
31394                 }
31395
31396                 if(this.labelWidth < 13 && this.labelmd == 0){
31397                     this.labelmd = this.labelWidth;
31398                 }
31399
31400                 if(this.labellg > 0){
31401                     content[0].cls += ' col-lg-' + this.labellg;
31402                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31403                 }
31404
31405                 if(this.labelmd > 0){
31406                     content[0].cls += ' col-md-' + this.labelmd;
31407                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31408                 }
31409
31410                 if(this.labelsm > 0){
31411                     content[0].cls += ' col-sm-' + this.labelsm;
31412                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31413                 }
31414
31415                 if(this.labelxs > 0){
31416                     content[0].cls += ' col-xs-' + this.labelxs;
31417                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31418                 }
31419                 
31420             }
31421         }
31422         
31423         var cfg = {
31424             tag : 'div',
31425             cls : 'row clearfix',
31426             cn : content
31427         };
31428         
31429         return cfg;
31430         
31431     },
31432     
31433     initEvents : function()
31434     {
31435         this.managerEl = this.el.select('.roo-document-manager', true).first();
31436         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31437         
31438         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31439         this.selectorEl.hide();
31440         
31441         if(this.multiple){
31442             this.selectorEl.attr('multiple', 'multiple');
31443         }
31444         
31445         this.selectorEl.on('change', this.onFileSelected, this);
31446         
31447         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31448         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31449         
31450         this.uploader.on('click', this.onUploaderClick, this);
31451         
31452         this.renderProgressDialog();
31453         
31454         var _this = this;
31455         
31456         window.addEventListener("resize", function() { _this.refresh(); } );
31457         
31458         this.fireEvent('initial', this);
31459     },
31460     
31461     renderProgressDialog : function()
31462     {
31463         var _this = this;
31464         
31465         this.progressDialog = new Roo.bootstrap.Modal({
31466             cls : 'roo-document-manager-progress-dialog',
31467             allow_close : false,
31468             animate : false,
31469             title : '',
31470             buttons : [
31471                 {
31472                     name  :'cancel',
31473                     weight : 'danger',
31474                     html : 'Cancel'
31475                 }
31476             ], 
31477             listeners : { 
31478                 btnclick : function() {
31479                     _this.uploadCancel();
31480                     this.hide();
31481                 }
31482             }
31483         });
31484          
31485         this.progressDialog.render(Roo.get(document.body));
31486          
31487         this.progress = new Roo.bootstrap.Progress({
31488             cls : 'roo-document-manager-progress',
31489             active : true,
31490             striped : true
31491         });
31492         
31493         this.progress.render(this.progressDialog.getChildContainer());
31494         
31495         this.progressBar = new Roo.bootstrap.ProgressBar({
31496             cls : 'roo-document-manager-progress-bar',
31497             aria_valuenow : 0,
31498             aria_valuemin : 0,
31499             aria_valuemax : 12,
31500             panel : 'success'
31501         });
31502         
31503         this.progressBar.render(this.progress.getChildContainer());
31504     },
31505     
31506     onUploaderClick : function(e)
31507     {
31508         e.preventDefault();
31509      
31510         if(this.fireEvent('beforeselectfile', this) != false){
31511             this.selectorEl.dom.click();
31512         }
31513         
31514     },
31515     
31516     onFileSelected : function(e)
31517     {
31518         e.preventDefault();
31519         
31520         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31521             return;
31522         }
31523         
31524         Roo.each(this.selectorEl.dom.files, function(file){
31525             if(this.fireEvent('inspect', this, file) != false){
31526                 this.files.push(file);
31527             }
31528         }, this);
31529         
31530         this.queue();
31531         
31532     },
31533     
31534     queue : function()
31535     {
31536         this.selectorEl.dom.value = '';
31537         
31538         if(!this.files || !this.files.length){
31539             return;
31540         }
31541         
31542         if(this.boxes > 0 && this.files.length > this.boxes){
31543             this.files = this.files.slice(0, this.boxes);
31544         }
31545         
31546         this.uploader.show();
31547         
31548         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31549             this.uploader.hide();
31550         }
31551         
31552         var _this = this;
31553         
31554         var files = [];
31555         
31556         var docs = [];
31557         
31558         Roo.each(this.files, function(file){
31559             
31560             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31561                 var f = this.renderPreview(file);
31562                 files.push(f);
31563                 return;
31564             }
31565             
31566             if(file.type.indexOf('image') != -1){
31567                 this.delegates.push(
31568                     (function(){
31569                         _this.process(file);
31570                     }).createDelegate(this)
31571                 );
31572         
31573                 return;
31574             }
31575             
31576             docs.push(
31577                 (function(){
31578                     _this.process(file);
31579                 }).createDelegate(this)
31580             );
31581             
31582         }, this);
31583         
31584         this.files = files;
31585         
31586         this.delegates = this.delegates.concat(docs);
31587         
31588         if(!this.delegates.length){
31589             this.refresh();
31590             return;
31591         }
31592         
31593         this.progressBar.aria_valuemax = this.delegates.length;
31594         
31595         this.arrange();
31596         
31597         return;
31598     },
31599     
31600     arrange : function()
31601     {
31602         if(!this.delegates.length){
31603             this.progressDialog.hide();
31604             this.refresh();
31605             return;
31606         }
31607         
31608         var delegate = this.delegates.shift();
31609         
31610         this.progressDialog.show();
31611         
31612         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31613         
31614         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31615         
31616         delegate();
31617     },
31618     
31619     refresh : function()
31620     {
31621         this.uploader.show();
31622         
31623         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31624             this.uploader.hide();
31625         }
31626         
31627         Roo.isTouch ? this.closable(false) : this.closable(true);
31628         
31629         this.fireEvent('refresh', this);
31630     },
31631     
31632     onRemove : function(e, el, o)
31633     {
31634         e.preventDefault();
31635         
31636         this.fireEvent('remove', this, o);
31637         
31638     },
31639     
31640     remove : function(o)
31641     {
31642         var files = [];
31643         
31644         Roo.each(this.files, function(file){
31645             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31646                 files.push(file);
31647                 return;
31648             }
31649
31650             o.target.remove();
31651
31652         }, this);
31653         
31654         this.files = files;
31655         
31656         this.refresh();
31657     },
31658     
31659     clear : function()
31660     {
31661         Roo.each(this.files, function(file){
31662             if(!file.target){
31663                 return;
31664             }
31665             
31666             file.target.remove();
31667
31668         }, this);
31669         
31670         this.files = [];
31671         
31672         this.refresh();
31673     },
31674     
31675     onClick : function(e, el, o)
31676     {
31677         e.preventDefault();
31678         
31679         this.fireEvent('click', this, o);
31680         
31681     },
31682     
31683     closable : function(closable)
31684     {
31685         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31686             
31687             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31688             
31689             if(closable){
31690                 el.show();
31691                 return;
31692             }
31693             
31694             el.hide();
31695             
31696         }, this);
31697     },
31698     
31699     xhrOnLoad : function(xhr)
31700     {
31701         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31702             el.remove();
31703         }, this);
31704         
31705         if (xhr.readyState !== 4) {
31706             this.arrange();
31707             this.fireEvent('exception', this, xhr);
31708             return;
31709         }
31710
31711         var response = Roo.decode(xhr.responseText);
31712         
31713         if(!response.success){
31714             this.arrange();
31715             this.fireEvent('exception', this, xhr);
31716             return;
31717         }
31718         
31719         var file = this.renderPreview(response.data);
31720         
31721         this.files.push(file);
31722         
31723         this.arrange();
31724         
31725         this.fireEvent('afterupload', this, xhr);
31726         
31727     },
31728     
31729     xhrOnError : function(xhr)
31730     {
31731         Roo.log('xhr on error');
31732         
31733         var response = Roo.decode(xhr.responseText);
31734           
31735         Roo.log(response);
31736         
31737         this.arrange();
31738     },
31739     
31740     process : function(file)
31741     {
31742         if(this.fireEvent('process', this, file) !== false){
31743             if(this.editable && file.type.indexOf('image') != -1){
31744                 this.fireEvent('edit', this, file);
31745                 return;
31746             }
31747
31748             this.uploadStart(file, false);
31749
31750             return;
31751         }
31752         
31753     },
31754     
31755     uploadStart : function(file, crop)
31756     {
31757         this.xhr = new XMLHttpRequest();
31758         
31759         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31760             this.arrange();
31761             return;
31762         }
31763         
31764         file.xhr = this.xhr;
31765             
31766         this.managerEl.createChild({
31767             tag : 'div',
31768             cls : 'roo-document-manager-loading',
31769             cn : [
31770                 {
31771                     tag : 'div',
31772                     tooltip : file.name,
31773                     cls : 'roo-document-manager-thumb',
31774                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31775                 }
31776             ]
31777
31778         });
31779
31780         this.xhr.open(this.method, this.url, true);
31781         
31782         var headers = {
31783             "Accept": "application/json",
31784             "Cache-Control": "no-cache",
31785             "X-Requested-With": "XMLHttpRequest"
31786         };
31787         
31788         for (var headerName in headers) {
31789             var headerValue = headers[headerName];
31790             if (headerValue) {
31791                 this.xhr.setRequestHeader(headerName, headerValue);
31792             }
31793         }
31794         
31795         var _this = this;
31796         
31797         this.xhr.onload = function()
31798         {
31799             _this.xhrOnLoad(_this.xhr);
31800         }
31801         
31802         this.xhr.onerror = function()
31803         {
31804             _this.xhrOnError(_this.xhr);
31805         }
31806         
31807         var formData = new FormData();
31808
31809         formData.append('returnHTML', 'NO');
31810         
31811         if(crop){
31812             formData.append('crop', crop);
31813         }
31814         
31815         formData.append(this.paramName, file, file.name);
31816         
31817         var options = {
31818             file : file, 
31819             manually : false
31820         };
31821         
31822         if(this.fireEvent('prepare', this, formData, options) != false){
31823             
31824             if(options.manually){
31825                 return;
31826             }
31827             
31828             this.xhr.send(formData);
31829             return;
31830         };
31831         
31832         this.uploadCancel();
31833     },
31834     
31835     uploadCancel : function()
31836     {
31837         if (this.xhr) {
31838             this.xhr.abort();
31839         }
31840         
31841         this.delegates = [];
31842         
31843         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31844             el.remove();
31845         }, this);
31846         
31847         this.arrange();
31848     },
31849     
31850     renderPreview : function(file)
31851     {
31852         if(typeof(file.target) != 'undefined' && file.target){
31853             return file;
31854         }
31855         
31856         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31857         
31858         var previewEl = this.managerEl.createChild({
31859             tag : 'div',
31860             cls : 'roo-document-manager-preview',
31861             cn : [
31862                 {
31863                     tag : 'div',
31864                     tooltip : file[this.toolTipName],
31865                     cls : 'roo-document-manager-thumb',
31866                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31867                 },
31868                 {
31869                     tag : 'button',
31870                     cls : 'close',
31871                     html : '<i class="fa fa-times-circle"></i>'
31872                 }
31873             ]
31874         });
31875
31876         var close = previewEl.select('button.close', true).first();
31877
31878         close.on('click', this.onRemove, this, file);
31879
31880         file.target = previewEl;
31881
31882         var image = previewEl.select('img', true).first();
31883         
31884         var _this = this;
31885         
31886         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31887         
31888         image.on('click', this.onClick, this, file);
31889         
31890         this.fireEvent('previewrendered', this, file);
31891         
31892         return file;
31893         
31894     },
31895     
31896     onPreviewLoad : function(file, image)
31897     {
31898         if(typeof(file.target) == 'undefined' || !file.target){
31899             return;
31900         }
31901         
31902         var width = image.dom.naturalWidth || image.dom.width;
31903         var height = image.dom.naturalHeight || image.dom.height;
31904         
31905         if(!this.previewResize) {
31906             return;
31907         }
31908         
31909         if(width > height){
31910             file.target.addClass('wide');
31911             return;
31912         }
31913         
31914         file.target.addClass('tall');
31915         return;
31916         
31917     },
31918     
31919     uploadFromSource : function(file, crop)
31920     {
31921         this.xhr = new XMLHttpRequest();
31922         
31923         this.managerEl.createChild({
31924             tag : 'div',
31925             cls : 'roo-document-manager-loading',
31926             cn : [
31927                 {
31928                     tag : 'div',
31929                     tooltip : file.name,
31930                     cls : 'roo-document-manager-thumb',
31931                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31932                 }
31933             ]
31934
31935         });
31936
31937         this.xhr.open(this.method, this.url, true);
31938         
31939         var headers = {
31940             "Accept": "application/json",
31941             "Cache-Control": "no-cache",
31942             "X-Requested-With": "XMLHttpRequest"
31943         };
31944         
31945         for (var headerName in headers) {
31946             var headerValue = headers[headerName];
31947             if (headerValue) {
31948                 this.xhr.setRequestHeader(headerName, headerValue);
31949             }
31950         }
31951         
31952         var _this = this;
31953         
31954         this.xhr.onload = function()
31955         {
31956             _this.xhrOnLoad(_this.xhr);
31957         }
31958         
31959         this.xhr.onerror = function()
31960         {
31961             _this.xhrOnError(_this.xhr);
31962         }
31963         
31964         var formData = new FormData();
31965
31966         formData.append('returnHTML', 'NO');
31967         
31968         formData.append('crop', crop);
31969         
31970         if(typeof(file.filename) != 'undefined'){
31971             formData.append('filename', file.filename);
31972         }
31973         
31974         if(typeof(file.mimetype) != 'undefined'){
31975             formData.append('mimetype', file.mimetype);
31976         }
31977         
31978         Roo.log(formData);
31979         
31980         if(this.fireEvent('prepare', this, formData) != false){
31981             this.xhr.send(formData);
31982         };
31983     }
31984 });
31985
31986 /*
31987 * Licence: LGPL
31988 */
31989
31990 /**
31991  * @class Roo.bootstrap.DocumentViewer
31992  * @extends Roo.bootstrap.Component
31993  * Bootstrap DocumentViewer class
31994  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31995  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31996  * 
31997  * @constructor
31998  * Create a new DocumentViewer
31999  * @param {Object} config The config object
32000  */
32001
32002 Roo.bootstrap.DocumentViewer = function(config){
32003     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32004     
32005     this.addEvents({
32006         /**
32007          * @event initial
32008          * Fire after initEvent
32009          * @param {Roo.bootstrap.DocumentViewer} this
32010          */
32011         "initial" : true,
32012         /**
32013          * @event click
32014          * Fire after click
32015          * @param {Roo.bootstrap.DocumentViewer} this
32016          */
32017         "click" : true,
32018         /**
32019          * @event download
32020          * Fire after download button
32021          * @param {Roo.bootstrap.DocumentViewer} this
32022          */
32023         "download" : true,
32024         /**
32025          * @event trash
32026          * Fire after trash button
32027          * @param {Roo.bootstrap.DocumentViewer} this
32028          */
32029         "trash" : true
32030         
32031     });
32032 };
32033
32034 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32035     
32036     showDownload : true,
32037     
32038     showTrash : true,
32039     
32040     getAutoCreate : function()
32041     {
32042         var cfg = {
32043             tag : 'div',
32044             cls : 'roo-document-viewer',
32045             cn : [
32046                 {
32047                     tag : 'div',
32048                     cls : 'roo-document-viewer-body',
32049                     cn : [
32050                         {
32051                             tag : 'div',
32052                             cls : 'roo-document-viewer-thumb',
32053                             cn : [
32054                                 {
32055                                     tag : 'img',
32056                                     cls : 'roo-document-viewer-image'
32057                                 }
32058                             ]
32059                         }
32060                     ]
32061                 },
32062                 {
32063                     tag : 'div',
32064                     cls : 'roo-document-viewer-footer',
32065                     cn : {
32066                         tag : 'div',
32067                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32068                         cn : [
32069                             {
32070                                 tag : 'div',
32071                                 cls : 'btn-group roo-document-viewer-download',
32072                                 cn : [
32073                                     {
32074                                         tag : 'button',
32075                                         cls : 'btn btn-default',
32076                                         html : '<i class="fa fa-download"></i>'
32077                                     }
32078                                 ]
32079                             },
32080                             {
32081                                 tag : 'div',
32082                                 cls : 'btn-group roo-document-viewer-trash',
32083                                 cn : [
32084                                     {
32085                                         tag : 'button',
32086                                         cls : 'btn btn-default',
32087                                         html : '<i class="fa fa-trash"></i>'
32088                                     }
32089                                 ]
32090                             }
32091                         ]
32092                     }
32093                 }
32094             ]
32095         };
32096         
32097         return cfg;
32098     },
32099     
32100     initEvents : function()
32101     {
32102         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32103         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32104         
32105         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32106         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32107         
32108         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32109         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32110         
32111         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32112         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32113         
32114         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32115         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32116         
32117         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32118         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32119         
32120         this.bodyEl.on('click', this.onClick, this);
32121         this.downloadBtn.on('click', this.onDownload, this);
32122         this.trashBtn.on('click', this.onTrash, this);
32123         
32124         this.downloadBtn.hide();
32125         this.trashBtn.hide();
32126         
32127         if(this.showDownload){
32128             this.downloadBtn.show();
32129         }
32130         
32131         if(this.showTrash){
32132             this.trashBtn.show();
32133         }
32134         
32135         if(!this.showDownload && !this.showTrash) {
32136             this.footerEl.hide();
32137         }
32138         
32139     },
32140     
32141     initial : function()
32142     {
32143         this.fireEvent('initial', this);
32144         
32145     },
32146     
32147     onClick : function(e)
32148     {
32149         e.preventDefault();
32150         
32151         this.fireEvent('click', this);
32152     },
32153     
32154     onDownload : function(e)
32155     {
32156         e.preventDefault();
32157         
32158         this.fireEvent('download', this);
32159     },
32160     
32161     onTrash : function(e)
32162     {
32163         e.preventDefault();
32164         
32165         this.fireEvent('trash', this);
32166     }
32167     
32168 });
32169 /*
32170  * - LGPL
32171  *
32172  * nav progress bar
32173  * 
32174  */
32175
32176 /**
32177  * @class Roo.bootstrap.NavProgressBar
32178  * @extends Roo.bootstrap.Component
32179  * Bootstrap NavProgressBar class
32180  * 
32181  * @constructor
32182  * Create a new nav progress bar
32183  * @param {Object} config The config object
32184  */
32185
32186 Roo.bootstrap.NavProgressBar = function(config){
32187     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32188
32189     this.bullets = this.bullets || [];
32190    
32191 //    Roo.bootstrap.NavProgressBar.register(this);
32192      this.addEvents({
32193         /**
32194              * @event changed
32195              * Fires when the active item changes
32196              * @param {Roo.bootstrap.NavProgressBar} this
32197              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32198              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32199          */
32200         'changed': true
32201      });
32202     
32203 };
32204
32205 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32206     
32207     bullets : [],
32208     barItems : [],
32209     
32210     getAutoCreate : function()
32211     {
32212         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32213         
32214         cfg = {
32215             tag : 'div',
32216             cls : 'roo-navigation-bar-group',
32217             cn : [
32218                 {
32219                     tag : 'div',
32220                     cls : 'roo-navigation-top-bar'
32221                 },
32222                 {
32223                     tag : 'div',
32224                     cls : 'roo-navigation-bullets-bar',
32225                     cn : [
32226                         {
32227                             tag : 'ul',
32228                             cls : 'roo-navigation-bar'
32229                         }
32230                     ]
32231                 },
32232                 
32233                 {
32234                     tag : 'div',
32235                     cls : 'roo-navigation-bottom-bar'
32236                 }
32237             ]
32238             
32239         };
32240         
32241         return cfg;
32242         
32243     },
32244     
32245     initEvents: function() 
32246     {
32247         
32248     },
32249     
32250     onRender : function(ct, position) 
32251     {
32252         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32253         
32254         if(this.bullets.length){
32255             Roo.each(this.bullets, function(b){
32256                this.addItem(b);
32257             }, this);
32258         }
32259         
32260         this.format();
32261         
32262     },
32263     
32264     addItem : function(cfg)
32265     {
32266         var item = new Roo.bootstrap.NavProgressItem(cfg);
32267         
32268         item.parentId = this.id;
32269         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32270         
32271         if(cfg.html){
32272             var top = new Roo.bootstrap.Element({
32273                 tag : 'div',
32274                 cls : 'roo-navigation-bar-text'
32275             });
32276             
32277             var bottom = new Roo.bootstrap.Element({
32278                 tag : 'div',
32279                 cls : 'roo-navigation-bar-text'
32280             });
32281             
32282             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32283             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32284             
32285             var topText = new Roo.bootstrap.Element({
32286                 tag : 'span',
32287                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32288             });
32289             
32290             var bottomText = new Roo.bootstrap.Element({
32291                 tag : 'span',
32292                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32293             });
32294             
32295             topText.onRender(top.el, null);
32296             bottomText.onRender(bottom.el, null);
32297             
32298             item.topEl = top;
32299             item.bottomEl = bottom;
32300         }
32301         
32302         this.barItems.push(item);
32303         
32304         return item;
32305     },
32306     
32307     getActive : function()
32308     {
32309         var active = false;
32310         
32311         Roo.each(this.barItems, function(v){
32312             
32313             if (!v.isActive()) {
32314                 return;
32315             }
32316             
32317             active = v;
32318             return false;
32319             
32320         });
32321         
32322         return active;
32323     },
32324     
32325     setActiveItem : function(item)
32326     {
32327         var prev = false;
32328         
32329         Roo.each(this.barItems, function(v){
32330             if (v.rid == item.rid) {
32331                 return ;
32332             }
32333             
32334             if (v.isActive()) {
32335                 v.setActive(false);
32336                 prev = v;
32337             }
32338         });
32339
32340         item.setActive(true);
32341         
32342         this.fireEvent('changed', this, item, prev);
32343     },
32344     
32345     getBarItem: function(rid)
32346     {
32347         var ret = false;
32348         
32349         Roo.each(this.barItems, function(e) {
32350             if (e.rid != rid) {
32351                 return;
32352             }
32353             
32354             ret =  e;
32355             return false;
32356         });
32357         
32358         return ret;
32359     },
32360     
32361     indexOfItem : function(item)
32362     {
32363         var index = false;
32364         
32365         Roo.each(this.barItems, function(v, i){
32366             
32367             if (v.rid != item.rid) {
32368                 return;
32369             }
32370             
32371             index = i;
32372             return false
32373         });
32374         
32375         return index;
32376     },
32377     
32378     setActiveNext : function()
32379     {
32380         var i = this.indexOfItem(this.getActive());
32381         
32382         if (i > this.barItems.length) {
32383             return;
32384         }
32385         
32386         this.setActiveItem(this.barItems[i+1]);
32387     },
32388     
32389     setActivePrev : function()
32390     {
32391         var i = this.indexOfItem(this.getActive());
32392         
32393         if (i  < 1) {
32394             return;
32395         }
32396         
32397         this.setActiveItem(this.barItems[i-1]);
32398     },
32399     
32400     format : function()
32401     {
32402         if(!this.barItems.length){
32403             return;
32404         }
32405      
32406         var width = 100 / this.barItems.length;
32407         
32408         Roo.each(this.barItems, function(i){
32409             i.el.setStyle('width', width + '%');
32410             i.topEl.el.setStyle('width', width + '%');
32411             i.bottomEl.el.setStyle('width', width + '%');
32412         }, this);
32413         
32414     }
32415     
32416 });
32417 /*
32418  * - LGPL
32419  *
32420  * Nav Progress Item
32421  * 
32422  */
32423
32424 /**
32425  * @class Roo.bootstrap.NavProgressItem
32426  * @extends Roo.bootstrap.Component
32427  * Bootstrap NavProgressItem class
32428  * @cfg {String} rid the reference id
32429  * @cfg {Boolean} active (true|false) Is item active default false
32430  * @cfg {Boolean} disabled (true|false) Is item active default false
32431  * @cfg {String} html
32432  * @cfg {String} position (top|bottom) text position default bottom
32433  * @cfg {String} icon show icon instead of number
32434  * 
32435  * @constructor
32436  * Create a new NavProgressItem
32437  * @param {Object} config The config object
32438  */
32439 Roo.bootstrap.NavProgressItem = function(config){
32440     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32441     this.addEvents({
32442         // raw events
32443         /**
32444          * @event click
32445          * The raw click event for the entire grid.
32446          * @param {Roo.bootstrap.NavProgressItem} this
32447          * @param {Roo.EventObject} e
32448          */
32449         "click" : true
32450     });
32451    
32452 };
32453
32454 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32455     
32456     rid : '',
32457     active : false,
32458     disabled : false,
32459     html : '',
32460     position : 'bottom',
32461     icon : false,
32462     
32463     getAutoCreate : function()
32464     {
32465         var iconCls = 'roo-navigation-bar-item-icon';
32466         
32467         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32468         
32469         var cfg = {
32470             tag: 'li',
32471             cls: 'roo-navigation-bar-item',
32472             cn : [
32473                 {
32474                     tag : 'i',
32475                     cls : iconCls
32476                 }
32477             ]
32478         };
32479         
32480         if(this.active){
32481             cfg.cls += ' active';
32482         }
32483         if(this.disabled){
32484             cfg.cls += ' disabled';
32485         }
32486         
32487         return cfg;
32488     },
32489     
32490     disable : function()
32491     {
32492         this.setDisabled(true);
32493     },
32494     
32495     enable : function()
32496     {
32497         this.setDisabled(false);
32498     },
32499     
32500     initEvents: function() 
32501     {
32502         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32503         
32504         this.iconEl.on('click', this.onClick, this);
32505     },
32506     
32507     onClick : function(e)
32508     {
32509         e.preventDefault();
32510         
32511         if(this.disabled){
32512             return;
32513         }
32514         
32515         if(this.fireEvent('click', this, e) === false){
32516             return;
32517         };
32518         
32519         this.parent().setActiveItem(this);
32520     },
32521     
32522     isActive: function () 
32523     {
32524         return this.active;
32525     },
32526     
32527     setActive : function(state)
32528     {
32529         if(this.active == state){
32530             return;
32531         }
32532         
32533         this.active = state;
32534         
32535         if (state) {
32536             this.el.addClass('active');
32537             return;
32538         }
32539         
32540         this.el.removeClass('active');
32541         
32542         return;
32543     },
32544     
32545     setDisabled : function(state)
32546     {
32547         if(this.disabled == state){
32548             return;
32549         }
32550         
32551         this.disabled = state;
32552         
32553         if (state) {
32554             this.el.addClass('disabled');
32555             return;
32556         }
32557         
32558         this.el.removeClass('disabled');
32559     },
32560     
32561     tooltipEl : function()
32562     {
32563         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32564     }
32565 });
32566  
32567
32568  /*
32569  * - LGPL
32570  *
32571  * FieldLabel
32572  * 
32573  */
32574
32575 /**
32576  * @class Roo.bootstrap.FieldLabel
32577  * @extends Roo.bootstrap.Component
32578  * Bootstrap FieldLabel class
32579  * @cfg {String} html contents of the element
32580  * @cfg {String} tag tag of the element default label
32581  * @cfg {String} cls class of the element
32582  * @cfg {String} target label target 
32583  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32584  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32585  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32586  * @cfg {String} iconTooltip default "This field is required"
32587  * @cfg {String} indicatorpos (left|right) default left
32588  * 
32589  * @constructor
32590  * Create a new FieldLabel
32591  * @param {Object} config The config object
32592  */
32593
32594 Roo.bootstrap.FieldLabel = function(config){
32595     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32596     
32597     this.addEvents({
32598             /**
32599              * @event invalid
32600              * Fires after the field has been marked as invalid.
32601              * @param {Roo.form.FieldLabel} this
32602              * @param {String} msg The validation message
32603              */
32604             invalid : true,
32605             /**
32606              * @event valid
32607              * Fires after the field has been validated with no errors.
32608              * @param {Roo.form.FieldLabel} this
32609              */
32610             valid : true
32611         });
32612 };
32613
32614 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32615     
32616     tag: 'label',
32617     cls: '',
32618     html: '',
32619     target: '',
32620     allowBlank : true,
32621     invalidClass : 'has-warning',
32622     validClass : 'has-success',
32623     iconTooltip : 'This field is required',
32624     indicatorpos : 'left',
32625     
32626     getAutoCreate : function(){
32627         
32628         var cls = "";
32629         if (!this.allowBlank) {
32630             cls  = "visible";
32631         }
32632         
32633         var cfg = {
32634             tag : this.tag,
32635             cls : 'roo-bootstrap-field-label ' + this.cls,
32636             for : this.target,
32637             cn : [
32638                 {
32639                     tag : 'i',
32640                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32641                     tooltip : this.iconTooltip
32642                 },
32643                 {
32644                     tag : 'span',
32645                     html : this.html
32646                 }
32647             ] 
32648         };
32649         
32650         if(this.indicatorpos == 'right'){
32651             var cfg = {
32652                 tag : this.tag,
32653                 cls : 'roo-bootstrap-field-label ' + this.cls,
32654                 for : this.target,
32655                 cn : [
32656                     {
32657                         tag : 'span',
32658                         html : this.html
32659                     },
32660                     {
32661                         tag : 'i',
32662                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32663                         tooltip : this.iconTooltip
32664                     }
32665                 ] 
32666             };
32667         }
32668         
32669         return cfg;
32670     },
32671     
32672     initEvents: function() 
32673     {
32674         Roo.bootstrap.Element.superclass.initEvents.call(this);
32675         
32676         this.indicator = this.indicatorEl();
32677         
32678         if(this.indicator){
32679             this.indicator.removeClass('visible');
32680             this.indicator.addClass('invisible');
32681         }
32682         
32683         Roo.bootstrap.FieldLabel.register(this);
32684     },
32685     
32686     indicatorEl : function()
32687     {
32688         var indicator = this.el.select('i.roo-required-indicator',true).first();
32689         
32690         if(!indicator){
32691             return false;
32692         }
32693         
32694         return indicator;
32695         
32696     },
32697     
32698     /**
32699      * Mark this field as valid
32700      */
32701     markValid : function()
32702     {
32703         if(this.indicator){
32704             this.indicator.removeClass('visible');
32705             this.indicator.addClass('invisible');
32706         }
32707         if (Roo.bootstrap.version == 3) {
32708             this.el.removeClass(this.invalidClass);
32709             this.el.addClass(this.validClass);
32710         } else {
32711             this.el.removeClass('is-invalid');
32712             this.el.addClass('is-valid');
32713         }
32714         
32715         
32716         this.fireEvent('valid', this);
32717     },
32718     
32719     /**
32720      * Mark this field as invalid
32721      * @param {String} msg The validation message
32722      */
32723     markInvalid : function(msg)
32724     {
32725         if(this.indicator){
32726             this.indicator.removeClass('invisible');
32727             this.indicator.addClass('visible');
32728         }
32729           if (Roo.bootstrap.version == 3) {
32730             this.el.removeClass(this.validClass);
32731             this.el.addClass(this.invalidClass);
32732         } else {
32733             this.el.removeClass('is-valid');
32734             this.el.addClass('is-invalid');
32735         }
32736         
32737         
32738         this.fireEvent('invalid', this, msg);
32739     }
32740     
32741    
32742 });
32743
32744 Roo.apply(Roo.bootstrap.FieldLabel, {
32745     
32746     groups: {},
32747     
32748      /**
32749     * register a FieldLabel Group
32750     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32751     */
32752     register : function(label)
32753     {
32754         if(this.groups.hasOwnProperty(label.target)){
32755             return;
32756         }
32757      
32758         this.groups[label.target] = label;
32759         
32760     },
32761     /**
32762     * fetch a FieldLabel Group based on the target
32763     * @param {string} target
32764     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32765     */
32766     get: function(target) {
32767         if (typeof(this.groups[target]) == 'undefined') {
32768             return false;
32769         }
32770         
32771         return this.groups[target] ;
32772     }
32773 });
32774
32775  
32776
32777  /*
32778  * - LGPL
32779  *
32780  * page DateSplitField.
32781  * 
32782  */
32783
32784
32785 /**
32786  * @class Roo.bootstrap.DateSplitField
32787  * @extends Roo.bootstrap.Component
32788  * Bootstrap DateSplitField class
32789  * @cfg {string} fieldLabel - the label associated
32790  * @cfg {Number} labelWidth set the width of label (0-12)
32791  * @cfg {String} labelAlign (top|left)
32792  * @cfg {Boolean} dayAllowBlank (true|false) default false
32793  * @cfg {Boolean} monthAllowBlank (true|false) default false
32794  * @cfg {Boolean} yearAllowBlank (true|false) default false
32795  * @cfg {string} dayPlaceholder 
32796  * @cfg {string} monthPlaceholder
32797  * @cfg {string} yearPlaceholder
32798  * @cfg {string} dayFormat default 'd'
32799  * @cfg {string} monthFormat default 'm'
32800  * @cfg {string} yearFormat default 'Y'
32801  * @cfg {Number} labellg set the width of label (1-12)
32802  * @cfg {Number} labelmd set the width of label (1-12)
32803  * @cfg {Number} labelsm set the width of label (1-12)
32804  * @cfg {Number} labelxs set the width of label (1-12)
32805
32806  *     
32807  * @constructor
32808  * Create a new DateSplitField
32809  * @param {Object} config The config object
32810  */
32811
32812 Roo.bootstrap.DateSplitField = function(config){
32813     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32814     
32815     this.addEvents({
32816         // raw events
32817          /**
32818          * @event years
32819          * getting the data of years
32820          * @param {Roo.bootstrap.DateSplitField} this
32821          * @param {Object} years
32822          */
32823         "years" : true,
32824         /**
32825          * @event days
32826          * getting the data of days
32827          * @param {Roo.bootstrap.DateSplitField} this
32828          * @param {Object} days
32829          */
32830         "days" : true,
32831         /**
32832          * @event invalid
32833          * Fires after the field has been marked as invalid.
32834          * @param {Roo.form.Field} this
32835          * @param {String} msg The validation message
32836          */
32837         invalid : true,
32838        /**
32839          * @event valid
32840          * Fires after the field has been validated with no errors.
32841          * @param {Roo.form.Field} this
32842          */
32843         valid : true
32844     });
32845 };
32846
32847 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32848     
32849     fieldLabel : '',
32850     labelAlign : 'top',
32851     labelWidth : 3,
32852     dayAllowBlank : false,
32853     monthAllowBlank : false,
32854     yearAllowBlank : false,
32855     dayPlaceholder : '',
32856     monthPlaceholder : '',
32857     yearPlaceholder : '',
32858     dayFormat : 'd',
32859     monthFormat : 'm',
32860     yearFormat : 'Y',
32861     isFormField : true,
32862     labellg : 0,
32863     labelmd : 0,
32864     labelsm : 0,
32865     labelxs : 0,
32866     
32867     getAutoCreate : function()
32868     {
32869         var cfg = {
32870             tag : 'div',
32871             cls : 'row roo-date-split-field-group',
32872             cn : [
32873                 {
32874                     tag : 'input',
32875                     type : 'hidden',
32876                     cls : 'form-hidden-field roo-date-split-field-group-value',
32877                     name : this.name
32878                 }
32879             ]
32880         };
32881         
32882         var labelCls = 'col-md-12';
32883         var contentCls = 'col-md-4';
32884         
32885         if(this.fieldLabel){
32886             
32887             var label = {
32888                 tag : 'div',
32889                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32890                 cn : [
32891                     {
32892                         tag : 'label',
32893                         html : this.fieldLabel
32894                     }
32895                 ]
32896             };
32897             
32898             if(this.labelAlign == 'left'){
32899             
32900                 if(this.labelWidth > 12){
32901                     label.style = "width: " + this.labelWidth + 'px';
32902                 }
32903
32904                 if(this.labelWidth < 13 && this.labelmd == 0){
32905                     this.labelmd = this.labelWidth;
32906                 }
32907
32908                 if(this.labellg > 0){
32909                     labelCls = ' col-lg-' + this.labellg;
32910                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32911                 }
32912
32913                 if(this.labelmd > 0){
32914                     labelCls = ' col-md-' + this.labelmd;
32915                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32916                 }
32917
32918                 if(this.labelsm > 0){
32919                     labelCls = ' col-sm-' + this.labelsm;
32920                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32921                 }
32922
32923                 if(this.labelxs > 0){
32924                     labelCls = ' col-xs-' + this.labelxs;
32925                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32926                 }
32927             }
32928             
32929             label.cls += ' ' + labelCls;
32930             
32931             cfg.cn.push(label);
32932         }
32933         
32934         Roo.each(['day', 'month', 'year'], function(t){
32935             cfg.cn.push({
32936                 tag : 'div',
32937                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32938             });
32939         }, this);
32940         
32941         return cfg;
32942     },
32943     
32944     inputEl: function ()
32945     {
32946         return this.el.select('.roo-date-split-field-group-value', true).first();
32947     },
32948     
32949     onRender : function(ct, position) 
32950     {
32951         var _this = this;
32952         
32953         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32954         
32955         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32956         
32957         this.dayField = new Roo.bootstrap.ComboBox({
32958             allowBlank : this.dayAllowBlank,
32959             alwaysQuery : true,
32960             displayField : 'value',
32961             editable : false,
32962             fieldLabel : '',
32963             forceSelection : true,
32964             mode : 'local',
32965             placeholder : this.dayPlaceholder,
32966             selectOnFocus : true,
32967             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32968             triggerAction : 'all',
32969             typeAhead : true,
32970             valueField : 'value',
32971             store : new Roo.data.SimpleStore({
32972                 data : (function() {    
32973                     var days = [];
32974                     _this.fireEvent('days', _this, days);
32975                     return days;
32976                 })(),
32977                 fields : [ 'value' ]
32978             }),
32979             listeners : {
32980                 select : function (_self, record, index)
32981                 {
32982                     _this.setValue(_this.getValue());
32983                 }
32984             }
32985         });
32986
32987         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32988         
32989         this.monthField = new Roo.bootstrap.MonthField({
32990             after : '<i class=\"fa fa-calendar\"></i>',
32991             allowBlank : this.monthAllowBlank,
32992             placeholder : this.monthPlaceholder,
32993             readOnly : true,
32994             listeners : {
32995                 render : function (_self)
32996                 {
32997                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32998                         e.preventDefault();
32999                         _self.focus();
33000                     });
33001                 },
33002                 select : function (_self, oldvalue, newvalue)
33003                 {
33004                     _this.setValue(_this.getValue());
33005                 }
33006             }
33007         });
33008         
33009         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33010         
33011         this.yearField = new Roo.bootstrap.ComboBox({
33012             allowBlank : this.yearAllowBlank,
33013             alwaysQuery : true,
33014             displayField : 'value',
33015             editable : false,
33016             fieldLabel : '',
33017             forceSelection : true,
33018             mode : 'local',
33019             placeholder : this.yearPlaceholder,
33020             selectOnFocus : true,
33021             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33022             triggerAction : 'all',
33023             typeAhead : true,
33024             valueField : 'value',
33025             store : new Roo.data.SimpleStore({
33026                 data : (function() {
33027                     var years = [];
33028                     _this.fireEvent('years', _this, years);
33029                     return years;
33030                 })(),
33031                 fields : [ 'value' ]
33032             }),
33033             listeners : {
33034                 select : function (_self, record, index)
33035                 {
33036                     _this.setValue(_this.getValue());
33037                 }
33038             }
33039         });
33040
33041         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33042     },
33043     
33044     setValue : function(v, format)
33045     {
33046         this.inputEl.dom.value = v;
33047         
33048         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33049         
33050         var d = Date.parseDate(v, f);
33051         
33052         if(!d){
33053             this.validate();
33054             return;
33055         }
33056         
33057         this.setDay(d.format(this.dayFormat));
33058         this.setMonth(d.format(this.monthFormat));
33059         this.setYear(d.format(this.yearFormat));
33060         
33061         this.validate();
33062         
33063         return;
33064     },
33065     
33066     setDay : function(v)
33067     {
33068         this.dayField.setValue(v);
33069         this.inputEl.dom.value = this.getValue();
33070         this.validate();
33071         return;
33072     },
33073     
33074     setMonth : function(v)
33075     {
33076         this.monthField.setValue(v, true);
33077         this.inputEl.dom.value = this.getValue();
33078         this.validate();
33079         return;
33080     },
33081     
33082     setYear : function(v)
33083     {
33084         this.yearField.setValue(v);
33085         this.inputEl.dom.value = this.getValue();
33086         this.validate();
33087         return;
33088     },
33089     
33090     getDay : function()
33091     {
33092         return this.dayField.getValue();
33093     },
33094     
33095     getMonth : function()
33096     {
33097         return this.monthField.getValue();
33098     },
33099     
33100     getYear : function()
33101     {
33102         return this.yearField.getValue();
33103     },
33104     
33105     getValue : function()
33106     {
33107         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33108         
33109         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33110         
33111         return date;
33112     },
33113     
33114     reset : function()
33115     {
33116         this.setDay('');
33117         this.setMonth('');
33118         this.setYear('');
33119         this.inputEl.dom.value = '';
33120         this.validate();
33121         return;
33122     },
33123     
33124     validate : function()
33125     {
33126         var d = this.dayField.validate();
33127         var m = this.monthField.validate();
33128         var y = this.yearField.validate();
33129         
33130         var valid = true;
33131         
33132         if(
33133                 (!this.dayAllowBlank && !d) ||
33134                 (!this.monthAllowBlank && !m) ||
33135                 (!this.yearAllowBlank && !y)
33136         ){
33137             valid = false;
33138         }
33139         
33140         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33141             return valid;
33142         }
33143         
33144         if(valid){
33145             this.markValid();
33146             return valid;
33147         }
33148         
33149         this.markInvalid();
33150         
33151         return valid;
33152     },
33153     
33154     markValid : function()
33155     {
33156         
33157         var label = this.el.select('label', true).first();
33158         var icon = this.el.select('i.fa-star', true).first();
33159
33160         if(label && icon){
33161             icon.remove();
33162         }
33163         
33164         this.fireEvent('valid', this);
33165     },
33166     
33167      /**
33168      * Mark this field as invalid
33169      * @param {String} msg The validation message
33170      */
33171     markInvalid : function(msg)
33172     {
33173         
33174         var label = this.el.select('label', true).first();
33175         var icon = this.el.select('i.fa-star', true).first();
33176
33177         if(label && !icon){
33178             this.el.select('.roo-date-split-field-label', true).createChild({
33179                 tag : 'i',
33180                 cls : 'text-danger fa fa-lg fa-star',
33181                 tooltip : 'This field is required',
33182                 style : 'margin-right:5px;'
33183             }, label, true);
33184         }
33185         
33186         this.fireEvent('invalid', this, msg);
33187     },
33188     
33189     clearInvalid : function()
33190     {
33191         var label = this.el.select('label', true).first();
33192         var icon = this.el.select('i.fa-star', true).first();
33193
33194         if(label && icon){
33195             icon.remove();
33196         }
33197         
33198         this.fireEvent('valid', this);
33199     },
33200     
33201     getName: function()
33202     {
33203         return this.name;
33204     }
33205     
33206 });
33207
33208  /**
33209  *
33210  * This is based on 
33211  * http://masonry.desandro.com
33212  *
33213  * The idea is to render all the bricks based on vertical width...
33214  *
33215  * The original code extends 'outlayer' - we might need to use that....
33216  * 
33217  */
33218
33219
33220 /**
33221  * @class Roo.bootstrap.LayoutMasonry
33222  * @extends Roo.bootstrap.Component
33223  * Bootstrap Layout Masonry class
33224  * 
33225  * @constructor
33226  * Create a new Element
33227  * @param {Object} config The config object
33228  */
33229
33230 Roo.bootstrap.LayoutMasonry = function(config){
33231     
33232     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33233     
33234     this.bricks = [];
33235     
33236     Roo.bootstrap.LayoutMasonry.register(this);
33237     
33238     this.addEvents({
33239         // raw events
33240         /**
33241          * @event layout
33242          * Fire after layout the items
33243          * @param {Roo.bootstrap.LayoutMasonry} this
33244          * @param {Roo.EventObject} e
33245          */
33246         "layout" : true
33247     });
33248     
33249 };
33250
33251 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33252     
33253     /**
33254      * @cfg {Boolean} isLayoutInstant = no animation?
33255      */   
33256     isLayoutInstant : false, // needed?
33257    
33258     /**
33259      * @cfg {Number} boxWidth  width of the columns
33260      */   
33261     boxWidth : 450,
33262     
33263       /**
33264      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33265      */   
33266     boxHeight : 0,
33267     
33268     /**
33269      * @cfg {Number} padWidth padding below box..
33270      */   
33271     padWidth : 10, 
33272     
33273     /**
33274      * @cfg {Number} gutter gutter width..
33275      */   
33276     gutter : 10,
33277     
33278      /**
33279      * @cfg {Number} maxCols maximum number of columns
33280      */   
33281     
33282     maxCols: 0,
33283     
33284     /**
33285      * @cfg {Boolean} isAutoInitial defalut true
33286      */   
33287     isAutoInitial : true, 
33288     
33289     containerWidth: 0,
33290     
33291     /**
33292      * @cfg {Boolean} isHorizontal defalut false
33293      */   
33294     isHorizontal : false, 
33295
33296     currentSize : null,
33297     
33298     tag: 'div',
33299     
33300     cls: '',
33301     
33302     bricks: null, //CompositeElement
33303     
33304     cols : 1,
33305     
33306     _isLayoutInited : false,
33307     
33308 //    isAlternative : false, // only use for vertical layout...
33309     
33310     /**
33311      * @cfg {Number} alternativePadWidth padding below box..
33312      */   
33313     alternativePadWidth : 50,
33314     
33315     selectedBrick : [],
33316     
33317     getAutoCreate : function(){
33318         
33319         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33320         
33321         var cfg = {
33322             tag: this.tag,
33323             cls: 'blog-masonary-wrapper ' + this.cls,
33324             cn : {
33325                 cls : 'mas-boxes masonary'
33326             }
33327         };
33328         
33329         return cfg;
33330     },
33331     
33332     getChildContainer: function( )
33333     {
33334         if (this.boxesEl) {
33335             return this.boxesEl;
33336         }
33337         
33338         this.boxesEl = this.el.select('.mas-boxes').first();
33339         
33340         return this.boxesEl;
33341     },
33342     
33343     
33344     initEvents : function()
33345     {
33346         var _this = this;
33347         
33348         if(this.isAutoInitial){
33349             Roo.log('hook children rendered');
33350             this.on('childrenrendered', function() {
33351                 Roo.log('children rendered');
33352                 _this.initial();
33353             } ,this);
33354         }
33355     },
33356     
33357     initial : function()
33358     {
33359         this.selectedBrick = [];
33360         
33361         this.currentSize = this.el.getBox(true);
33362         
33363         Roo.EventManager.onWindowResize(this.resize, this); 
33364
33365         if(!this.isAutoInitial){
33366             this.layout();
33367             return;
33368         }
33369         
33370         this.layout();
33371         
33372         return;
33373         //this.layout.defer(500,this);
33374         
33375     },
33376     
33377     resize : function()
33378     {
33379         var cs = this.el.getBox(true);
33380         
33381         if (
33382                 this.currentSize.width == cs.width && 
33383                 this.currentSize.x == cs.x && 
33384                 this.currentSize.height == cs.height && 
33385                 this.currentSize.y == cs.y 
33386         ) {
33387             Roo.log("no change in with or X or Y");
33388             return;
33389         }
33390         
33391         this.currentSize = cs;
33392         
33393         this.layout();
33394         
33395     },
33396     
33397     layout : function()
33398     {   
33399         this._resetLayout();
33400         
33401         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33402         
33403         this.layoutItems( isInstant );
33404       
33405         this._isLayoutInited = true;
33406         
33407         this.fireEvent('layout', this);
33408         
33409     },
33410     
33411     _resetLayout : function()
33412     {
33413         if(this.isHorizontal){
33414             this.horizontalMeasureColumns();
33415             return;
33416         }
33417         
33418         this.verticalMeasureColumns();
33419         
33420     },
33421     
33422     verticalMeasureColumns : function()
33423     {
33424         this.getContainerWidth();
33425         
33426 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33427 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33428 //            return;
33429 //        }
33430         
33431         var boxWidth = this.boxWidth + this.padWidth;
33432         
33433         if(this.containerWidth < this.boxWidth){
33434             boxWidth = this.containerWidth
33435         }
33436         
33437         var containerWidth = this.containerWidth;
33438         
33439         var cols = Math.floor(containerWidth / boxWidth);
33440         
33441         this.cols = Math.max( cols, 1 );
33442         
33443         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33444         
33445         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33446         
33447         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33448         
33449         this.colWidth = boxWidth + avail - this.padWidth;
33450         
33451         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33452         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33453     },
33454     
33455     horizontalMeasureColumns : function()
33456     {
33457         this.getContainerWidth();
33458         
33459         var boxWidth = this.boxWidth;
33460         
33461         if(this.containerWidth < boxWidth){
33462             boxWidth = this.containerWidth;
33463         }
33464         
33465         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33466         
33467         this.el.setHeight(boxWidth);
33468         
33469     },
33470     
33471     getContainerWidth : function()
33472     {
33473         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33474     },
33475     
33476     layoutItems : function( isInstant )
33477     {
33478         Roo.log(this.bricks);
33479         
33480         var items = Roo.apply([], this.bricks);
33481         
33482         if(this.isHorizontal){
33483             this._horizontalLayoutItems( items , isInstant );
33484             return;
33485         }
33486         
33487 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33488 //            this._verticalAlternativeLayoutItems( items , isInstant );
33489 //            return;
33490 //        }
33491         
33492         this._verticalLayoutItems( items , isInstant );
33493         
33494     },
33495     
33496     _verticalLayoutItems : function ( items , isInstant)
33497     {
33498         if ( !items || !items.length ) {
33499             return;
33500         }
33501         
33502         var standard = [
33503             ['xs', 'xs', 'xs', 'tall'],
33504             ['xs', 'xs', 'tall'],
33505             ['xs', 'xs', 'sm'],
33506             ['xs', 'xs', 'xs'],
33507             ['xs', 'tall'],
33508             ['xs', 'sm'],
33509             ['xs', 'xs'],
33510             ['xs'],
33511             
33512             ['sm', 'xs', 'xs'],
33513             ['sm', 'xs'],
33514             ['sm'],
33515             
33516             ['tall', 'xs', 'xs', 'xs'],
33517             ['tall', 'xs', 'xs'],
33518             ['tall', 'xs'],
33519             ['tall']
33520             
33521         ];
33522         
33523         var queue = [];
33524         
33525         var boxes = [];
33526         
33527         var box = [];
33528         
33529         Roo.each(items, function(item, k){
33530             
33531             switch (item.size) {
33532                 // these layouts take up a full box,
33533                 case 'md' :
33534                 case 'md-left' :
33535                 case 'md-right' :
33536                 case 'wide' :
33537                     
33538                     if(box.length){
33539                         boxes.push(box);
33540                         box = [];
33541                     }
33542                     
33543                     boxes.push([item]);
33544                     
33545                     break;
33546                     
33547                 case 'xs' :
33548                 case 'sm' :
33549                 case 'tall' :
33550                     
33551                     box.push(item);
33552                     
33553                     break;
33554                 default :
33555                     break;
33556                     
33557             }
33558             
33559         }, this);
33560         
33561         if(box.length){
33562             boxes.push(box);
33563             box = [];
33564         }
33565         
33566         var filterPattern = function(box, length)
33567         {
33568             if(!box.length){
33569                 return;
33570             }
33571             
33572             var match = false;
33573             
33574             var pattern = box.slice(0, length);
33575             
33576             var format = [];
33577             
33578             Roo.each(pattern, function(i){
33579                 format.push(i.size);
33580             }, this);
33581             
33582             Roo.each(standard, function(s){
33583                 
33584                 if(String(s) != String(format)){
33585                     return;
33586                 }
33587                 
33588                 match = true;
33589                 return false;
33590                 
33591             }, this);
33592             
33593             if(!match && length == 1){
33594                 return;
33595             }
33596             
33597             if(!match){
33598                 filterPattern(box, length - 1);
33599                 return;
33600             }
33601                 
33602             queue.push(pattern);
33603
33604             box = box.slice(length, box.length);
33605
33606             filterPattern(box, 4);
33607
33608             return;
33609             
33610         }
33611         
33612         Roo.each(boxes, function(box, k){
33613             
33614             if(!box.length){
33615                 return;
33616             }
33617             
33618             if(box.length == 1){
33619                 queue.push(box);
33620                 return;
33621             }
33622             
33623             filterPattern(box, 4);
33624             
33625         }, this);
33626         
33627         this._processVerticalLayoutQueue( queue, isInstant );
33628         
33629     },
33630     
33631 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33632 //    {
33633 //        if ( !items || !items.length ) {
33634 //            return;
33635 //        }
33636 //
33637 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33638 //        
33639 //    },
33640     
33641     _horizontalLayoutItems : function ( items , isInstant)
33642     {
33643         if ( !items || !items.length || items.length < 3) {
33644             return;
33645         }
33646         
33647         items.reverse();
33648         
33649         var eItems = items.slice(0, 3);
33650         
33651         items = items.slice(3, items.length);
33652         
33653         var standard = [
33654             ['xs', 'xs', 'xs', 'wide'],
33655             ['xs', 'xs', 'wide'],
33656             ['xs', 'xs', 'sm'],
33657             ['xs', 'xs', 'xs'],
33658             ['xs', 'wide'],
33659             ['xs', 'sm'],
33660             ['xs', 'xs'],
33661             ['xs'],
33662             
33663             ['sm', 'xs', 'xs'],
33664             ['sm', 'xs'],
33665             ['sm'],
33666             
33667             ['wide', 'xs', 'xs', 'xs'],
33668             ['wide', 'xs', 'xs'],
33669             ['wide', 'xs'],
33670             ['wide'],
33671             
33672             ['wide-thin']
33673         ];
33674         
33675         var queue = [];
33676         
33677         var boxes = [];
33678         
33679         var box = [];
33680         
33681         Roo.each(items, function(item, k){
33682             
33683             switch (item.size) {
33684                 case 'md' :
33685                 case 'md-left' :
33686                 case 'md-right' :
33687                 case 'tall' :
33688                     
33689                     if(box.length){
33690                         boxes.push(box);
33691                         box = [];
33692                     }
33693                     
33694                     boxes.push([item]);
33695                     
33696                     break;
33697                     
33698                 case 'xs' :
33699                 case 'sm' :
33700                 case 'wide' :
33701                 case 'wide-thin' :
33702                     
33703                     box.push(item);
33704                     
33705                     break;
33706                 default :
33707                     break;
33708                     
33709             }
33710             
33711         }, this);
33712         
33713         if(box.length){
33714             boxes.push(box);
33715             box = [];
33716         }
33717         
33718         var filterPattern = function(box, length)
33719         {
33720             if(!box.length){
33721                 return;
33722             }
33723             
33724             var match = false;
33725             
33726             var pattern = box.slice(0, length);
33727             
33728             var format = [];
33729             
33730             Roo.each(pattern, function(i){
33731                 format.push(i.size);
33732             }, this);
33733             
33734             Roo.each(standard, function(s){
33735                 
33736                 if(String(s) != String(format)){
33737                     return;
33738                 }
33739                 
33740                 match = true;
33741                 return false;
33742                 
33743             }, this);
33744             
33745             if(!match && length == 1){
33746                 return;
33747             }
33748             
33749             if(!match){
33750                 filterPattern(box, length - 1);
33751                 return;
33752             }
33753                 
33754             queue.push(pattern);
33755
33756             box = box.slice(length, box.length);
33757
33758             filterPattern(box, 4);
33759
33760             return;
33761             
33762         }
33763         
33764         Roo.each(boxes, function(box, k){
33765             
33766             if(!box.length){
33767                 return;
33768             }
33769             
33770             if(box.length == 1){
33771                 queue.push(box);
33772                 return;
33773             }
33774             
33775             filterPattern(box, 4);
33776             
33777         }, this);
33778         
33779         
33780         var prune = [];
33781         
33782         var pos = this.el.getBox(true);
33783         
33784         var minX = pos.x;
33785         
33786         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33787         
33788         var hit_end = false;
33789         
33790         Roo.each(queue, function(box){
33791             
33792             if(hit_end){
33793                 
33794                 Roo.each(box, function(b){
33795                 
33796                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33797                     b.el.hide();
33798
33799                 }, this);
33800
33801                 return;
33802             }
33803             
33804             var mx = 0;
33805             
33806             Roo.each(box, function(b){
33807                 
33808                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33809                 b.el.show();
33810
33811                 mx = Math.max(mx, b.x);
33812                 
33813             }, this);
33814             
33815             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33816             
33817             if(maxX < minX){
33818                 
33819                 Roo.each(box, function(b){
33820                 
33821                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33822                     b.el.hide();
33823                     
33824                 }, this);
33825                 
33826                 hit_end = true;
33827                 
33828                 return;
33829             }
33830             
33831             prune.push(box);
33832             
33833         }, this);
33834         
33835         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33836     },
33837     
33838     /** Sets position of item in DOM
33839     * @param {Element} item
33840     * @param {Number} x - horizontal position
33841     * @param {Number} y - vertical position
33842     * @param {Boolean} isInstant - disables transitions
33843     */
33844     _processVerticalLayoutQueue : function( queue, isInstant )
33845     {
33846         var pos = this.el.getBox(true);
33847         var x = pos.x;
33848         var y = pos.y;
33849         var maxY = [];
33850         
33851         for (var i = 0; i < this.cols; i++){
33852             maxY[i] = pos.y;
33853         }
33854         
33855         Roo.each(queue, function(box, k){
33856             
33857             var col = k % this.cols;
33858             
33859             Roo.each(box, function(b,kk){
33860                 
33861                 b.el.position('absolute');
33862                 
33863                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33864                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33865                 
33866                 if(b.size == 'md-left' || b.size == 'md-right'){
33867                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33868                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33869                 }
33870                 
33871                 b.el.setWidth(width);
33872                 b.el.setHeight(height);
33873                 // iframe?
33874                 b.el.select('iframe',true).setSize(width,height);
33875                 
33876             }, this);
33877             
33878             for (var i = 0; i < this.cols; i++){
33879                 
33880                 if(maxY[i] < maxY[col]){
33881                     col = i;
33882                     continue;
33883                 }
33884                 
33885                 col = Math.min(col, i);
33886                 
33887             }
33888             
33889             x = pos.x + col * (this.colWidth + this.padWidth);
33890             
33891             y = maxY[col];
33892             
33893             var positions = [];
33894             
33895             switch (box.length){
33896                 case 1 :
33897                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33898                     break;
33899                 case 2 :
33900                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33901                     break;
33902                 case 3 :
33903                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33904                     break;
33905                 case 4 :
33906                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33907                     break;
33908                 default :
33909                     break;
33910             }
33911             
33912             Roo.each(box, function(b,kk){
33913                 
33914                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33915                 
33916                 var sz = b.el.getSize();
33917                 
33918                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33919                 
33920             }, this);
33921             
33922         }, this);
33923         
33924         var mY = 0;
33925         
33926         for (var i = 0; i < this.cols; i++){
33927             mY = Math.max(mY, maxY[i]);
33928         }
33929         
33930         this.el.setHeight(mY - pos.y);
33931         
33932     },
33933     
33934 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33935 //    {
33936 //        var pos = this.el.getBox(true);
33937 //        var x = pos.x;
33938 //        var y = pos.y;
33939 //        var maxX = pos.right;
33940 //        
33941 //        var maxHeight = 0;
33942 //        
33943 //        Roo.each(items, function(item, k){
33944 //            
33945 //            var c = k % 2;
33946 //            
33947 //            item.el.position('absolute');
33948 //                
33949 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33950 //
33951 //            item.el.setWidth(width);
33952 //
33953 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33954 //
33955 //            item.el.setHeight(height);
33956 //            
33957 //            if(c == 0){
33958 //                item.el.setXY([x, y], isInstant ? false : true);
33959 //            } else {
33960 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33961 //            }
33962 //            
33963 //            y = y + height + this.alternativePadWidth;
33964 //            
33965 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33966 //            
33967 //        }, this);
33968 //        
33969 //        this.el.setHeight(maxHeight);
33970 //        
33971 //    },
33972     
33973     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33974     {
33975         var pos = this.el.getBox(true);
33976         
33977         var minX = pos.x;
33978         var minY = pos.y;
33979         
33980         var maxX = pos.right;
33981         
33982         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33983         
33984         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33985         
33986         Roo.each(queue, function(box, k){
33987             
33988             Roo.each(box, function(b, kk){
33989                 
33990                 b.el.position('absolute');
33991                 
33992                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33993                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33994                 
33995                 if(b.size == 'md-left' || b.size == 'md-right'){
33996                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33997                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33998                 }
33999                 
34000                 b.el.setWidth(width);
34001                 b.el.setHeight(height);
34002                 
34003             }, this);
34004             
34005             if(!box.length){
34006                 return;
34007             }
34008             
34009             var positions = [];
34010             
34011             switch (box.length){
34012                 case 1 :
34013                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34014                     break;
34015                 case 2 :
34016                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34017                     break;
34018                 case 3 :
34019                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34020                     break;
34021                 case 4 :
34022                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34023                     break;
34024                 default :
34025                     break;
34026             }
34027             
34028             Roo.each(box, function(b,kk){
34029                 
34030                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34031                 
34032                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34033                 
34034             }, this);
34035             
34036         }, this);
34037         
34038     },
34039     
34040     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34041     {
34042         Roo.each(eItems, function(b,k){
34043             
34044             b.size = (k == 0) ? 'sm' : 'xs';
34045             b.x = (k == 0) ? 2 : 1;
34046             b.y = (k == 0) ? 2 : 1;
34047             
34048             b.el.position('absolute');
34049             
34050             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34051                 
34052             b.el.setWidth(width);
34053             
34054             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34055             
34056             b.el.setHeight(height);
34057             
34058         }, this);
34059
34060         var positions = [];
34061         
34062         positions.push({
34063             x : maxX - this.unitWidth * 2 - this.gutter,
34064             y : minY
34065         });
34066         
34067         positions.push({
34068             x : maxX - this.unitWidth,
34069             y : minY + (this.unitWidth + this.gutter) * 2
34070         });
34071         
34072         positions.push({
34073             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34074             y : minY
34075         });
34076         
34077         Roo.each(eItems, function(b,k){
34078             
34079             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34080
34081         }, this);
34082         
34083     },
34084     
34085     getVerticalOneBoxColPositions : function(x, y, box)
34086     {
34087         var pos = [];
34088         
34089         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34090         
34091         if(box[0].size == 'md-left'){
34092             rand = 0;
34093         }
34094         
34095         if(box[0].size == 'md-right'){
34096             rand = 1;
34097         }
34098         
34099         pos.push({
34100             x : x + (this.unitWidth + this.gutter) * rand,
34101             y : y
34102         });
34103         
34104         return pos;
34105     },
34106     
34107     getVerticalTwoBoxColPositions : function(x, y, box)
34108     {
34109         var pos = [];
34110         
34111         if(box[0].size == 'xs'){
34112             
34113             pos.push({
34114                 x : x,
34115                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34116             });
34117
34118             pos.push({
34119                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34120                 y : y
34121             });
34122             
34123             return pos;
34124             
34125         }
34126         
34127         pos.push({
34128             x : x,
34129             y : y
34130         });
34131
34132         pos.push({
34133             x : x + (this.unitWidth + this.gutter) * 2,
34134             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34135         });
34136         
34137         return pos;
34138         
34139     },
34140     
34141     getVerticalThreeBoxColPositions : function(x, y, box)
34142     {
34143         var pos = [];
34144         
34145         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34146             
34147             pos.push({
34148                 x : x,
34149                 y : y
34150             });
34151
34152             pos.push({
34153                 x : x + (this.unitWidth + this.gutter) * 1,
34154                 y : y
34155             });
34156             
34157             pos.push({
34158                 x : x + (this.unitWidth + this.gutter) * 2,
34159                 y : y
34160             });
34161             
34162             return pos;
34163             
34164         }
34165         
34166         if(box[0].size == 'xs' && box[1].size == 'xs'){
34167             
34168             pos.push({
34169                 x : x,
34170                 y : y
34171             });
34172
34173             pos.push({
34174                 x : x,
34175                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34176             });
34177             
34178             pos.push({
34179                 x : x + (this.unitWidth + this.gutter) * 1,
34180                 y : y
34181             });
34182             
34183             return pos;
34184             
34185         }
34186         
34187         pos.push({
34188             x : x,
34189             y : y
34190         });
34191
34192         pos.push({
34193             x : x + (this.unitWidth + this.gutter) * 2,
34194             y : y
34195         });
34196
34197         pos.push({
34198             x : x + (this.unitWidth + this.gutter) * 2,
34199             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34200         });
34201             
34202         return pos;
34203         
34204     },
34205     
34206     getVerticalFourBoxColPositions : function(x, y, box)
34207     {
34208         var pos = [];
34209         
34210         if(box[0].size == 'xs'){
34211             
34212             pos.push({
34213                 x : x,
34214                 y : y
34215             });
34216
34217             pos.push({
34218                 x : x,
34219                 y : y + (this.unitHeight + this.gutter) * 1
34220             });
34221             
34222             pos.push({
34223                 x : x,
34224                 y : y + (this.unitHeight + this.gutter) * 2
34225             });
34226             
34227             pos.push({
34228                 x : x + (this.unitWidth + this.gutter) * 1,
34229                 y : y
34230             });
34231             
34232             return pos;
34233             
34234         }
34235         
34236         pos.push({
34237             x : x,
34238             y : y
34239         });
34240
34241         pos.push({
34242             x : x + (this.unitWidth + this.gutter) * 2,
34243             y : y
34244         });
34245
34246         pos.push({
34247             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34248             y : y + (this.unitHeight + this.gutter) * 1
34249         });
34250
34251         pos.push({
34252             x : x + (this.unitWidth + this.gutter) * 2,
34253             y : y + (this.unitWidth + this.gutter) * 2
34254         });
34255
34256         return pos;
34257         
34258     },
34259     
34260     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34261     {
34262         var pos = [];
34263         
34264         if(box[0].size == 'md-left'){
34265             pos.push({
34266                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34267                 y : minY
34268             });
34269             
34270             return pos;
34271         }
34272         
34273         if(box[0].size == 'md-right'){
34274             pos.push({
34275                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34276                 y : minY + (this.unitWidth + this.gutter) * 1
34277             });
34278             
34279             return pos;
34280         }
34281         
34282         var rand = Math.floor(Math.random() * (4 - box[0].y));
34283         
34284         pos.push({
34285             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34286             y : minY + (this.unitWidth + this.gutter) * rand
34287         });
34288         
34289         return pos;
34290         
34291     },
34292     
34293     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34294     {
34295         var pos = [];
34296         
34297         if(box[0].size == 'xs'){
34298             
34299             pos.push({
34300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34301                 y : minY
34302             });
34303
34304             pos.push({
34305                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34306                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34307             });
34308             
34309             return pos;
34310             
34311         }
34312         
34313         pos.push({
34314             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34315             y : minY
34316         });
34317
34318         pos.push({
34319             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34320             y : minY + (this.unitWidth + this.gutter) * 2
34321         });
34322         
34323         return pos;
34324         
34325     },
34326     
34327     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34328     {
34329         var pos = [];
34330         
34331         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34332             
34333             pos.push({
34334                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34335                 y : minY
34336             });
34337
34338             pos.push({
34339                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34340                 y : minY + (this.unitWidth + this.gutter) * 1
34341             });
34342             
34343             pos.push({
34344                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34345                 y : minY + (this.unitWidth + this.gutter) * 2
34346             });
34347             
34348             return pos;
34349             
34350         }
34351         
34352         if(box[0].size == 'xs' && box[1].size == 'xs'){
34353             
34354             pos.push({
34355                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34356                 y : minY
34357             });
34358
34359             pos.push({
34360                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34361                 y : minY
34362             });
34363             
34364             pos.push({
34365                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34366                 y : minY + (this.unitWidth + this.gutter) * 1
34367             });
34368             
34369             return pos;
34370             
34371         }
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[1].x - this.gutter * (box[1].x - 1),
34380             y : minY + (this.unitWidth + this.gutter) * 2
34381         });
34382
34383         pos.push({
34384             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34385             y : minY + (this.unitWidth + this.gutter) * 2
34386         });
34387             
34388         return pos;
34389         
34390     },
34391     
34392     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34393     {
34394         var pos = [];
34395         
34396         if(box[0].size == 'xs'){
34397             
34398             pos.push({
34399                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34400                 y : minY
34401             });
34402
34403             pos.push({
34404                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34405                 y : minY
34406             });
34407             
34408             pos.push({
34409                 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),
34410                 y : minY
34411             });
34412             
34413             pos.push({
34414                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34415                 y : minY + (this.unitWidth + this.gutter) * 1
34416             });
34417             
34418             return pos;
34419             
34420         }
34421         
34422         pos.push({
34423             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34424             y : minY
34425         });
34426         
34427         pos.push({
34428             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34429             y : minY + (this.unitWidth + this.gutter) * 2
34430         });
34431         
34432         pos.push({
34433             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34434             y : minY + (this.unitWidth + this.gutter) * 2
34435         });
34436         
34437         pos.push({
34438             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),
34439             y : minY + (this.unitWidth + this.gutter) * 2
34440         });
34441
34442         return pos;
34443         
34444     },
34445     
34446     /**
34447     * remove a Masonry Brick
34448     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34449     */
34450     removeBrick : function(brick_id)
34451     {
34452         if (!brick_id) {
34453             return;
34454         }
34455         
34456         for (var i = 0; i<this.bricks.length; i++) {
34457             if (this.bricks[i].id == brick_id) {
34458                 this.bricks.splice(i,1);
34459                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34460                 this.initial();
34461             }
34462         }
34463     },
34464     
34465     /**
34466     * adds a Masonry Brick
34467     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34468     */
34469     addBrick : function(cfg)
34470     {
34471         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34472         //this.register(cn);
34473         cn.parentId = this.id;
34474         cn.render(this.el);
34475         return cn;
34476     },
34477     
34478     /**
34479     * register a Masonry Brick
34480     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34481     */
34482     
34483     register : function(brick)
34484     {
34485         this.bricks.push(brick);
34486         brick.masonryId = this.id;
34487     },
34488     
34489     /**
34490     * clear all the Masonry Brick
34491     */
34492     clearAll : function()
34493     {
34494         this.bricks = [];
34495         //this.getChildContainer().dom.innerHTML = "";
34496         this.el.dom.innerHTML = '';
34497     },
34498     
34499     getSelected : function()
34500     {
34501         if (!this.selectedBrick) {
34502             return false;
34503         }
34504         
34505         return this.selectedBrick;
34506     }
34507 });
34508
34509 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34510     
34511     groups: {},
34512      /**
34513     * register a Masonry Layout
34514     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34515     */
34516     
34517     register : function(layout)
34518     {
34519         this.groups[layout.id] = layout;
34520     },
34521     /**
34522     * fetch a  Masonry Layout based on the masonry layout ID
34523     * @param {string} the masonry layout to add
34524     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34525     */
34526     
34527     get: function(layout_id) {
34528         if (typeof(this.groups[layout_id]) == 'undefined') {
34529             return false;
34530         }
34531         return this.groups[layout_id] ;
34532     }
34533     
34534     
34535     
34536 });
34537
34538  
34539
34540  /**
34541  *
34542  * This is based on 
34543  * http://masonry.desandro.com
34544  *
34545  * The idea is to render all the bricks based on vertical width...
34546  *
34547  * The original code extends 'outlayer' - we might need to use that....
34548  * 
34549  */
34550
34551
34552 /**
34553  * @class Roo.bootstrap.LayoutMasonryAuto
34554  * @extends Roo.bootstrap.Component
34555  * Bootstrap Layout Masonry class
34556  * 
34557  * @constructor
34558  * Create a new Element
34559  * @param {Object} config The config object
34560  */
34561
34562 Roo.bootstrap.LayoutMasonryAuto = function(config){
34563     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34564 };
34565
34566 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34567     
34568       /**
34569      * @cfg {Boolean} isFitWidth  - resize the width..
34570      */   
34571     isFitWidth : false,  // options..
34572     /**
34573      * @cfg {Boolean} isOriginLeft = left align?
34574      */   
34575     isOriginLeft : true,
34576     /**
34577      * @cfg {Boolean} isOriginTop = top align?
34578      */   
34579     isOriginTop : false,
34580     /**
34581      * @cfg {Boolean} isLayoutInstant = no animation?
34582      */   
34583     isLayoutInstant : false, // needed?
34584     /**
34585      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34586      */   
34587     isResizingContainer : true,
34588     /**
34589      * @cfg {Number} columnWidth  width of the columns 
34590      */   
34591     
34592     columnWidth : 0,
34593     
34594     /**
34595      * @cfg {Number} maxCols maximum number of columns
34596      */   
34597     
34598     maxCols: 0,
34599     /**
34600      * @cfg {Number} padHeight padding below box..
34601      */   
34602     
34603     padHeight : 10, 
34604     
34605     /**
34606      * @cfg {Boolean} isAutoInitial defalut true
34607      */   
34608     
34609     isAutoInitial : true, 
34610     
34611     // private?
34612     gutter : 0,
34613     
34614     containerWidth: 0,
34615     initialColumnWidth : 0,
34616     currentSize : null,
34617     
34618     colYs : null, // array.
34619     maxY : 0,
34620     padWidth: 10,
34621     
34622     
34623     tag: 'div',
34624     cls: '',
34625     bricks: null, //CompositeElement
34626     cols : 0, // array?
34627     // element : null, // wrapped now this.el
34628     _isLayoutInited : null, 
34629     
34630     
34631     getAutoCreate : function(){
34632         
34633         var cfg = {
34634             tag: this.tag,
34635             cls: 'blog-masonary-wrapper ' + this.cls,
34636             cn : {
34637                 cls : 'mas-boxes masonary'
34638             }
34639         };
34640         
34641         return cfg;
34642     },
34643     
34644     getChildContainer: function( )
34645     {
34646         if (this.boxesEl) {
34647             return this.boxesEl;
34648         }
34649         
34650         this.boxesEl = this.el.select('.mas-boxes').first();
34651         
34652         return this.boxesEl;
34653     },
34654     
34655     
34656     initEvents : function()
34657     {
34658         var _this = this;
34659         
34660         if(this.isAutoInitial){
34661             Roo.log('hook children rendered');
34662             this.on('childrenrendered', function() {
34663                 Roo.log('children rendered');
34664                 _this.initial();
34665             } ,this);
34666         }
34667         
34668     },
34669     
34670     initial : function()
34671     {
34672         this.reloadItems();
34673
34674         this.currentSize = this.el.getBox(true);
34675
34676         /// was window resize... - let's see if this works..
34677         Roo.EventManager.onWindowResize(this.resize, this); 
34678
34679         if(!this.isAutoInitial){
34680             this.layout();
34681             return;
34682         }
34683         
34684         this.layout.defer(500,this);
34685     },
34686     
34687     reloadItems: function()
34688     {
34689         this.bricks = this.el.select('.masonry-brick', true);
34690         
34691         this.bricks.each(function(b) {
34692             //Roo.log(b.getSize());
34693             if (!b.attr('originalwidth')) {
34694                 b.attr('originalwidth',  b.getSize().width);
34695             }
34696             
34697         });
34698         
34699         Roo.log(this.bricks.elements.length);
34700     },
34701     
34702     resize : function()
34703     {
34704         Roo.log('resize');
34705         var cs = this.el.getBox(true);
34706         
34707         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34708             Roo.log("no change in with or X");
34709             return;
34710         }
34711         this.currentSize = cs;
34712         this.layout();
34713     },
34714     
34715     layout : function()
34716     {
34717          Roo.log('layout');
34718         this._resetLayout();
34719         //this._manageStamps();
34720       
34721         // don't animate first layout
34722         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34723         this.layoutItems( isInstant );
34724       
34725         // flag for initalized
34726         this._isLayoutInited = true;
34727     },
34728     
34729     layoutItems : function( isInstant )
34730     {
34731         //var items = this._getItemsForLayout( this.items );
34732         // original code supports filtering layout items.. we just ignore it..
34733         
34734         this._layoutItems( this.bricks , isInstant );
34735       
34736         this._postLayout();
34737     },
34738     _layoutItems : function ( items , isInstant)
34739     {
34740        //this.fireEvent( 'layout', this, items );
34741     
34742
34743         if ( !items || !items.elements.length ) {
34744           // no items, emit event with empty array
34745             return;
34746         }
34747
34748         var queue = [];
34749         items.each(function(item) {
34750             Roo.log("layout item");
34751             Roo.log(item);
34752             // get x/y object from method
34753             var position = this._getItemLayoutPosition( item );
34754             // enqueue
34755             position.item = item;
34756             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34757             queue.push( position );
34758         }, this);
34759       
34760         this._processLayoutQueue( queue );
34761     },
34762     /** Sets position of item in DOM
34763     * @param {Element} item
34764     * @param {Number} x - horizontal position
34765     * @param {Number} y - vertical position
34766     * @param {Boolean} isInstant - disables transitions
34767     */
34768     _processLayoutQueue : function( queue )
34769     {
34770         for ( var i=0, len = queue.length; i < len; i++ ) {
34771             var obj = queue[i];
34772             obj.item.position('absolute');
34773             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34774         }
34775     },
34776       
34777     
34778     /**
34779     * Any logic you want to do after each layout,
34780     * i.e. size the container
34781     */
34782     _postLayout : function()
34783     {
34784         this.resizeContainer();
34785     },
34786     
34787     resizeContainer : function()
34788     {
34789         if ( !this.isResizingContainer ) {
34790             return;
34791         }
34792         var size = this._getContainerSize();
34793         if ( size ) {
34794             this.el.setSize(size.width,size.height);
34795             this.boxesEl.setSize(size.width,size.height);
34796         }
34797     },
34798     
34799     
34800     
34801     _resetLayout : function()
34802     {
34803         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34804         this.colWidth = this.el.getWidth();
34805         //this.gutter = this.el.getWidth(); 
34806         
34807         this.measureColumns();
34808
34809         // reset column Y
34810         var i = this.cols;
34811         this.colYs = [];
34812         while (i--) {
34813             this.colYs.push( 0 );
34814         }
34815     
34816         this.maxY = 0;
34817     },
34818
34819     measureColumns : function()
34820     {
34821         this.getContainerWidth();
34822       // if columnWidth is 0, default to outerWidth of first item
34823         if ( !this.columnWidth ) {
34824             var firstItem = this.bricks.first();
34825             Roo.log(firstItem);
34826             this.columnWidth  = this.containerWidth;
34827             if (firstItem && firstItem.attr('originalwidth') ) {
34828                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34829             }
34830             // columnWidth fall back to item of first element
34831             Roo.log("set column width?");
34832                         this.initialColumnWidth = this.columnWidth  ;
34833
34834             // if first elem has no width, default to size of container
34835             
34836         }
34837         
34838         
34839         if (this.initialColumnWidth) {
34840             this.columnWidth = this.initialColumnWidth;
34841         }
34842         
34843         
34844             
34845         // column width is fixed at the top - however if container width get's smaller we should
34846         // reduce it...
34847         
34848         // this bit calcs how man columns..
34849             
34850         var columnWidth = this.columnWidth += this.gutter;
34851       
34852         // calculate columns
34853         var containerWidth = this.containerWidth + this.gutter;
34854         
34855         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34856         // fix rounding errors, typically with gutters
34857         var excess = columnWidth - containerWidth % columnWidth;
34858         
34859         
34860         // if overshoot is less than a pixel, round up, otherwise floor it
34861         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34862         cols = Math[ mathMethod ]( cols );
34863         this.cols = Math.max( cols, 1 );
34864         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34865         
34866          // padding positioning..
34867         var totalColWidth = this.cols * this.columnWidth;
34868         var padavail = this.containerWidth - totalColWidth;
34869         // so for 2 columns - we need 3 'pads'
34870         
34871         var padNeeded = (1+this.cols) * this.padWidth;
34872         
34873         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34874         
34875         this.columnWidth += padExtra
34876         //this.padWidth = Math.floor(padavail /  ( this.cols));
34877         
34878         // adjust colum width so that padding is fixed??
34879         
34880         // we have 3 columns ... total = width * 3
34881         // we have X left over... that should be used by 
34882         
34883         //if (this.expandC) {
34884             
34885         //}
34886         
34887         
34888         
34889     },
34890     
34891     getContainerWidth : function()
34892     {
34893        /* // container is parent if fit width
34894         var container = this.isFitWidth ? this.element.parentNode : this.element;
34895         // check that this.size and size are there
34896         // IE8 triggers resize on body size change, so they might not be
34897         
34898         var size = getSize( container );  //FIXME
34899         this.containerWidth = size && size.innerWidth; //FIXME
34900         */
34901          
34902         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34903         
34904     },
34905     
34906     _getItemLayoutPosition : function( item )  // what is item?
34907     {
34908         // we resize the item to our columnWidth..
34909       
34910         item.setWidth(this.columnWidth);
34911         item.autoBoxAdjust  = false;
34912         
34913         var sz = item.getSize();
34914  
34915         // how many columns does this brick span
34916         var remainder = this.containerWidth % this.columnWidth;
34917         
34918         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34919         // round if off by 1 pixel, otherwise use ceil
34920         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34921         colSpan = Math.min( colSpan, this.cols );
34922         
34923         // normally this should be '1' as we dont' currently allow multi width columns..
34924         
34925         var colGroup = this._getColGroup( colSpan );
34926         // get the minimum Y value from the columns
34927         var minimumY = Math.min.apply( Math, colGroup );
34928         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34929         
34930         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34931          
34932         // position the brick
34933         var position = {
34934             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34935             y: this.currentSize.y + minimumY + this.padHeight
34936         };
34937         
34938         Roo.log(position);
34939         // apply setHeight to necessary columns
34940         var setHeight = minimumY + sz.height + this.padHeight;
34941         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34942         
34943         var setSpan = this.cols + 1 - colGroup.length;
34944         for ( var i = 0; i < setSpan; i++ ) {
34945           this.colYs[ shortColIndex + i ] = setHeight ;
34946         }
34947       
34948         return position;
34949     },
34950     
34951     /**
34952      * @param {Number} colSpan - number of columns the element spans
34953      * @returns {Array} colGroup
34954      */
34955     _getColGroup : function( colSpan )
34956     {
34957         if ( colSpan < 2 ) {
34958           // if brick spans only one column, use all the column Ys
34959           return this.colYs;
34960         }
34961       
34962         var colGroup = [];
34963         // how many different places could this brick fit horizontally
34964         var groupCount = this.cols + 1 - colSpan;
34965         // for each group potential horizontal position
34966         for ( var i = 0; i < groupCount; i++ ) {
34967           // make an array of colY values for that one group
34968           var groupColYs = this.colYs.slice( i, i + colSpan );
34969           // and get the max value of the array
34970           colGroup[i] = Math.max.apply( Math, groupColYs );
34971         }
34972         return colGroup;
34973     },
34974     /*
34975     _manageStamp : function( stamp )
34976     {
34977         var stampSize =  stamp.getSize();
34978         var offset = stamp.getBox();
34979         // get the columns that this stamp affects
34980         var firstX = this.isOriginLeft ? offset.x : offset.right;
34981         var lastX = firstX + stampSize.width;
34982         var firstCol = Math.floor( firstX / this.columnWidth );
34983         firstCol = Math.max( 0, firstCol );
34984         
34985         var lastCol = Math.floor( lastX / this.columnWidth );
34986         // lastCol should not go over if multiple of columnWidth #425
34987         lastCol -= lastX % this.columnWidth ? 0 : 1;
34988         lastCol = Math.min( this.cols - 1, lastCol );
34989         
34990         // set colYs to bottom of the stamp
34991         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34992             stampSize.height;
34993             
34994         for ( var i = firstCol; i <= lastCol; i++ ) {
34995           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34996         }
34997     },
34998     */
34999     
35000     _getContainerSize : function()
35001     {
35002         this.maxY = Math.max.apply( Math, this.colYs );
35003         var size = {
35004             height: this.maxY
35005         };
35006       
35007         if ( this.isFitWidth ) {
35008             size.width = this._getContainerFitWidth();
35009         }
35010       
35011         return size;
35012     },
35013     
35014     _getContainerFitWidth : function()
35015     {
35016         var unusedCols = 0;
35017         // count unused columns
35018         var i = this.cols;
35019         while ( --i ) {
35020           if ( this.colYs[i] !== 0 ) {
35021             break;
35022           }
35023           unusedCols++;
35024         }
35025         // fit container to columns that have been used
35026         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35027     },
35028     
35029     needsResizeLayout : function()
35030     {
35031         var previousWidth = this.containerWidth;
35032         this.getContainerWidth();
35033         return previousWidth !== this.containerWidth;
35034     }
35035  
35036 });
35037
35038  
35039
35040  /*
35041  * - LGPL
35042  *
35043  * element
35044  * 
35045  */
35046
35047 /**
35048  * @class Roo.bootstrap.MasonryBrick
35049  * @extends Roo.bootstrap.Component
35050  * Bootstrap MasonryBrick class
35051  * 
35052  * @constructor
35053  * Create a new MasonryBrick
35054  * @param {Object} config The config object
35055  */
35056
35057 Roo.bootstrap.MasonryBrick = function(config){
35058     
35059     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35060     
35061     Roo.bootstrap.MasonryBrick.register(this);
35062     
35063     this.addEvents({
35064         // raw events
35065         /**
35066          * @event click
35067          * When a MasonryBrick is clcik
35068          * @param {Roo.bootstrap.MasonryBrick} this
35069          * @param {Roo.EventObject} e
35070          */
35071         "click" : true
35072     });
35073 };
35074
35075 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35076     
35077     /**
35078      * @cfg {String} title
35079      */   
35080     title : '',
35081     /**
35082      * @cfg {String} html
35083      */   
35084     html : '',
35085     /**
35086      * @cfg {String} bgimage
35087      */   
35088     bgimage : '',
35089     /**
35090      * @cfg {String} videourl
35091      */   
35092     videourl : '',
35093     /**
35094      * @cfg {String} cls
35095      */   
35096     cls : '',
35097     /**
35098      * @cfg {String} href
35099      */   
35100     href : '',
35101     /**
35102      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35103      */   
35104     size : 'xs',
35105     
35106     /**
35107      * @cfg {String} placetitle (center|bottom)
35108      */   
35109     placetitle : '',
35110     
35111     /**
35112      * @cfg {Boolean} isFitContainer defalut true
35113      */   
35114     isFitContainer : true, 
35115     
35116     /**
35117      * @cfg {Boolean} preventDefault defalut false
35118      */   
35119     preventDefault : false, 
35120     
35121     /**
35122      * @cfg {Boolean} inverse defalut false
35123      */   
35124     maskInverse : false, 
35125     
35126     getAutoCreate : function()
35127     {
35128         if(!this.isFitContainer){
35129             return this.getSplitAutoCreate();
35130         }
35131         
35132         var cls = 'masonry-brick masonry-brick-full';
35133         
35134         if(this.href.length){
35135             cls += ' masonry-brick-link';
35136         }
35137         
35138         if(this.bgimage.length){
35139             cls += ' masonry-brick-image';
35140         }
35141         
35142         if(this.maskInverse){
35143             cls += ' mask-inverse';
35144         }
35145         
35146         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35147             cls += ' enable-mask';
35148         }
35149         
35150         if(this.size){
35151             cls += ' masonry-' + this.size + '-brick';
35152         }
35153         
35154         if(this.placetitle.length){
35155             
35156             switch (this.placetitle) {
35157                 case 'center' :
35158                     cls += ' masonry-center-title';
35159                     break;
35160                 case 'bottom' :
35161                     cls += ' masonry-bottom-title';
35162                     break;
35163                 default:
35164                     break;
35165             }
35166             
35167         } else {
35168             if(!this.html.length && !this.bgimage.length){
35169                 cls += ' masonry-center-title';
35170             }
35171
35172             if(!this.html.length && this.bgimage.length){
35173                 cls += ' masonry-bottom-title';
35174             }
35175         }
35176         
35177         if(this.cls){
35178             cls += ' ' + this.cls;
35179         }
35180         
35181         var cfg = {
35182             tag: (this.href.length) ? 'a' : 'div',
35183             cls: cls,
35184             cn: [
35185                 {
35186                     tag: 'div',
35187                     cls: 'masonry-brick-mask'
35188                 },
35189                 {
35190                     tag: 'div',
35191                     cls: 'masonry-brick-paragraph',
35192                     cn: []
35193                 }
35194             ]
35195         };
35196         
35197         if(this.href.length){
35198             cfg.href = this.href;
35199         }
35200         
35201         var cn = cfg.cn[1].cn;
35202         
35203         if(this.title.length){
35204             cn.push({
35205                 tag: 'h4',
35206                 cls: 'masonry-brick-title',
35207                 html: this.title
35208             });
35209         }
35210         
35211         if(this.html.length){
35212             cn.push({
35213                 tag: 'p',
35214                 cls: 'masonry-brick-text',
35215                 html: this.html
35216             });
35217         }
35218         
35219         if (!this.title.length && !this.html.length) {
35220             cfg.cn[1].cls += ' hide';
35221         }
35222         
35223         if(this.bgimage.length){
35224             cfg.cn.push({
35225                 tag: 'img',
35226                 cls: 'masonry-brick-image-view',
35227                 src: this.bgimage
35228             });
35229         }
35230         
35231         if(this.videourl.length){
35232             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35233             // youtube support only?
35234             cfg.cn.push({
35235                 tag: 'iframe',
35236                 cls: 'masonry-brick-image-view',
35237                 src: vurl,
35238                 frameborder : 0,
35239                 allowfullscreen : true
35240             });
35241         }
35242         
35243         return cfg;
35244         
35245     },
35246     
35247     getSplitAutoCreate : function()
35248     {
35249         var cls = 'masonry-brick masonry-brick-split';
35250         
35251         if(this.href.length){
35252             cls += ' masonry-brick-link';
35253         }
35254         
35255         if(this.bgimage.length){
35256             cls += ' masonry-brick-image';
35257         }
35258         
35259         if(this.size){
35260             cls += ' masonry-' + this.size + '-brick';
35261         }
35262         
35263         switch (this.placetitle) {
35264             case 'center' :
35265                 cls += ' masonry-center-title';
35266                 break;
35267             case 'bottom' :
35268                 cls += ' masonry-bottom-title';
35269                 break;
35270             default:
35271                 if(!this.bgimage.length){
35272                     cls += ' masonry-center-title';
35273                 }
35274
35275                 if(this.bgimage.length){
35276                     cls += ' masonry-bottom-title';
35277                 }
35278                 break;
35279         }
35280         
35281         if(this.cls){
35282             cls += ' ' + this.cls;
35283         }
35284         
35285         var cfg = {
35286             tag: (this.href.length) ? 'a' : 'div',
35287             cls: cls,
35288             cn: [
35289                 {
35290                     tag: 'div',
35291                     cls: 'masonry-brick-split-head',
35292                     cn: [
35293                         {
35294                             tag: 'div',
35295                             cls: 'masonry-brick-paragraph',
35296                             cn: []
35297                         }
35298                     ]
35299                 },
35300                 {
35301                     tag: 'div',
35302                     cls: 'masonry-brick-split-body',
35303                     cn: []
35304                 }
35305             ]
35306         };
35307         
35308         if(this.href.length){
35309             cfg.href = this.href;
35310         }
35311         
35312         if(this.title.length){
35313             cfg.cn[0].cn[0].cn.push({
35314                 tag: 'h4',
35315                 cls: 'masonry-brick-title',
35316                 html: this.title
35317             });
35318         }
35319         
35320         if(this.html.length){
35321             cfg.cn[1].cn.push({
35322                 tag: 'p',
35323                 cls: 'masonry-brick-text',
35324                 html: this.html
35325             });
35326         }
35327
35328         if(this.bgimage.length){
35329             cfg.cn[0].cn.push({
35330                 tag: 'img',
35331                 cls: 'masonry-brick-image-view',
35332                 src: this.bgimage
35333             });
35334         }
35335         
35336         if(this.videourl.length){
35337             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35338             // youtube support only?
35339             cfg.cn[0].cn.cn.push({
35340                 tag: 'iframe',
35341                 cls: 'masonry-brick-image-view',
35342                 src: vurl,
35343                 frameborder : 0,
35344                 allowfullscreen : true
35345             });
35346         }
35347         
35348         return cfg;
35349     },
35350     
35351     initEvents: function() 
35352     {
35353         switch (this.size) {
35354             case 'xs' :
35355                 this.x = 1;
35356                 this.y = 1;
35357                 break;
35358             case 'sm' :
35359                 this.x = 2;
35360                 this.y = 2;
35361                 break;
35362             case 'md' :
35363             case 'md-left' :
35364             case 'md-right' :
35365                 this.x = 3;
35366                 this.y = 3;
35367                 break;
35368             case 'tall' :
35369                 this.x = 2;
35370                 this.y = 3;
35371                 break;
35372             case 'wide' :
35373                 this.x = 3;
35374                 this.y = 2;
35375                 break;
35376             case 'wide-thin' :
35377                 this.x = 3;
35378                 this.y = 1;
35379                 break;
35380                         
35381             default :
35382                 break;
35383         }
35384         
35385         if(Roo.isTouch){
35386             this.el.on('touchstart', this.onTouchStart, this);
35387             this.el.on('touchmove', this.onTouchMove, this);
35388             this.el.on('touchend', this.onTouchEnd, this);
35389             this.el.on('contextmenu', this.onContextMenu, this);
35390         } else {
35391             this.el.on('mouseenter'  ,this.enter, this);
35392             this.el.on('mouseleave', this.leave, this);
35393             this.el.on('click', this.onClick, this);
35394         }
35395         
35396         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35397             this.parent().bricks.push(this);   
35398         }
35399         
35400     },
35401     
35402     onClick: function(e, el)
35403     {
35404         var time = this.endTimer - this.startTimer;
35405         // Roo.log(e.preventDefault());
35406         if(Roo.isTouch){
35407             if(time > 1000){
35408                 e.preventDefault();
35409                 return;
35410             }
35411         }
35412         
35413         if(!this.preventDefault){
35414             return;
35415         }
35416         
35417         e.preventDefault();
35418         
35419         if (this.activeClass != '') {
35420             this.selectBrick();
35421         }
35422         
35423         this.fireEvent('click', this, e);
35424     },
35425     
35426     enter: function(e, el)
35427     {
35428         e.preventDefault();
35429         
35430         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35431             return;
35432         }
35433         
35434         if(this.bgimage.length && this.html.length){
35435             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35436         }
35437     },
35438     
35439     leave: function(e, el)
35440     {
35441         e.preventDefault();
35442         
35443         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35444             return;
35445         }
35446         
35447         if(this.bgimage.length && this.html.length){
35448             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35449         }
35450     },
35451     
35452     onTouchStart: function(e, el)
35453     {
35454 //        e.preventDefault();
35455         
35456         this.touchmoved = false;
35457         
35458         if(!this.isFitContainer){
35459             return;
35460         }
35461         
35462         if(!this.bgimage.length || !this.html.length){
35463             return;
35464         }
35465         
35466         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35467         
35468         this.timer = new Date().getTime();
35469         
35470     },
35471     
35472     onTouchMove: function(e, el)
35473     {
35474         this.touchmoved = true;
35475     },
35476     
35477     onContextMenu : function(e,el)
35478     {
35479         e.preventDefault();
35480         e.stopPropagation();
35481         return false;
35482     },
35483     
35484     onTouchEnd: function(e, el)
35485     {
35486 //        e.preventDefault();
35487         
35488         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35489         
35490             this.leave(e,el);
35491             
35492             return;
35493         }
35494         
35495         if(!this.bgimage.length || !this.html.length){
35496             
35497             if(this.href.length){
35498                 window.location.href = this.href;
35499             }
35500             
35501             return;
35502         }
35503         
35504         if(!this.isFitContainer){
35505             return;
35506         }
35507         
35508         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35509         
35510         window.location.href = this.href;
35511     },
35512     
35513     //selection on single brick only
35514     selectBrick : function() {
35515         
35516         if (!this.parentId) {
35517             return;
35518         }
35519         
35520         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35521         var index = m.selectedBrick.indexOf(this.id);
35522         
35523         if ( index > -1) {
35524             m.selectedBrick.splice(index,1);
35525             this.el.removeClass(this.activeClass);
35526             return;
35527         }
35528         
35529         for(var i = 0; i < m.selectedBrick.length; i++) {
35530             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35531             b.el.removeClass(b.activeClass);
35532         }
35533         
35534         m.selectedBrick = [];
35535         
35536         m.selectedBrick.push(this.id);
35537         this.el.addClass(this.activeClass);
35538         return;
35539     },
35540     
35541     isSelected : function(){
35542         return this.el.hasClass(this.activeClass);
35543         
35544     }
35545 });
35546
35547 Roo.apply(Roo.bootstrap.MasonryBrick, {
35548     
35549     //groups: {},
35550     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35551      /**
35552     * register a Masonry Brick
35553     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35554     */
35555     
35556     register : function(brick)
35557     {
35558         //this.groups[brick.id] = brick;
35559         this.groups.add(brick.id, brick);
35560     },
35561     /**
35562     * fetch a  masonry brick based on the masonry brick ID
35563     * @param {string} the masonry brick to add
35564     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35565     */
35566     
35567     get: function(brick_id) 
35568     {
35569         // if (typeof(this.groups[brick_id]) == 'undefined') {
35570         //     return false;
35571         // }
35572         // return this.groups[brick_id] ;
35573         
35574         if(this.groups.key(brick_id)) {
35575             return this.groups.key(brick_id);
35576         }
35577         
35578         return false;
35579     }
35580     
35581     
35582     
35583 });
35584
35585  /*
35586  * - LGPL
35587  *
35588  * element
35589  * 
35590  */
35591
35592 /**
35593  * @class Roo.bootstrap.Brick
35594  * @extends Roo.bootstrap.Component
35595  * Bootstrap Brick class
35596  * 
35597  * @constructor
35598  * Create a new Brick
35599  * @param {Object} config The config object
35600  */
35601
35602 Roo.bootstrap.Brick = function(config){
35603     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35604     
35605     this.addEvents({
35606         // raw events
35607         /**
35608          * @event click
35609          * When a Brick is click
35610          * @param {Roo.bootstrap.Brick} this
35611          * @param {Roo.EventObject} e
35612          */
35613         "click" : true
35614     });
35615 };
35616
35617 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35618     
35619     /**
35620      * @cfg {String} title
35621      */   
35622     title : '',
35623     /**
35624      * @cfg {String} html
35625      */   
35626     html : '',
35627     /**
35628      * @cfg {String} bgimage
35629      */   
35630     bgimage : '',
35631     /**
35632      * @cfg {String} cls
35633      */   
35634     cls : '',
35635     /**
35636      * @cfg {String} href
35637      */   
35638     href : '',
35639     /**
35640      * @cfg {String} video
35641      */   
35642     video : '',
35643     /**
35644      * @cfg {Boolean} square
35645      */   
35646     square : true,
35647     
35648     getAutoCreate : function()
35649     {
35650         var cls = 'roo-brick';
35651         
35652         if(this.href.length){
35653             cls += ' roo-brick-link';
35654         }
35655         
35656         if(this.bgimage.length){
35657             cls += ' roo-brick-image';
35658         }
35659         
35660         if(!this.html.length && !this.bgimage.length){
35661             cls += ' roo-brick-center-title';
35662         }
35663         
35664         if(!this.html.length && this.bgimage.length){
35665             cls += ' roo-brick-bottom-title';
35666         }
35667         
35668         if(this.cls){
35669             cls += ' ' + this.cls;
35670         }
35671         
35672         var cfg = {
35673             tag: (this.href.length) ? 'a' : 'div',
35674             cls: cls,
35675             cn: [
35676                 {
35677                     tag: 'div',
35678                     cls: 'roo-brick-paragraph',
35679                     cn: []
35680                 }
35681             ]
35682         };
35683         
35684         if(this.href.length){
35685             cfg.href = this.href;
35686         }
35687         
35688         var cn = cfg.cn[0].cn;
35689         
35690         if(this.title.length){
35691             cn.push({
35692                 tag: 'h4',
35693                 cls: 'roo-brick-title',
35694                 html: this.title
35695             });
35696         }
35697         
35698         if(this.html.length){
35699             cn.push({
35700                 tag: 'p',
35701                 cls: 'roo-brick-text',
35702                 html: this.html
35703             });
35704         } else {
35705             cn.cls += ' hide';
35706         }
35707         
35708         if(this.bgimage.length){
35709             cfg.cn.push({
35710                 tag: 'img',
35711                 cls: 'roo-brick-image-view',
35712                 src: this.bgimage
35713             });
35714         }
35715         
35716         return cfg;
35717     },
35718     
35719     initEvents: function() 
35720     {
35721         if(this.title.length || this.html.length){
35722             this.el.on('mouseenter'  ,this.enter, this);
35723             this.el.on('mouseleave', this.leave, this);
35724         }
35725         
35726         Roo.EventManager.onWindowResize(this.resize, this); 
35727         
35728         if(this.bgimage.length){
35729             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35730             this.imageEl.on('load', this.onImageLoad, this);
35731             return;
35732         }
35733         
35734         this.resize();
35735     },
35736     
35737     onImageLoad : function()
35738     {
35739         this.resize();
35740     },
35741     
35742     resize : function()
35743     {
35744         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35745         
35746         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35747         
35748         if(this.bgimage.length){
35749             var image = this.el.select('.roo-brick-image-view', true).first();
35750             
35751             image.setWidth(paragraph.getWidth());
35752             
35753             if(this.square){
35754                 image.setHeight(paragraph.getWidth());
35755             }
35756             
35757             this.el.setHeight(image.getHeight());
35758             paragraph.setHeight(image.getHeight());
35759             
35760         }
35761         
35762     },
35763     
35764     enter: function(e, el)
35765     {
35766         e.preventDefault();
35767         
35768         if(this.bgimage.length){
35769             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35770             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35771         }
35772     },
35773     
35774     leave: function(e, el)
35775     {
35776         e.preventDefault();
35777         
35778         if(this.bgimage.length){
35779             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35780             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35781         }
35782     }
35783     
35784 });
35785
35786  
35787
35788  /*
35789  * - LGPL
35790  *
35791  * Number field 
35792  */
35793
35794 /**
35795  * @class Roo.bootstrap.NumberField
35796  * @extends Roo.bootstrap.Input
35797  * Bootstrap NumberField class
35798  * 
35799  * 
35800  * 
35801  * 
35802  * @constructor
35803  * Create a new NumberField
35804  * @param {Object} config The config object
35805  */
35806
35807 Roo.bootstrap.NumberField = function(config){
35808     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35809 };
35810
35811 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35812     
35813     /**
35814      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35815      */
35816     allowDecimals : true,
35817     /**
35818      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35819      */
35820     decimalSeparator : ".",
35821     /**
35822      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35823      */
35824     decimalPrecision : 2,
35825     /**
35826      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35827      */
35828     allowNegative : true,
35829     
35830     /**
35831      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35832      */
35833     allowZero: true,
35834     /**
35835      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35836      */
35837     minValue : Number.NEGATIVE_INFINITY,
35838     /**
35839      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35840      */
35841     maxValue : Number.MAX_VALUE,
35842     /**
35843      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35844      */
35845     minText : "The minimum value for this field is {0}",
35846     /**
35847      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35848      */
35849     maxText : "The maximum value for this field is {0}",
35850     /**
35851      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35852      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35853      */
35854     nanText : "{0} is not a valid number",
35855     /**
35856      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35857      */
35858     thousandsDelimiter : false,
35859     /**
35860      * @cfg {String} valueAlign alignment of value
35861      */
35862     valueAlign : "left",
35863
35864     getAutoCreate : function()
35865     {
35866         var hiddenInput = {
35867             tag: 'input',
35868             type: 'hidden',
35869             id: Roo.id(),
35870             cls: 'hidden-number-input'
35871         };
35872         
35873         if (this.name) {
35874             hiddenInput.name = this.name;
35875         }
35876         
35877         this.name = '';
35878         
35879         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35880         
35881         this.name = hiddenInput.name;
35882         
35883         if(cfg.cn.length > 0) {
35884             cfg.cn.push(hiddenInput);
35885         }
35886         
35887         return cfg;
35888     },
35889
35890     // private
35891     initEvents : function()
35892     {   
35893         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35894         
35895         var allowed = "0123456789";
35896         
35897         if(this.allowDecimals){
35898             allowed += this.decimalSeparator;
35899         }
35900         
35901         if(this.allowNegative){
35902             allowed += "-";
35903         }
35904         
35905         if(this.thousandsDelimiter) {
35906             allowed += ",";
35907         }
35908         
35909         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35910         
35911         var keyPress = function(e){
35912             
35913             var k = e.getKey();
35914             
35915             var c = e.getCharCode();
35916             
35917             if(
35918                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35919                     allowed.indexOf(String.fromCharCode(c)) === -1
35920             ){
35921                 e.stopEvent();
35922                 return;
35923             }
35924             
35925             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35926                 return;
35927             }
35928             
35929             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35930                 e.stopEvent();
35931             }
35932         };
35933         
35934         this.el.on("keypress", keyPress, this);
35935     },
35936     
35937     validateValue : function(value)
35938     {
35939         
35940         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35941             return false;
35942         }
35943         
35944         var num = this.parseValue(value);
35945         
35946         if(isNaN(num)){
35947             this.markInvalid(String.format(this.nanText, value));
35948             return false;
35949         }
35950         
35951         if(num < this.minValue){
35952             this.markInvalid(String.format(this.minText, this.minValue));
35953             return false;
35954         }
35955         
35956         if(num > this.maxValue){
35957             this.markInvalid(String.format(this.maxText, this.maxValue));
35958             return false;
35959         }
35960         
35961         return true;
35962     },
35963
35964     getValue : function()
35965     {
35966         var v = this.hiddenEl().getValue();
35967         
35968         return this.fixPrecision(this.parseValue(v));
35969     },
35970
35971     parseValue : function(value)
35972     {
35973         if(this.thousandsDelimiter) {
35974             value += "";
35975             r = new RegExp(",", "g");
35976             value = value.replace(r, "");
35977         }
35978         
35979         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35980         return isNaN(value) ? '' : value;
35981     },
35982
35983     fixPrecision : function(value)
35984     {
35985         if(this.thousandsDelimiter) {
35986             value += "";
35987             r = new RegExp(",", "g");
35988             value = value.replace(r, "");
35989         }
35990         
35991         var nan = isNaN(value);
35992         
35993         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35994             return nan ? '' : value;
35995         }
35996         return parseFloat(value).toFixed(this.decimalPrecision);
35997     },
35998
35999     setValue : function(v)
36000     {
36001         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36002         
36003         this.value = v;
36004         
36005         if(this.rendered){
36006             
36007             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36008             
36009             this.inputEl().dom.value = (v == '') ? '' :
36010                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36011             
36012             if(!this.allowZero && v === '0') {
36013                 this.hiddenEl().dom.value = '';
36014                 this.inputEl().dom.value = '';
36015             }
36016             
36017             this.validate();
36018         }
36019     },
36020
36021     decimalPrecisionFcn : function(v)
36022     {
36023         return Math.floor(v);
36024     },
36025
36026     beforeBlur : function()
36027     {
36028         var v = this.parseValue(this.getRawValue());
36029         
36030         if(v || v === 0 || v === ''){
36031             this.setValue(v);
36032         }
36033     },
36034     
36035     hiddenEl : function()
36036     {
36037         return this.el.select('input.hidden-number-input',true).first();
36038     }
36039     
36040 });
36041
36042  
36043
36044 /*
36045 * Licence: LGPL
36046 */
36047
36048 /**
36049  * @class Roo.bootstrap.DocumentSlider
36050  * @extends Roo.bootstrap.Component
36051  * Bootstrap DocumentSlider class
36052  * 
36053  * @constructor
36054  * Create a new DocumentViewer
36055  * @param {Object} config The config object
36056  */
36057
36058 Roo.bootstrap.DocumentSlider = function(config){
36059     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36060     
36061     this.files = [];
36062     
36063     this.addEvents({
36064         /**
36065          * @event initial
36066          * Fire after initEvent
36067          * @param {Roo.bootstrap.DocumentSlider} this
36068          */
36069         "initial" : true,
36070         /**
36071          * @event update
36072          * Fire after update
36073          * @param {Roo.bootstrap.DocumentSlider} this
36074          */
36075         "update" : true,
36076         /**
36077          * @event click
36078          * Fire after click
36079          * @param {Roo.bootstrap.DocumentSlider} this
36080          */
36081         "click" : true
36082     });
36083 };
36084
36085 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36086     
36087     files : false,
36088     
36089     indicator : 0,
36090     
36091     getAutoCreate : function()
36092     {
36093         var cfg = {
36094             tag : 'div',
36095             cls : 'roo-document-slider',
36096             cn : [
36097                 {
36098                     tag : 'div',
36099                     cls : 'roo-document-slider-header',
36100                     cn : [
36101                         {
36102                             tag : 'div',
36103                             cls : 'roo-document-slider-header-title'
36104                         }
36105                     ]
36106                 },
36107                 {
36108                     tag : 'div',
36109                     cls : 'roo-document-slider-body',
36110                     cn : [
36111                         {
36112                             tag : 'div',
36113                             cls : 'roo-document-slider-prev',
36114                             cn : [
36115                                 {
36116                                     tag : 'i',
36117                                     cls : 'fa fa-chevron-left'
36118                                 }
36119                             ]
36120                         },
36121                         {
36122                             tag : 'div',
36123                             cls : 'roo-document-slider-thumb',
36124                             cn : [
36125                                 {
36126                                     tag : 'img',
36127                                     cls : 'roo-document-slider-image'
36128                                 }
36129                             ]
36130                         },
36131                         {
36132                             tag : 'div',
36133                             cls : 'roo-document-slider-next',
36134                             cn : [
36135                                 {
36136                                     tag : 'i',
36137                                     cls : 'fa fa-chevron-right'
36138                                 }
36139                             ]
36140                         }
36141                     ]
36142                 }
36143             ]
36144         };
36145         
36146         return cfg;
36147     },
36148     
36149     initEvents : function()
36150     {
36151         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36152         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36153         
36154         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36155         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36156         
36157         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36158         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36159         
36160         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36161         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36162         
36163         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36164         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36165         
36166         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36167         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36168         
36169         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36170         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36171         
36172         this.thumbEl.on('click', this.onClick, this);
36173         
36174         this.prevIndicator.on('click', this.prev, this);
36175         
36176         this.nextIndicator.on('click', this.next, this);
36177         
36178     },
36179     
36180     initial : function()
36181     {
36182         if(this.files.length){
36183             this.indicator = 1;
36184             this.update()
36185         }
36186         
36187         this.fireEvent('initial', this);
36188     },
36189     
36190     update : function()
36191     {
36192         this.imageEl.attr('src', this.files[this.indicator - 1]);
36193         
36194         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36195         
36196         this.prevIndicator.show();
36197         
36198         if(this.indicator == 1){
36199             this.prevIndicator.hide();
36200         }
36201         
36202         this.nextIndicator.show();
36203         
36204         if(this.indicator == this.files.length){
36205             this.nextIndicator.hide();
36206         }
36207         
36208         this.thumbEl.scrollTo('top');
36209         
36210         this.fireEvent('update', this);
36211     },
36212     
36213     onClick : function(e)
36214     {
36215         e.preventDefault();
36216         
36217         this.fireEvent('click', this);
36218     },
36219     
36220     prev : function(e)
36221     {
36222         e.preventDefault();
36223         
36224         this.indicator = Math.max(1, this.indicator - 1);
36225         
36226         this.update();
36227     },
36228     
36229     next : function(e)
36230     {
36231         e.preventDefault();
36232         
36233         this.indicator = Math.min(this.files.length, this.indicator + 1);
36234         
36235         this.update();
36236     }
36237 });
36238 /*
36239  * - LGPL
36240  *
36241  * RadioSet
36242  *
36243  *
36244  */
36245
36246 /**
36247  * @class Roo.bootstrap.RadioSet
36248  * @extends Roo.bootstrap.Input
36249  * Bootstrap RadioSet class
36250  * @cfg {String} indicatorpos (left|right) default left
36251  * @cfg {Boolean} inline (true|false) inline the element (default true)
36252  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36253  * @constructor
36254  * Create a new RadioSet
36255  * @param {Object} config The config object
36256  */
36257
36258 Roo.bootstrap.RadioSet = function(config){
36259     
36260     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36261     
36262     this.radioes = [];
36263     
36264     Roo.bootstrap.RadioSet.register(this);
36265     
36266     this.addEvents({
36267         /**
36268         * @event check
36269         * Fires when the element is checked or unchecked.
36270         * @param {Roo.bootstrap.RadioSet} this This radio
36271         * @param {Roo.bootstrap.Radio} item The checked item
36272         */
36273        check : true,
36274        /**
36275         * @event click
36276         * Fires when the element is click.
36277         * @param {Roo.bootstrap.RadioSet} this This radio set
36278         * @param {Roo.bootstrap.Radio} item The checked item
36279         * @param {Roo.EventObject} e The event object
36280         */
36281        click : true
36282     });
36283     
36284 };
36285
36286 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36287
36288     radioes : false,
36289     
36290     inline : true,
36291     
36292     weight : '',
36293     
36294     indicatorpos : 'left',
36295     
36296     getAutoCreate : function()
36297     {
36298         var label = {
36299             tag : 'label',
36300             cls : 'roo-radio-set-label',
36301             cn : [
36302                 {
36303                     tag : 'span',
36304                     html : this.fieldLabel
36305                 }
36306             ]
36307         };
36308         if (Roo.bootstrap.version == 3) {
36309             
36310             
36311             if(this.indicatorpos == 'left'){
36312                 label.cn.unshift({
36313                     tag : 'i',
36314                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36315                     tooltip : 'This field is required'
36316                 });
36317             } else {
36318                 label.cn.push({
36319                     tag : 'i',
36320                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36321                     tooltip : 'This field is required'
36322                 });
36323             }
36324         }
36325         var items = {
36326             tag : 'div',
36327             cls : 'roo-radio-set-items'
36328         };
36329         
36330         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36331         
36332         if (align === 'left' && this.fieldLabel.length) {
36333             
36334             items = {
36335                 cls : "roo-radio-set-right", 
36336                 cn: [
36337                     items
36338                 ]
36339             };
36340             
36341             if(this.labelWidth > 12){
36342                 label.style = "width: " + this.labelWidth + 'px';
36343             }
36344             
36345             if(this.labelWidth < 13 && this.labelmd == 0){
36346                 this.labelmd = this.labelWidth;
36347             }
36348             
36349             if(this.labellg > 0){
36350                 label.cls += ' col-lg-' + this.labellg;
36351                 items.cls += ' col-lg-' + (12 - this.labellg);
36352             }
36353             
36354             if(this.labelmd > 0){
36355                 label.cls += ' col-md-' + this.labelmd;
36356                 items.cls += ' col-md-' + (12 - this.labelmd);
36357             }
36358             
36359             if(this.labelsm > 0){
36360                 label.cls += ' col-sm-' + this.labelsm;
36361                 items.cls += ' col-sm-' + (12 - this.labelsm);
36362             }
36363             
36364             if(this.labelxs > 0){
36365                 label.cls += ' col-xs-' + this.labelxs;
36366                 items.cls += ' col-xs-' + (12 - this.labelxs);
36367             }
36368         }
36369         
36370         var cfg = {
36371             tag : 'div',
36372             cls : 'roo-radio-set',
36373             cn : [
36374                 {
36375                     tag : 'input',
36376                     cls : 'roo-radio-set-input',
36377                     type : 'hidden',
36378                     name : this.name,
36379                     value : this.value ? this.value :  ''
36380                 },
36381                 label,
36382                 items
36383             ]
36384         };
36385         
36386         if(this.weight.length){
36387             cfg.cls += ' roo-radio-' + this.weight;
36388         }
36389         
36390         if(this.inline) {
36391             cfg.cls += ' roo-radio-set-inline';
36392         }
36393         
36394         var settings=this;
36395         ['xs','sm','md','lg'].map(function(size){
36396             if (settings[size]) {
36397                 cfg.cls += ' col-' + size + '-' + settings[size];
36398             }
36399         });
36400         
36401         return cfg;
36402         
36403     },
36404
36405     initEvents : function()
36406     {
36407         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36408         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36409         
36410         if(!this.fieldLabel.length){
36411             this.labelEl.hide();
36412         }
36413         
36414         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36415         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36416         
36417         this.indicator = this.indicatorEl();
36418         
36419         if(this.indicator){
36420             this.indicator.addClass('invisible');
36421         }
36422         
36423         this.originalValue = this.getValue();
36424         
36425     },
36426     
36427     inputEl: function ()
36428     {
36429         return this.el.select('.roo-radio-set-input', true).first();
36430     },
36431     
36432     getChildContainer : function()
36433     {
36434         return this.itemsEl;
36435     },
36436     
36437     register : function(item)
36438     {
36439         this.radioes.push(item);
36440         
36441     },
36442     
36443     validate : function()
36444     {   
36445         if(this.getVisibilityEl().hasClass('hidden')){
36446             return true;
36447         }
36448         
36449         var valid = false;
36450         
36451         Roo.each(this.radioes, function(i){
36452             if(!i.checked){
36453                 return;
36454             }
36455             
36456             valid = true;
36457             return false;
36458         });
36459         
36460         if(this.allowBlank) {
36461             return true;
36462         }
36463         
36464         if(this.disabled || valid){
36465             this.markValid();
36466             return true;
36467         }
36468         
36469         this.markInvalid();
36470         return false;
36471         
36472     },
36473     
36474     markValid : function()
36475     {
36476         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36477             this.indicatorEl().removeClass('visible');
36478             this.indicatorEl().addClass('invisible');
36479         }
36480         
36481         
36482         if (Roo.bootstrap.version == 3) {
36483             this.el.removeClass([this.invalidClass, this.validClass]);
36484             this.el.addClass(this.validClass);
36485         } else {
36486             this.el.removeClass(['is-invalid','is-valid']);
36487             this.el.addClass(['is-valid']);
36488         }
36489         this.fireEvent('valid', this);
36490     },
36491     
36492     markInvalid : function(msg)
36493     {
36494         if(this.allowBlank || this.disabled){
36495             return;
36496         }
36497         
36498         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36499             this.indicatorEl().removeClass('invisible');
36500             this.indicatorEl().addClass('visible');
36501         }
36502         if (Roo.bootstrap.version == 3) {
36503             this.el.removeClass([this.invalidClass, this.validClass]);
36504             this.el.addClass(this.invalidClass);
36505         } else {
36506             this.el.removeClass(['is-invalid','is-valid']);
36507             this.el.addClass(['is-invalid']);
36508         }
36509         
36510         this.fireEvent('invalid', this, msg);
36511         
36512     },
36513     
36514     setValue : function(v, suppressEvent)
36515     {   
36516         if(this.value === v){
36517             return;
36518         }
36519         
36520         this.value = v;
36521         
36522         if(this.rendered){
36523             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36524         }
36525         
36526         Roo.each(this.radioes, function(i){
36527             i.checked = false;
36528             i.el.removeClass('checked');
36529         });
36530         
36531         Roo.each(this.radioes, function(i){
36532             
36533             if(i.value === v || i.value.toString() === v.toString()){
36534                 i.checked = true;
36535                 i.el.addClass('checked');
36536                 
36537                 if(suppressEvent !== true){
36538                     this.fireEvent('check', this, i);
36539                 }
36540                 
36541                 return false;
36542             }
36543             
36544         }, this);
36545         
36546         this.validate();
36547     },
36548     
36549     clearInvalid : function(){
36550         
36551         if(!this.el || this.preventMark){
36552             return;
36553         }
36554         
36555         this.el.removeClass([this.invalidClass]);
36556         
36557         this.fireEvent('valid', this);
36558     }
36559     
36560 });
36561
36562 Roo.apply(Roo.bootstrap.RadioSet, {
36563     
36564     groups: {},
36565     
36566     register : function(set)
36567     {
36568         this.groups[set.name] = set;
36569     },
36570     
36571     get: function(name) 
36572     {
36573         if (typeof(this.groups[name]) == 'undefined') {
36574             return false;
36575         }
36576         
36577         return this.groups[name] ;
36578     }
36579     
36580 });
36581 /*
36582  * Based on:
36583  * Ext JS Library 1.1.1
36584  * Copyright(c) 2006-2007, Ext JS, LLC.
36585  *
36586  * Originally Released Under LGPL - original licence link has changed is not relivant.
36587  *
36588  * Fork - LGPL
36589  * <script type="text/javascript">
36590  */
36591
36592
36593 /**
36594  * @class Roo.bootstrap.SplitBar
36595  * @extends Roo.util.Observable
36596  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36597  * <br><br>
36598  * Usage:
36599  * <pre><code>
36600 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36601                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36602 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36603 split.minSize = 100;
36604 split.maxSize = 600;
36605 split.animate = true;
36606 split.on('moved', splitterMoved);
36607 </code></pre>
36608  * @constructor
36609  * Create a new SplitBar
36610  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36611  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36612  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36613  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36614                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36615                         position of the SplitBar).
36616  */
36617 Roo.bootstrap.SplitBar = function(cfg){
36618     
36619     /** @private */
36620     
36621     //{
36622     //  dragElement : elm
36623     //  resizingElement: el,
36624         // optional..
36625     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36626     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36627         // existingProxy ???
36628     //}
36629     
36630     this.el = Roo.get(cfg.dragElement, true);
36631     this.el.dom.unselectable = "on";
36632     /** @private */
36633     this.resizingEl = Roo.get(cfg.resizingElement, true);
36634
36635     /**
36636      * @private
36637      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36638      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36639      * @type Number
36640      */
36641     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36642     
36643     /**
36644      * The minimum size of the resizing element. (Defaults to 0)
36645      * @type Number
36646      */
36647     this.minSize = 0;
36648     
36649     /**
36650      * The maximum size of the resizing element. (Defaults to 2000)
36651      * @type Number
36652      */
36653     this.maxSize = 2000;
36654     
36655     /**
36656      * Whether to animate the transition to the new size
36657      * @type Boolean
36658      */
36659     this.animate = false;
36660     
36661     /**
36662      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36663      * @type Boolean
36664      */
36665     this.useShim = false;
36666     
36667     /** @private */
36668     this.shim = null;
36669     
36670     if(!cfg.existingProxy){
36671         /** @private */
36672         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36673     }else{
36674         this.proxy = Roo.get(cfg.existingProxy).dom;
36675     }
36676     /** @private */
36677     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36678     
36679     /** @private */
36680     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36681     
36682     /** @private */
36683     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36684     
36685     /** @private */
36686     this.dragSpecs = {};
36687     
36688     /**
36689      * @private The adapter to use to positon and resize elements
36690      */
36691     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36692     this.adapter.init(this);
36693     
36694     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36695         /** @private */
36696         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36697         this.el.addClass("roo-splitbar-h");
36698     }else{
36699         /** @private */
36700         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36701         this.el.addClass("roo-splitbar-v");
36702     }
36703     
36704     this.addEvents({
36705         /**
36706          * @event resize
36707          * Fires when the splitter is moved (alias for {@link #event-moved})
36708          * @param {Roo.bootstrap.SplitBar} this
36709          * @param {Number} newSize the new width or height
36710          */
36711         "resize" : true,
36712         /**
36713          * @event moved
36714          * Fires when the splitter is moved
36715          * @param {Roo.bootstrap.SplitBar} this
36716          * @param {Number} newSize the new width or height
36717          */
36718         "moved" : true,
36719         /**
36720          * @event beforeresize
36721          * Fires before the splitter is dragged
36722          * @param {Roo.bootstrap.SplitBar} this
36723          */
36724         "beforeresize" : true,
36725
36726         "beforeapply" : true
36727     });
36728
36729     Roo.util.Observable.call(this);
36730 };
36731
36732 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36733     onStartProxyDrag : function(x, y){
36734         this.fireEvent("beforeresize", this);
36735         if(!this.overlay){
36736             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36737             o.unselectable();
36738             o.enableDisplayMode("block");
36739             // all splitbars share the same overlay
36740             Roo.bootstrap.SplitBar.prototype.overlay = o;
36741         }
36742         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36743         this.overlay.show();
36744         Roo.get(this.proxy).setDisplayed("block");
36745         var size = this.adapter.getElementSize(this);
36746         this.activeMinSize = this.getMinimumSize();;
36747         this.activeMaxSize = this.getMaximumSize();;
36748         var c1 = size - this.activeMinSize;
36749         var c2 = Math.max(this.activeMaxSize - size, 0);
36750         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36751             this.dd.resetConstraints();
36752             this.dd.setXConstraint(
36753                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36754                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36755             );
36756             this.dd.setYConstraint(0, 0);
36757         }else{
36758             this.dd.resetConstraints();
36759             this.dd.setXConstraint(0, 0);
36760             this.dd.setYConstraint(
36761                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36762                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36763             );
36764          }
36765         this.dragSpecs.startSize = size;
36766         this.dragSpecs.startPoint = [x, y];
36767         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36768     },
36769     
36770     /** 
36771      * @private Called after the drag operation by the DDProxy
36772      */
36773     onEndProxyDrag : function(e){
36774         Roo.get(this.proxy).setDisplayed(false);
36775         var endPoint = Roo.lib.Event.getXY(e);
36776         if(this.overlay){
36777             this.overlay.hide();
36778         }
36779         var newSize;
36780         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36781             newSize = this.dragSpecs.startSize + 
36782                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36783                     endPoint[0] - this.dragSpecs.startPoint[0] :
36784                     this.dragSpecs.startPoint[0] - endPoint[0]
36785                 );
36786         }else{
36787             newSize = this.dragSpecs.startSize + 
36788                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36789                     endPoint[1] - this.dragSpecs.startPoint[1] :
36790                     this.dragSpecs.startPoint[1] - endPoint[1]
36791                 );
36792         }
36793         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36794         if(newSize != this.dragSpecs.startSize){
36795             if(this.fireEvent('beforeapply', this, newSize) !== false){
36796                 this.adapter.setElementSize(this, newSize);
36797                 this.fireEvent("moved", this, newSize);
36798                 this.fireEvent("resize", this, newSize);
36799             }
36800         }
36801     },
36802     
36803     /**
36804      * Get the adapter this SplitBar uses
36805      * @return The adapter object
36806      */
36807     getAdapter : function(){
36808         return this.adapter;
36809     },
36810     
36811     /**
36812      * Set the adapter this SplitBar uses
36813      * @param {Object} adapter A SplitBar adapter object
36814      */
36815     setAdapter : function(adapter){
36816         this.adapter = adapter;
36817         this.adapter.init(this);
36818     },
36819     
36820     /**
36821      * Gets the minimum size for the resizing element
36822      * @return {Number} The minimum size
36823      */
36824     getMinimumSize : function(){
36825         return this.minSize;
36826     },
36827     
36828     /**
36829      * Sets the minimum size for the resizing element
36830      * @param {Number} minSize The minimum size
36831      */
36832     setMinimumSize : function(minSize){
36833         this.minSize = minSize;
36834     },
36835     
36836     /**
36837      * Gets the maximum size for the resizing element
36838      * @return {Number} The maximum size
36839      */
36840     getMaximumSize : function(){
36841         return this.maxSize;
36842     },
36843     
36844     /**
36845      * Sets the maximum size for the resizing element
36846      * @param {Number} maxSize The maximum size
36847      */
36848     setMaximumSize : function(maxSize){
36849         this.maxSize = maxSize;
36850     },
36851     
36852     /**
36853      * Sets the initialize size for the resizing element
36854      * @param {Number} size The initial size
36855      */
36856     setCurrentSize : function(size){
36857         var oldAnimate = this.animate;
36858         this.animate = false;
36859         this.adapter.setElementSize(this, size);
36860         this.animate = oldAnimate;
36861     },
36862     
36863     /**
36864      * Destroy this splitbar. 
36865      * @param {Boolean} removeEl True to remove the element
36866      */
36867     destroy : function(removeEl){
36868         if(this.shim){
36869             this.shim.remove();
36870         }
36871         this.dd.unreg();
36872         this.proxy.parentNode.removeChild(this.proxy);
36873         if(removeEl){
36874             this.el.remove();
36875         }
36876     }
36877 });
36878
36879 /**
36880  * @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.
36881  */
36882 Roo.bootstrap.SplitBar.createProxy = function(dir){
36883     var proxy = new Roo.Element(document.createElement("div"));
36884     proxy.unselectable();
36885     var cls = 'roo-splitbar-proxy';
36886     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36887     document.body.appendChild(proxy.dom);
36888     return proxy.dom;
36889 };
36890
36891 /** 
36892  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36893  * Default Adapter. It assumes the splitter and resizing element are not positioned
36894  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36895  */
36896 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36897 };
36898
36899 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36900     // do nothing for now
36901     init : function(s){
36902     
36903     },
36904     /**
36905      * Called before drag operations to get the current size of the resizing element. 
36906      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36907      */
36908      getElementSize : function(s){
36909         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36910             return s.resizingEl.getWidth();
36911         }else{
36912             return s.resizingEl.getHeight();
36913         }
36914     },
36915     
36916     /**
36917      * Called after drag operations to set the size of the resizing element.
36918      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36919      * @param {Number} newSize The new size to set
36920      * @param {Function} onComplete A function to be invoked when resizing is complete
36921      */
36922     setElementSize : function(s, newSize, onComplete){
36923         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36924             if(!s.animate){
36925                 s.resizingEl.setWidth(newSize);
36926                 if(onComplete){
36927                     onComplete(s, newSize);
36928                 }
36929             }else{
36930                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36931             }
36932         }else{
36933             
36934             if(!s.animate){
36935                 s.resizingEl.setHeight(newSize);
36936                 if(onComplete){
36937                     onComplete(s, newSize);
36938                 }
36939             }else{
36940                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36941             }
36942         }
36943     }
36944 };
36945
36946 /** 
36947  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36948  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36949  * Adapter that  moves the splitter element to align with the resized sizing element. 
36950  * Used with an absolute positioned SplitBar.
36951  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36952  * document.body, make sure you assign an id to the body element.
36953  */
36954 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36955     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36956     this.container = Roo.get(container);
36957 };
36958
36959 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36960     init : function(s){
36961         this.basic.init(s);
36962     },
36963     
36964     getElementSize : function(s){
36965         return this.basic.getElementSize(s);
36966     },
36967     
36968     setElementSize : function(s, newSize, onComplete){
36969         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36970     },
36971     
36972     moveSplitter : function(s){
36973         var yes = Roo.bootstrap.SplitBar;
36974         switch(s.placement){
36975             case yes.LEFT:
36976                 s.el.setX(s.resizingEl.getRight());
36977                 break;
36978             case yes.RIGHT:
36979                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36980                 break;
36981             case yes.TOP:
36982                 s.el.setY(s.resizingEl.getBottom());
36983                 break;
36984             case yes.BOTTOM:
36985                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36986                 break;
36987         }
36988     }
36989 };
36990
36991 /**
36992  * Orientation constant - Create a vertical SplitBar
36993  * @static
36994  * @type Number
36995  */
36996 Roo.bootstrap.SplitBar.VERTICAL = 1;
36997
36998 /**
36999  * Orientation constant - Create a horizontal SplitBar
37000  * @static
37001  * @type Number
37002  */
37003 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37004
37005 /**
37006  * Placement constant - The resizing element is to the left of the splitter element
37007  * @static
37008  * @type Number
37009  */
37010 Roo.bootstrap.SplitBar.LEFT = 1;
37011
37012 /**
37013  * Placement constant - The resizing element is to the right of the splitter element
37014  * @static
37015  * @type Number
37016  */
37017 Roo.bootstrap.SplitBar.RIGHT = 2;
37018
37019 /**
37020  * Placement constant - The resizing element is positioned above the splitter element
37021  * @static
37022  * @type Number
37023  */
37024 Roo.bootstrap.SplitBar.TOP = 3;
37025
37026 /**
37027  * Placement constant - The resizing element is positioned under splitter element
37028  * @static
37029  * @type Number
37030  */
37031 Roo.bootstrap.SplitBar.BOTTOM = 4;
37032 Roo.namespace("Roo.bootstrap.layout");/*
37033  * Based on:
37034  * Ext JS Library 1.1.1
37035  * Copyright(c) 2006-2007, Ext JS, LLC.
37036  *
37037  * Originally Released Under LGPL - original licence link has changed is not relivant.
37038  *
37039  * Fork - LGPL
37040  * <script type="text/javascript">
37041  */
37042
37043 /**
37044  * @class Roo.bootstrap.layout.Manager
37045  * @extends Roo.bootstrap.Component
37046  * Base class for layout managers.
37047  */
37048 Roo.bootstrap.layout.Manager = function(config)
37049 {
37050     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37051
37052
37053
37054
37055
37056     /** false to disable window resize monitoring @type Boolean */
37057     this.monitorWindowResize = true;
37058     this.regions = {};
37059     this.addEvents({
37060         /**
37061          * @event layout
37062          * Fires when a layout is performed.
37063          * @param {Roo.LayoutManager} this
37064          */
37065         "layout" : true,
37066         /**
37067          * @event regionresized
37068          * Fires when the user resizes a region.
37069          * @param {Roo.LayoutRegion} region The resized region
37070          * @param {Number} newSize The new size (width for east/west, height for north/south)
37071          */
37072         "regionresized" : true,
37073         /**
37074          * @event regioncollapsed
37075          * Fires when a region is collapsed.
37076          * @param {Roo.LayoutRegion} region The collapsed region
37077          */
37078         "regioncollapsed" : true,
37079         /**
37080          * @event regionexpanded
37081          * Fires when a region is expanded.
37082          * @param {Roo.LayoutRegion} region The expanded region
37083          */
37084         "regionexpanded" : true
37085     });
37086     this.updating = false;
37087
37088     if (config.el) {
37089         this.el = Roo.get(config.el);
37090         this.initEvents();
37091     }
37092
37093 };
37094
37095 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37096
37097
37098     regions : null,
37099
37100     monitorWindowResize : true,
37101
37102
37103     updating : false,
37104
37105
37106     onRender : function(ct, position)
37107     {
37108         if(!this.el){
37109             this.el = Roo.get(ct);
37110             this.initEvents();
37111         }
37112         //this.fireEvent('render',this);
37113     },
37114
37115
37116     initEvents: function()
37117     {
37118
37119
37120         // ie scrollbar fix
37121         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37122             document.body.scroll = "no";
37123         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37124             this.el.position('relative');
37125         }
37126         this.id = this.el.id;
37127         this.el.addClass("roo-layout-container");
37128         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37129         if(this.el.dom != document.body ) {
37130             this.el.on('resize', this.layout,this);
37131             this.el.on('show', this.layout,this);
37132         }
37133
37134     },
37135
37136     /**
37137      * Returns true if this layout is currently being updated
37138      * @return {Boolean}
37139      */
37140     isUpdating : function(){
37141         return this.updating;
37142     },
37143
37144     /**
37145      * Suspend the LayoutManager from doing auto-layouts while
37146      * making multiple add or remove calls
37147      */
37148     beginUpdate : function(){
37149         this.updating = true;
37150     },
37151
37152     /**
37153      * Restore auto-layouts and optionally disable the manager from performing a layout
37154      * @param {Boolean} noLayout true to disable a layout update
37155      */
37156     endUpdate : function(noLayout){
37157         this.updating = false;
37158         if(!noLayout){
37159             this.layout();
37160         }
37161     },
37162
37163     layout: function(){
37164         // abstract...
37165     },
37166
37167     onRegionResized : function(region, newSize){
37168         this.fireEvent("regionresized", region, newSize);
37169         this.layout();
37170     },
37171
37172     onRegionCollapsed : function(region){
37173         this.fireEvent("regioncollapsed", region);
37174     },
37175
37176     onRegionExpanded : function(region){
37177         this.fireEvent("regionexpanded", region);
37178     },
37179
37180     /**
37181      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37182      * performs box-model adjustments.
37183      * @return {Object} The size as an object {width: (the width), height: (the height)}
37184      */
37185     getViewSize : function()
37186     {
37187         var size;
37188         if(this.el.dom != document.body){
37189             size = this.el.getSize();
37190         }else{
37191             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37192         }
37193         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37194         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37195         return size;
37196     },
37197
37198     /**
37199      * Returns the Element this layout is bound to.
37200      * @return {Roo.Element}
37201      */
37202     getEl : function(){
37203         return this.el;
37204     },
37205
37206     /**
37207      * Returns the specified region.
37208      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37209      * @return {Roo.LayoutRegion}
37210      */
37211     getRegion : function(target){
37212         return this.regions[target.toLowerCase()];
37213     },
37214
37215     onWindowResize : function(){
37216         if(this.monitorWindowResize){
37217             this.layout();
37218         }
37219     }
37220 });
37221 /*
37222  * Based on:
37223  * Ext JS Library 1.1.1
37224  * Copyright(c) 2006-2007, Ext JS, LLC.
37225  *
37226  * Originally Released Under LGPL - original licence link has changed is not relivant.
37227  *
37228  * Fork - LGPL
37229  * <script type="text/javascript">
37230  */
37231 /**
37232  * @class Roo.bootstrap.layout.Border
37233  * @extends Roo.bootstrap.layout.Manager
37234  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37235  * please see: examples/bootstrap/nested.html<br><br>
37236  
37237 <b>The container the layout is rendered into can be either the body element or any other element.
37238 If it is not the body element, the container needs to either be an absolute positioned element,
37239 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37240 the container size if it is not the body element.</b>
37241
37242 * @constructor
37243 * Create a new Border
37244 * @param {Object} config Configuration options
37245  */
37246 Roo.bootstrap.layout.Border = function(config){
37247     config = config || {};
37248     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37249     
37250     
37251     
37252     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37253         if(config[region]){
37254             config[region].region = region;
37255             this.addRegion(config[region]);
37256         }
37257     },this);
37258     
37259 };
37260
37261 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37262
37263 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37264     
37265     parent : false, // this might point to a 'nest' or a ???
37266     
37267     /**
37268      * Creates and adds a new region if it doesn't already exist.
37269      * @param {String} target The target region key (north, south, east, west or center).
37270      * @param {Object} config The regions config object
37271      * @return {BorderLayoutRegion} The new region
37272      */
37273     addRegion : function(config)
37274     {
37275         if(!this.regions[config.region]){
37276             var r = this.factory(config);
37277             this.bindRegion(r);
37278         }
37279         return this.regions[config.region];
37280     },
37281
37282     // private (kinda)
37283     bindRegion : function(r){
37284         this.regions[r.config.region] = r;
37285         
37286         r.on("visibilitychange",    this.layout, this);
37287         r.on("paneladded",          this.layout, this);
37288         r.on("panelremoved",        this.layout, this);
37289         r.on("invalidated",         this.layout, this);
37290         r.on("resized",             this.onRegionResized, this);
37291         r.on("collapsed",           this.onRegionCollapsed, this);
37292         r.on("expanded",            this.onRegionExpanded, this);
37293     },
37294
37295     /**
37296      * Performs a layout update.
37297      */
37298     layout : function()
37299     {
37300         if(this.updating) {
37301             return;
37302         }
37303         
37304         // render all the rebions if they have not been done alreayd?
37305         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37306             if(this.regions[region] && !this.regions[region].bodyEl){
37307                 this.regions[region].onRender(this.el)
37308             }
37309         },this);
37310         
37311         var size = this.getViewSize();
37312         var w = size.width;
37313         var h = size.height;
37314         var centerW = w;
37315         var centerH = h;
37316         var centerY = 0;
37317         var centerX = 0;
37318         //var x = 0, y = 0;
37319
37320         var rs = this.regions;
37321         var north = rs["north"];
37322         var south = rs["south"]; 
37323         var west = rs["west"];
37324         var east = rs["east"];
37325         var center = rs["center"];
37326         //if(this.hideOnLayout){ // not supported anymore
37327             //c.el.setStyle("display", "none");
37328         //}
37329         if(north && north.isVisible()){
37330             var b = north.getBox();
37331             var m = north.getMargins();
37332             b.width = w - (m.left+m.right);
37333             b.x = m.left;
37334             b.y = m.top;
37335             centerY = b.height + b.y + m.bottom;
37336             centerH -= centerY;
37337             north.updateBox(this.safeBox(b));
37338         }
37339         if(south && south.isVisible()){
37340             var b = south.getBox();
37341             var m = south.getMargins();
37342             b.width = w - (m.left+m.right);
37343             b.x = m.left;
37344             var totalHeight = (b.height + m.top + m.bottom);
37345             b.y = h - totalHeight + m.top;
37346             centerH -= totalHeight;
37347             south.updateBox(this.safeBox(b));
37348         }
37349         if(west && west.isVisible()){
37350             var b = west.getBox();
37351             var m = west.getMargins();
37352             b.height = centerH - (m.top+m.bottom);
37353             b.x = m.left;
37354             b.y = centerY + m.top;
37355             var totalWidth = (b.width + m.left + m.right);
37356             centerX += totalWidth;
37357             centerW -= totalWidth;
37358             west.updateBox(this.safeBox(b));
37359         }
37360         if(east && east.isVisible()){
37361             var b = east.getBox();
37362             var m = east.getMargins();
37363             b.height = centerH - (m.top+m.bottom);
37364             var totalWidth = (b.width + m.left + m.right);
37365             b.x = w - totalWidth + m.left;
37366             b.y = centerY + m.top;
37367             centerW -= totalWidth;
37368             east.updateBox(this.safeBox(b));
37369         }
37370         if(center){
37371             var m = center.getMargins();
37372             var centerBox = {
37373                 x: centerX + m.left,
37374                 y: centerY + m.top,
37375                 width: centerW - (m.left+m.right),
37376                 height: centerH - (m.top+m.bottom)
37377             };
37378             //if(this.hideOnLayout){
37379                 //center.el.setStyle("display", "block");
37380             //}
37381             center.updateBox(this.safeBox(centerBox));
37382         }
37383         this.el.repaint();
37384         this.fireEvent("layout", this);
37385     },
37386
37387     // private
37388     safeBox : function(box){
37389         box.width = Math.max(0, box.width);
37390         box.height = Math.max(0, box.height);
37391         return box;
37392     },
37393
37394     /**
37395      * Adds a ContentPanel (or subclass) to this layout.
37396      * @param {String} target The target region key (north, south, east, west or center).
37397      * @param {Roo.ContentPanel} panel The panel to add
37398      * @return {Roo.ContentPanel} The added panel
37399      */
37400     add : function(target, panel){
37401          
37402         target = target.toLowerCase();
37403         return this.regions[target].add(panel);
37404     },
37405
37406     /**
37407      * Remove a ContentPanel (or subclass) to this layout.
37408      * @param {String} target The target region key (north, south, east, west or center).
37409      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37410      * @return {Roo.ContentPanel} The removed panel
37411      */
37412     remove : function(target, panel){
37413         target = target.toLowerCase();
37414         return this.regions[target].remove(panel);
37415     },
37416
37417     /**
37418      * Searches all regions for a panel with the specified id
37419      * @param {String} panelId
37420      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37421      */
37422     findPanel : function(panelId){
37423         var rs = this.regions;
37424         for(var target in rs){
37425             if(typeof rs[target] != "function"){
37426                 var p = rs[target].getPanel(panelId);
37427                 if(p){
37428                     return p;
37429                 }
37430             }
37431         }
37432         return null;
37433     },
37434
37435     /**
37436      * Searches all regions for a panel with the specified id and activates (shows) it.
37437      * @param {String/ContentPanel} panelId The panels id or the panel itself
37438      * @return {Roo.ContentPanel} The shown panel or null
37439      */
37440     showPanel : function(panelId) {
37441       var rs = this.regions;
37442       for(var target in rs){
37443          var r = rs[target];
37444          if(typeof r != "function"){
37445             if(r.hasPanel(panelId)){
37446                return r.showPanel(panelId);
37447             }
37448          }
37449       }
37450       return null;
37451    },
37452
37453    /**
37454      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37455      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37456      */
37457    /*
37458     restoreState : function(provider){
37459         if(!provider){
37460             provider = Roo.state.Manager;
37461         }
37462         var sm = new Roo.LayoutStateManager();
37463         sm.init(this, provider);
37464     },
37465 */
37466  
37467  
37468     /**
37469      * Adds a xtype elements to the layout.
37470      * <pre><code>
37471
37472 layout.addxtype({
37473        xtype : 'ContentPanel',
37474        region: 'west',
37475        items: [ .... ]
37476    }
37477 );
37478
37479 layout.addxtype({
37480         xtype : 'NestedLayoutPanel',
37481         region: 'west',
37482         layout: {
37483            center: { },
37484            west: { }   
37485         },
37486         items : [ ... list of content panels or nested layout panels.. ]
37487    }
37488 );
37489 </code></pre>
37490      * @param {Object} cfg Xtype definition of item to add.
37491      */
37492     addxtype : function(cfg)
37493     {
37494         // basically accepts a pannel...
37495         // can accept a layout region..!?!?
37496         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37497         
37498         
37499         // theory?  children can only be panels??
37500         
37501         //if (!cfg.xtype.match(/Panel$/)) {
37502         //    return false;
37503         //}
37504         var ret = false;
37505         
37506         if (typeof(cfg.region) == 'undefined') {
37507             Roo.log("Failed to add Panel, region was not set");
37508             Roo.log(cfg);
37509             return false;
37510         }
37511         var region = cfg.region;
37512         delete cfg.region;
37513         
37514           
37515         var xitems = [];
37516         if (cfg.items) {
37517             xitems = cfg.items;
37518             delete cfg.items;
37519         }
37520         var nb = false;
37521         
37522         if ( region == 'center') {
37523             Roo.log("Center: " + cfg.title);
37524         }
37525         
37526         
37527         switch(cfg.xtype) 
37528         {
37529             case 'Content':  // ContentPanel (el, cfg)
37530             case 'Scroll':  // ContentPanel (el, cfg)
37531             case 'View': 
37532                 cfg.autoCreate = cfg.autoCreate || true;
37533                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37534                 //} else {
37535                 //    var el = this.el.createChild();
37536                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37537                 //}
37538                 
37539                 this.add(region, ret);
37540                 break;
37541             
37542             /*
37543             case 'TreePanel': // our new panel!
37544                 cfg.el = this.el.createChild();
37545                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37546                 this.add(region, ret);
37547                 break;
37548             */
37549             
37550             case 'Nest': 
37551                 // create a new Layout (which is  a Border Layout...
37552                 
37553                 var clayout = cfg.layout;
37554                 clayout.el  = this.el.createChild();
37555                 clayout.items   = clayout.items  || [];
37556                 
37557                 delete cfg.layout;
37558                 
37559                 // replace this exitems with the clayout ones..
37560                 xitems = clayout.items;
37561                  
37562                 // force background off if it's in center...
37563                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37564                     cfg.background = false;
37565                 }
37566                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37567                 
37568                 
37569                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37570                 //console.log('adding nested layout panel '  + cfg.toSource());
37571                 this.add(region, ret);
37572                 nb = {}; /// find first...
37573                 break;
37574             
37575             case 'Grid':
37576                 
37577                 // needs grid and region
37578                 
37579                 //var el = this.getRegion(region).el.createChild();
37580                 /*
37581                  *var el = this.el.createChild();
37582                 // create the grid first...
37583                 cfg.grid.container = el;
37584                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37585                 */
37586                 
37587                 if (region == 'center' && this.active ) {
37588                     cfg.background = false;
37589                 }
37590                 
37591                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37592                 
37593                 this.add(region, ret);
37594                 /*
37595                 if (cfg.background) {
37596                     // render grid on panel activation (if panel background)
37597                     ret.on('activate', function(gp) {
37598                         if (!gp.grid.rendered) {
37599                     //        gp.grid.render(el);
37600                         }
37601                     });
37602                 } else {
37603                   //  cfg.grid.render(el);
37604                 }
37605                 */
37606                 break;
37607            
37608            
37609             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37610                 // it was the old xcomponent building that caused this before.
37611                 // espeically if border is the top element in the tree.
37612                 ret = this;
37613                 break; 
37614                 
37615                     
37616                 
37617                 
37618                 
37619             default:
37620                 /*
37621                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37622                     
37623                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37624                     this.add(region, ret);
37625                 } else {
37626                 */
37627                     Roo.log(cfg);
37628                     throw "Can not add '" + cfg.xtype + "' to Border";
37629                     return null;
37630              
37631                                 
37632              
37633         }
37634         this.beginUpdate();
37635         // add children..
37636         var region = '';
37637         var abn = {};
37638         Roo.each(xitems, function(i)  {
37639             region = nb && i.region ? i.region : false;
37640             
37641             var add = ret.addxtype(i);
37642            
37643             if (region) {
37644                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37645                 if (!i.background) {
37646                     abn[region] = nb[region] ;
37647                 }
37648             }
37649             
37650         });
37651         this.endUpdate();
37652
37653         // make the last non-background panel active..
37654         //if (nb) { Roo.log(abn); }
37655         if (nb) {
37656             
37657             for(var r in abn) {
37658                 region = this.getRegion(r);
37659                 if (region) {
37660                     // tried using nb[r], but it does not work..
37661                      
37662                     region.showPanel(abn[r]);
37663                    
37664                 }
37665             }
37666         }
37667         return ret;
37668         
37669     },
37670     
37671     
37672 // private
37673     factory : function(cfg)
37674     {
37675         
37676         var validRegions = Roo.bootstrap.layout.Border.regions;
37677
37678         var target = cfg.region;
37679         cfg.mgr = this;
37680         
37681         var r = Roo.bootstrap.layout;
37682         Roo.log(target);
37683         switch(target){
37684             case "north":
37685                 return new r.North(cfg);
37686             case "south":
37687                 return new r.South(cfg);
37688             case "east":
37689                 return new r.East(cfg);
37690             case "west":
37691                 return new r.West(cfg);
37692             case "center":
37693                 return new r.Center(cfg);
37694         }
37695         throw 'Layout region "'+target+'" not supported.';
37696     }
37697     
37698     
37699 });
37700  /*
37701  * Based on:
37702  * Ext JS Library 1.1.1
37703  * Copyright(c) 2006-2007, Ext JS, LLC.
37704  *
37705  * Originally Released Under LGPL - original licence link has changed is not relivant.
37706  *
37707  * Fork - LGPL
37708  * <script type="text/javascript">
37709  */
37710  
37711 /**
37712  * @class Roo.bootstrap.layout.Basic
37713  * @extends Roo.util.Observable
37714  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37715  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37716  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37717  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37718  * @cfg {string}   region  the region that it inhabits..
37719  * @cfg {bool}   skipConfig skip config?
37720  * 
37721
37722  */
37723 Roo.bootstrap.layout.Basic = function(config){
37724     
37725     this.mgr = config.mgr;
37726     
37727     this.position = config.region;
37728     
37729     var skipConfig = config.skipConfig;
37730     
37731     this.events = {
37732         /**
37733          * @scope Roo.BasicLayoutRegion
37734          */
37735         
37736         /**
37737          * @event beforeremove
37738          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37739          * @param {Roo.LayoutRegion} this
37740          * @param {Roo.ContentPanel} panel The panel
37741          * @param {Object} e The cancel event object
37742          */
37743         "beforeremove" : true,
37744         /**
37745          * @event invalidated
37746          * Fires when the layout for this region is changed.
37747          * @param {Roo.LayoutRegion} this
37748          */
37749         "invalidated" : true,
37750         /**
37751          * @event visibilitychange
37752          * Fires when this region is shown or hidden 
37753          * @param {Roo.LayoutRegion} this
37754          * @param {Boolean} visibility true or false
37755          */
37756         "visibilitychange" : true,
37757         /**
37758          * @event paneladded
37759          * Fires when a panel is added. 
37760          * @param {Roo.LayoutRegion} this
37761          * @param {Roo.ContentPanel} panel The panel
37762          */
37763         "paneladded" : true,
37764         /**
37765          * @event panelremoved
37766          * Fires when a panel is removed. 
37767          * @param {Roo.LayoutRegion} this
37768          * @param {Roo.ContentPanel} panel The panel
37769          */
37770         "panelremoved" : true,
37771         /**
37772          * @event beforecollapse
37773          * Fires when this region before collapse.
37774          * @param {Roo.LayoutRegion} this
37775          */
37776         "beforecollapse" : true,
37777         /**
37778          * @event collapsed
37779          * Fires when this region is collapsed.
37780          * @param {Roo.LayoutRegion} this
37781          */
37782         "collapsed" : true,
37783         /**
37784          * @event expanded
37785          * Fires when this region is expanded.
37786          * @param {Roo.LayoutRegion} this
37787          */
37788         "expanded" : true,
37789         /**
37790          * @event slideshow
37791          * Fires when this region is slid into view.
37792          * @param {Roo.LayoutRegion} this
37793          */
37794         "slideshow" : true,
37795         /**
37796          * @event slidehide
37797          * Fires when this region slides out of view. 
37798          * @param {Roo.LayoutRegion} this
37799          */
37800         "slidehide" : true,
37801         /**
37802          * @event panelactivated
37803          * Fires when a panel is activated. 
37804          * @param {Roo.LayoutRegion} this
37805          * @param {Roo.ContentPanel} panel The activated panel
37806          */
37807         "panelactivated" : true,
37808         /**
37809          * @event resized
37810          * Fires when the user resizes this region. 
37811          * @param {Roo.LayoutRegion} this
37812          * @param {Number} newSize The new size (width for east/west, height for north/south)
37813          */
37814         "resized" : true
37815     };
37816     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37817     this.panels = new Roo.util.MixedCollection();
37818     this.panels.getKey = this.getPanelId.createDelegate(this);
37819     this.box = null;
37820     this.activePanel = null;
37821     // ensure listeners are added...
37822     
37823     if (config.listeners || config.events) {
37824         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37825             listeners : config.listeners || {},
37826             events : config.events || {}
37827         });
37828     }
37829     
37830     if(skipConfig !== true){
37831         this.applyConfig(config);
37832     }
37833 };
37834
37835 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37836 {
37837     getPanelId : function(p){
37838         return p.getId();
37839     },
37840     
37841     applyConfig : function(config){
37842         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37843         this.config = config;
37844         
37845     },
37846     
37847     /**
37848      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37849      * the width, for horizontal (north, south) the height.
37850      * @param {Number} newSize The new width or height
37851      */
37852     resizeTo : function(newSize){
37853         var el = this.el ? this.el :
37854                  (this.activePanel ? this.activePanel.getEl() : null);
37855         if(el){
37856             switch(this.position){
37857                 case "east":
37858                 case "west":
37859                     el.setWidth(newSize);
37860                     this.fireEvent("resized", this, newSize);
37861                 break;
37862                 case "north":
37863                 case "south":
37864                     el.setHeight(newSize);
37865                     this.fireEvent("resized", this, newSize);
37866                 break;                
37867             }
37868         }
37869     },
37870     
37871     getBox : function(){
37872         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37873     },
37874     
37875     getMargins : function(){
37876         return this.margins;
37877     },
37878     
37879     updateBox : function(box){
37880         this.box = box;
37881         var el = this.activePanel.getEl();
37882         el.dom.style.left = box.x + "px";
37883         el.dom.style.top = box.y + "px";
37884         this.activePanel.setSize(box.width, box.height);
37885     },
37886     
37887     /**
37888      * Returns the container element for this region.
37889      * @return {Roo.Element}
37890      */
37891     getEl : function(){
37892         return this.activePanel;
37893     },
37894     
37895     /**
37896      * Returns true if this region is currently visible.
37897      * @return {Boolean}
37898      */
37899     isVisible : function(){
37900         return this.activePanel ? true : false;
37901     },
37902     
37903     setActivePanel : function(panel){
37904         panel = this.getPanel(panel);
37905         if(this.activePanel && this.activePanel != panel){
37906             this.activePanel.setActiveState(false);
37907             this.activePanel.getEl().setLeftTop(-10000,-10000);
37908         }
37909         this.activePanel = panel;
37910         panel.setActiveState(true);
37911         if(this.box){
37912             panel.setSize(this.box.width, this.box.height);
37913         }
37914         this.fireEvent("panelactivated", this, panel);
37915         this.fireEvent("invalidated");
37916     },
37917     
37918     /**
37919      * Show the specified panel.
37920      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37921      * @return {Roo.ContentPanel} The shown panel or null
37922      */
37923     showPanel : function(panel){
37924         panel = this.getPanel(panel);
37925         if(panel){
37926             this.setActivePanel(panel);
37927         }
37928         return panel;
37929     },
37930     
37931     /**
37932      * Get the active panel for this region.
37933      * @return {Roo.ContentPanel} The active panel or null
37934      */
37935     getActivePanel : function(){
37936         return this.activePanel;
37937     },
37938     
37939     /**
37940      * Add the passed ContentPanel(s)
37941      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37942      * @return {Roo.ContentPanel} The panel added (if only one was added)
37943      */
37944     add : function(panel){
37945         if(arguments.length > 1){
37946             for(var i = 0, len = arguments.length; i < len; i++) {
37947                 this.add(arguments[i]);
37948             }
37949             return null;
37950         }
37951         if(this.hasPanel(panel)){
37952             this.showPanel(panel);
37953             return panel;
37954         }
37955         var el = panel.getEl();
37956         if(el.dom.parentNode != this.mgr.el.dom){
37957             this.mgr.el.dom.appendChild(el.dom);
37958         }
37959         if(panel.setRegion){
37960             panel.setRegion(this);
37961         }
37962         this.panels.add(panel);
37963         el.setStyle("position", "absolute");
37964         if(!panel.background){
37965             this.setActivePanel(panel);
37966             if(this.config.initialSize && this.panels.getCount()==1){
37967                 this.resizeTo(this.config.initialSize);
37968             }
37969         }
37970         this.fireEvent("paneladded", this, panel);
37971         return panel;
37972     },
37973     
37974     /**
37975      * Returns true if the panel is in this region.
37976      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37977      * @return {Boolean}
37978      */
37979     hasPanel : function(panel){
37980         if(typeof panel == "object"){ // must be panel obj
37981             panel = panel.getId();
37982         }
37983         return this.getPanel(panel) ? true : false;
37984     },
37985     
37986     /**
37987      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37988      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37989      * @param {Boolean} preservePanel Overrides the config preservePanel option
37990      * @return {Roo.ContentPanel} The panel that was removed
37991      */
37992     remove : function(panel, preservePanel){
37993         panel = this.getPanel(panel);
37994         if(!panel){
37995             return null;
37996         }
37997         var e = {};
37998         this.fireEvent("beforeremove", this, panel, e);
37999         if(e.cancel === true){
38000             return null;
38001         }
38002         var panelId = panel.getId();
38003         this.panels.removeKey(panelId);
38004         return panel;
38005     },
38006     
38007     /**
38008      * Returns the panel specified or null if it's not in this region.
38009      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38010      * @return {Roo.ContentPanel}
38011      */
38012     getPanel : function(id){
38013         if(typeof id == "object"){ // must be panel obj
38014             return id;
38015         }
38016         return this.panels.get(id);
38017     },
38018     
38019     /**
38020      * Returns this regions position (north/south/east/west/center).
38021      * @return {String} 
38022      */
38023     getPosition: function(){
38024         return this.position;    
38025     }
38026 });/*
38027  * Based on:
38028  * Ext JS Library 1.1.1
38029  * Copyright(c) 2006-2007, Ext JS, LLC.
38030  *
38031  * Originally Released Under LGPL - original licence link has changed is not relivant.
38032  *
38033  * Fork - LGPL
38034  * <script type="text/javascript">
38035  */
38036  
38037 /**
38038  * @class Roo.bootstrap.layout.Region
38039  * @extends Roo.bootstrap.layout.Basic
38040  * This class represents a region in a layout manager.
38041  
38042  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38043  * @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})
38044  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38045  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38046  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38047  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38048  * @cfg {String}    title           The title for the region (overrides panel titles)
38049  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38050  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38051  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38052  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38053  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38054  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38055  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38056  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38057  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38058  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38059
38060  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38061  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38062  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38063  * @cfg {Number}    width           For East/West panels
38064  * @cfg {Number}    height          For North/South panels
38065  * @cfg {Boolean}   split           To show the splitter
38066  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38067  * 
38068  * @cfg {string}   cls             Extra CSS classes to add to region
38069  * 
38070  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38071  * @cfg {string}   region  the region that it inhabits..
38072  *
38073
38074  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38075  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38076
38077  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38078  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38079  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38080  */
38081 Roo.bootstrap.layout.Region = function(config)
38082 {
38083     this.applyConfig(config);
38084
38085     var mgr = config.mgr;
38086     var pos = config.region;
38087     config.skipConfig = true;
38088     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38089     
38090     if (mgr.el) {
38091         this.onRender(mgr.el);   
38092     }
38093      
38094     this.visible = true;
38095     this.collapsed = false;
38096     this.unrendered_panels = [];
38097 };
38098
38099 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38100
38101     position: '', // set by wrapper (eg. north/south etc..)
38102     unrendered_panels : null,  // unrendered panels.
38103     
38104     tabPosition : false,
38105     
38106     mgr: false, // points to 'Border'
38107     
38108     
38109     createBody : function(){
38110         /** This region's body element 
38111         * @type Roo.Element */
38112         this.bodyEl = this.el.createChild({
38113                 tag: "div",
38114                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38115         });
38116     },
38117
38118     onRender: function(ctr, pos)
38119     {
38120         var dh = Roo.DomHelper;
38121         /** This region's container element 
38122         * @type Roo.Element */
38123         this.el = dh.append(ctr.dom, {
38124                 tag: "div",
38125                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38126             }, true);
38127         /** This region's title element 
38128         * @type Roo.Element */
38129     
38130         this.titleEl = dh.append(this.el.dom,  {
38131                 tag: "div",
38132                 unselectable: "on",
38133                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38134                 children:[
38135                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38136                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38137                 ]
38138             }, true);
38139         
38140         this.titleEl.enableDisplayMode();
38141         /** This region's title text element 
38142         * @type HTMLElement */
38143         this.titleTextEl = this.titleEl.dom.firstChild;
38144         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38145         /*
38146         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38147         this.closeBtn.enableDisplayMode();
38148         this.closeBtn.on("click", this.closeClicked, this);
38149         this.closeBtn.hide();
38150     */
38151         this.createBody(this.config);
38152         if(this.config.hideWhenEmpty){
38153             this.hide();
38154             this.on("paneladded", this.validateVisibility, this);
38155             this.on("panelremoved", this.validateVisibility, this);
38156         }
38157         if(this.autoScroll){
38158             this.bodyEl.setStyle("overflow", "auto");
38159         }else{
38160             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38161         }
38162         //if(c.titlebar !== false){
38163             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38164                 this.titleEl.hide();
38165             }else{
38166                 this.titleEl.show();
38167                 if(this.config.title){
38168                     this.titleTextEl.innerHTML = this.config.title;
38169                 }
38170             }
38171         //}
38172         if(this.config.collapsed){
38173             this.collapse(true);
38174         }
38175         if(this.config.hidden){
38176             this.hide();
38177         }
38178         
38179         if (this.unrendered_panels && this.unrendered_panels.length) {
38180             for (var i =0;i< this.unrendered_panels.length; i++) {
38181                 this.add(this.unrendered_panels[i]);
38182             }
38183             this.unrendered_panels = null;
38184             
38185         }
38186         
38187     },
38188     
38189     applyConfig : function(c)
38190     {
38191         /*
38192          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38193             var dh = Roo.DomHelper;
38194             if(c.titlebar !== false){
38195                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38196                 this.collapseBtn.on("click", this.collapse, this);
38197                 this.collapseBtn.enableDisplayMode();
38198                 /*
38199                 if(c.showPin === true || this.showPin){
38200                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38201                     this.stickBtn.enableDisplayMode();
38202                     this.stickBtn.on("click", this.expand, this);
38203                     this.stickBtn.hide();
38204                 }
38205                 
38206             }
38207             */
38208             /** This region's collapsed element
38209             * @type Roo.Element */
38210             /*
38211              *
38212             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38213                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38214             ]}, true);
38215             
38216             if(c.floatable !== false){
38217                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38218                this.collapsedEl.on("click", this.collapseClick, this);
38219             }
38220
38221             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38222                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38223                    id: "message", unselectable: "on", style:{"float":"left"}});
38224                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38225              }
38226             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38227             this.expandBtn.on("click", this.expand, this);
38228             
38229         }
38230         
38231         if(this.collapseBtn){
38232             this.collapseBtn.setVisible(c.collapsible == true);
38233         }
38234         
38235         this.cmargins = c.cmargins || this.cmargins ||
38236                          (this.position == "west" || this.position == "east" ?
38237                              {top: 0, left: 2, right:2, bottom: 0} :
38238                              {top: 2, left: 0, right:0, bottom: 2});
38239         */
38240         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38241         
38242         
38243         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38244         
38245         this.autoScroll = c.autoScroll || false;
38246         
38247         
38248        
38249         
38250         this.duration = c.duration || .30;
38251         this.slideDuration = c.slideDuration || .45;
38252         this.config = c;
38253        
38254     },
38255     /**
38256      * Returns true if this region is currently visible.
38257      * @return {Boolean}
38258      */
38259     isVisible : function(){
38260         return this.visible;
38261     },
38262
38263     /**
38264      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38265      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38266      */
38267     //setCollapsedTitle : function(title){
38268     //    title = title || "&#160;";
38269      //   if(this.collapsedTitleTextEl){
38270       //      this.collapsedTitleTextEl.innerHTML = title;
38271        // }
38272     //},
38273
38274     getBox : function(){
38275         var b;
38276       //  if(!this.collapsed){
38277             b = this.el.getBox(false, true);
38278        // }else{
38279           //  b = this.collapsedEl.getBox(false, true);
38280         //}
38281         return b;
38282     },
38283
38284     getMargins : function(){
38285         return this.margins;
38286         //return this.collapsed ? this.cmargins : this.margins;
38287     },
38288 /*
38289     highlight : function(){
38290         this.el.addClass("x-layout-panel-dragover");
38291     },
38292
38293     unhighlight : function(){
38294         this.el.removeClass("x-layout-panel-dragover");
38295     },
38296 */
38297     updateBox : function(box)
38298     {
38299         if (!this.bodyEl) {
38300             return; // not rendered yet..
38301         }
38302         
38303         this.box = box;
38304         if(!this.collapsed){
38305             this.el.dom.style.left = box.x + "px";
38306             this.el.dom.style.top = box.y + "px";
38307             this.updateBody(box.width, box.height);
38308         }else{
38309             this.collapsedEl.dom.style.left = box.x + "px";
38310             this.collapsedEl.dom.style.top = box.y + "px";
38311             this.collapsedEl.setSize(box.width, box.height);
38312         }
38313         if(this.tabs){
38314             this.tabs.autoSizeTabs();
38315         }
38316     },
38317
38318     updateBody : function(w, h)
38319     {
38320         if(w !== null){
38321             this.el.setWidth(w);
38322             w -= this.el.getBorderWidth("rl");
38323             if(this.config.adjustments){
38324                 w += this.config.adjustments[0];
38325             }
38326         }
38327         if(h !== null && h > 0){
38328             this.el.setHeight(h);
38329             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38330             h -= this.el.getBorderWidth("tb");
38331             if(this.config.adjustments){
38332                 h += this.config.adjustments[1];
38333             }
38334             this.bodyEl.setHeight(h);
38335             if(this.tabs){
38336                 h = this.tabs.syncHeight(h);
38337             }
38338         }
38339         if(this.panelSize){
38340             w = w !== null ? w : this.panelSize.width;
38341             h = h !== null ? h : this.panelSize.height;
38342         }
38343         if(this.activePanel){
38344             var el = this.activePanel.getEl();
38345             w = w !== null ? w : el.getWidth();
38346             h = h !== null ? h : el.getHeight();
38347             this.panelSize = {width: w, height: h};
38348             this.activePanel.setSize(w, h);
38349         }
38350         if(Roo.isIE && this.tabs){
38351             this.tabs.el.repaint();
38352         }
38353     },
38354
38355     /**
38356      * Returns the container element for this region.
38357      * @return {Roo.Element}
38358      */
38359     getEl : function(){
38360         return this.el;
38361     },
38362
38363     /**
38364      * Hides this region.
38365      */
38366     hide : function(){
38367         //if(!this.collapsed){
38368             this.el.dom.style.left = "-2000px";
38369             this.el.hide();
38370         //}else{
38371          //   this.collapsedEl.dom.style.left = "-2000px";
38372          //   this.collapsedEl.hide();
38373        // }
38374         this.visible = false;
38375         this.fireEvent("visibilitychange", this, false);
38376     },
38377
38378     /**
38379      * Shows this region if it was previously hidden.
38380      */
38381     show : function(){
38382         //if(!this.collapsed){
38383             this.el.show();
38384         //}else{
38385         //    this.collapsedEl.show();
38386        // }
38387         this.visible = true;
38388         this.fireEvent("visibilitychange", this, true);
38389     },
38390 /*
38391     closeClicked : function(){
38392         if(this.activePanel){
38393             this.remove(this.activePanel);
38394         }
38395     },
38396
38397     collapseClick : function(e){
38398         if(this.isSlid){
38399            e.stopPropagation();
38400            this.slideIn();
38401         }else{
38402            e.stopPropagation();
38403            this.slideOut();
38404         }
38405     },
38406 */
38407     /**
38408      * Collapses this region.
38409      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38410      */
38411     /*
38412     collapse : function(skipAnim, skipCheck = false){
38413         if(this.collapsed) {
38414             return;
38415         }
38416         
38417         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38418             
38419             this.collapsed = true;
38420             if(this.split){
38421                 this.split.el.hide();
38422             }
38423             if(this.config.animate && skipAnim !== true){
38424                 this.fireEvent("invalidated", this);
38425                 this.animateCollapse();
38426             }else{
38427                 this.el.setLocation(-20000,-20000);
38428                 this.el.hide();
38429                 this.collapsedEl.show();
38430                 this.fireEvent("collapsed", this);
38431                 this.fireEvent("invalidated", this);
38432             }
38433         }
38434         
38435     },
38436 */
38437     animateCollapse : function(){
38438         // overridden
38439     },
38440
38441     /**
38442      * Expands this region if it was previously collapsed.
38443      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38444      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38445      */
38446     /*
38447     expand : function(e, skipAnim){
38448         if(e) {
38449             e.stopPropagation();
38450         }
38451         if(!this.collapsed || this.el.hasActiveFx()) {
38452             return;
38453         }
38454         if(this.isSlid){
38455             this.afterSlideIn();
38456             skipAnim = true;
38457         }
38458         this.collapsed = false;
38459         if(this.config.animate && skipAnim !== true){
38460             this.animateExpand();
38461         }else{
38462             this.el.show();
38463             if(this.split){
38464                 this.split.el.show();
38465             }
38466             this.collapsedEl.setLocation(-2000,-2000);
38467             this.collapsedEl.hide();
38468             this.fireEvent("invalidated", this);
38469             this.fireEvent("expanded", this);
38470         }
38471     },
38472 */
38473     animateExpand : function(){
38474         // overridden
38475     },
38476
38477     initTabs : function()
38478     {
38479         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38480         
38481         var ts = new Roo.bootstrap.panel.Tabs({
38482             el: this.bodyEl.dom,
38483             region : this,
38484             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38485             disableTooltips: this.config.disableTabTips,
38486             toolbar : this.config.toolbar
38487         });
38488         
38489         if(this.config.hideTabs){
38490             ts.stripWrap.setDisplayed(false);
38491         }
38492         this.tabs = ts;
38493         ts.resizeTabs = this.config.resizeTabs === true;
38494         ts.minTabWidth = this.config.minTabWidth || 40;
38495         ts.maxTabWidth = this.config.maxTabWidth || 250;
38496         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38497         ts.monitorResize = false;
38498         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38499         ts.bodyEl.addClass('roo-layout-tabs-body');
38500         this.panels.each(this.initPanelAsTab, this);
38501     },
38502
38503     initPanelAsTab : function(panel){
38504         var ti = this.tabs.addTab(
38505             panel.getEl().id,
38506             panel.getTitle(),
38507             null,
38508             this.config.closeOnTab && panel.isClosable(),
38509             panel.tpl
38510         );
38511         if(panel.tabTip !== undefined){
38512             ti.setTooltip(panel.tabTip);
38513         }
38514         ti.on("activate", function(){
38515               this.setActivePanel(panel);
38516         }, this);
38517         
38518         if(this.config.closeOnTab){
38519             ti.on("beforeclose", function(t, e){
38520                 e.cancel = true;
38521                 this.remove(panel);
38522             }, this);
38523         }
38524         
38525         panel.tabItem = ti;
38526         
38527         return ti;
38528     },
38529
38530     updatePanelTitle : function(panel, title)
38531     {
38532         if(this.activePanel == panel){
38533             this.updateTitle(title);
38534         }
38535         if(this.tabs){
38536             var ti = this.tabs.getTab(panel.getEl().id);
38537             ti.setText(title);
38538             if(panel.tabTip !== undefined){
38539                 ti.setTooltip(panel.tabTip);
38540             }
38541         }
38542     },
38543
38544     updateTitle : function(title){
38545         if(this.titleTextEl && !this.config.title){
38546             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38547         }
38548     },
38549
38550     setActivePanel : function(panel)
38551     {
38552         panel = this.getPanel(panel);
38553         if(this.activePanel && this.activePanel != panel){
38554             if(this.activePanel.setActiveState(false) === false){
38555                 return;
38556             }
38557         }
38558         this.activePanel = panel;
38559         panel.setActiveState(true);
38560         if(this.panelSize){
38561             panel.setSize(this.panelSize.width, this.panelSize.height);
38562         }
38563         if(this.closeBtn){
38564             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38565         }
38566         this.updateTitle(panel.getTitle());
38567         if(this.tabs){
38568             this.fireEvent("invalidated", this);
38569         }
38570         this.fireEvent("panelactivated", this, panel);
38571     },
38572
38573     /**
38574      * Shows the specified panel.
38575      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38576      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38577      */
38578     showPanel : function(panel)
38579     {
38580         panel = this.getPanel(panel);
38581         if(panel){
38582             if(this.tabs){
38583                 var tab = this.tabs.getTab(panel.getEl().id);
38584                 if(tab.isHidden()){
38585                     this.tabs.unhideTab(tab.id);
38586                 }
38587                 tab.activate();
38588             }else{
38589                 this.setActivePanel(panel);
38590             }
38591         }
38592         return panel;
38593     },
38594
38595     /**
38596      * Get the active panel for this region.
38597      * @return {Roo.ContentPanel} The active panel or null
38598      */
38599     getActivePanel : function(){
38600         return this.activePanel;
38601     },
38602
38603     validateVisibility : function(){
38604         if(this.panels.getCount() < 1){
38605             this.updateTitle("&#160;");
38606             this.closeBtn.hide();
38607             this.hide();
38608         }else{
38609             if(!this.isVisible()){
38610                 this.show();
38611             }
38612         }
38613     },
38614
38615     /**
38616      * Adds the passed ContentPanel(s) to this region.
38617      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38618      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38619      */
38620     add : function(panel)
38621     {
38622         if(arguments.length > 1){
38623             for(var i = 0, len = arguments.length; i < len; i++) {
38624                 this.add(arguments[i]);
38625             }
38626             return null;
38627         }
38628         
38629         // if we have not been rendered yet, then we can not really do much of this..
38630         if (!this.bodyEl) {
38631             this.unrendered_panels.push(panel);
38632             return panel;
38633         }
38634         
38635         
38636         
38637         
38638         if(this.hasPanel(panel)){
38639             this.showPanel(panel);
38640             return panel;
38641         }
38642         panel.setRegion(this);
38643         this.panels.add(panel);
38644        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38645             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38646             // and hide them... ???
38647             this.bodyEl.dom.appendChild(panel.getEl().dom);
38648             if(panel.background !== true){
38649                 this.setActivePanel(panel);
38650             }
38651             this.fireEvent("paneladded", this, panel);
38652             return panel;
38653         }
38654         */
38655         if(!this.tabs){
38656             this.initTabs();
38657         }else{
38658             this.initPanelAsTab(panel);
38659         }
38660         
38661         
38662         if(panel.background !== true){
38663             this.tabs.activate(panel.getEl().id);
38664         }
38665         this.fireEvent("paneladded", this, panel);
38666         return panel;
38667     },
38668
38669     /**
38670      * Hides the tab for the specified panel.
38671      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38672      */
38673     hidePanel : function(panel){
38674         if(this.tabs && (panel = this.getPanel(panel))){
38675             this.tabs.hideTab(panel.getEl().id);
38676         }
38677     },
38678
38679     /**
38680      * Unhides the tab for a previously hidden panel.
38681      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38682      */
38683     unhidePanel : function(panel){
38684         if(this.tabs && (panel = this.getPanel(panel))){
38685             this.tabs.unhideTab(panel.getEl().id);
38686         }
38687     },
38688
38689     clearPanels : function(){
38690         while(this.panels.getCount() > 0){
38691              this.remove(this.panels.first());
38692         }
38693     },
38694
38695     /**
38696      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38697      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38698      * @param {Boolean} preservePanel Overrides the config preservePanel option
38699      * @return {Roo.ContentPanel} The panel that was removed
38700      */
38701     remove : function(panel, preservePanel)
38702     {
38703         panel = this.getPanel(panel);
38704         if(!panel){
38705             return null;
38706         }
38707         var e = {};
38708         this.fireEvent("beforeremove", this, panel, e);
38709         if(e.cancel === true){
38710             return null;
38711         }
38712         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38713         var panelId = panel.getId();
38714         this.panels.removeKey(panelId);
38715         if(preservePanel){
38716             document.body.appendChild(panel.getEl().dom);
38717         }
38718         if(this.tabs){
38719             this.tabs.removeTab(panel.getEl().id);
38720         }else if (!preservePanel){
38721             this.bodyEl.dom.removeChild(panel.getEl().dom);
38722         }
38723         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38724             var p = this.panels.first();
38725             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38726             tempEl.appendChild(p.getEl().dom);
38727             this.bodyEl.update("");
38728             this.bodyEl.dom.appendChild(p.getEl().dom);
38729             tempEl = null;
38730             this.updateTitle(p.getTitle());
38731             this.tabs = null;
38732             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38733             this.setActivePanel(p);
38734         }
38735         panel.setRegion(null);
38736         if(this.activePanel == panel){
38737             this.activePanel = null;
38738         }
38739         if(this.config.autoDestroy !== false && preservePanel !== true){
38740             try{panel.destroy();}catch(e){}
38741         }
38742         this.fireEvent("panelremoved", this, panel);
38743         return panel;
38744     },
38745
38746     /**
38747      * Returns the TabPanel component used by this region
38748      * @return {Roo.TabPanel}
38749      */
38750     getTabs : function(){
38751         return this.tabs;
38752     },
38753
38754     createTool : function(parentEl, className){
38755         var btn = Roo.DomHelper.append(parentEl, {
38756             tag: "div",
38757             cls: "x-layout-tools-button",
38758             children: [ {
38759                 tag: "div",
38760                 cls: "roo-layout-tools-button-inner " + className,
38761                 html: "&#160;"
38762             }]
38763         }, true);
38764         btn.addClassOnOver("roo-layout-tools-button-over");
38765         return btn;
38766     }
38767 });/*
38768  * Based on:
38769  * Ext JS Library 1.1.1
38770  * Copyright(c) 2006-2007, Ext JS, LLC.
38771  *
38772  * Originally Released Under LGPL - original licence link has changed is not relivant.
38773  *
38774  * Fork - LGPL
38775  * <script type="text/javascript">
38776  */
38777  
38778
38779
38780 /**
38781  * @class Roo.SplitLayoutRegion
38782  * @extends Roo.LayoutRegion
38783  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38784  */
38785 Roo.bootstrap.layout.Split = function(config){
38786     this.cursor = config.cursor;
38787     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38788 };
38789
38790 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38791 {
38792     splitTip : "Drag to resize.",
38793     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38794     useSplitTips : false,
38795
38796     applyConfig : function(config){
38797         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38798     },
38799     
38800     onRender : function(ctr,pos) {
38801         
38802         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38803         if(!this.config.split){
38804             return;
38805         }
38806         if(!this.split){
38807             
38808             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38809                             tag: "div",
38810                             id: this.el.id + "-split",
38811                             cls: "roo-layout-split roo-layout-split-"+this.position,
38812                             html: "&#160;"
38813             });
38814             /** The SplitBar for this region 
38815             * @type Roo.SplitBar */
38816             // does not exist yet...
38817             Roo.log([this.position, this.orientation]);
38818             
38819             this.split = new Roo.bootstrap.SplitBar({
38820                 dragElement : splitEl,
38821                 resizingElement: this.el,
38822                 orientation : this.orientation
38823             });
38824             
38825             this.split.on("moved", this.onSplitMove, this);
38826             this.split.useShim = this.config.useShim === true;
38827             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38828             if(this.useSplitTips){
38829                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38830             }
38831             //if(config.collapsible){
38832             //    this.split.el.on("dblclick", this.collapse,  this);
38833             //}
38834         }
38835         if(typeof this.config.minSize != "undefined"){
38836             this.split.minSize = this.config.minSize;
38837         }
38838         if(typeof this.config.maxSize != "undefined"){
38839             this.split.maxSize = this.config.maxSize;
38840         }
38841         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38842             this.hideSplitter();
38843         }
38844         
38845     },
38846
38847     getHMaxSize : function(){
38848          var cmax = this.config.maxSize || 10000;
38849          var center = this.mgr.getRegion("center");
38850          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38851     },
38852
38853     getVMaxSize : function(){
38854          var cmax = this.config.maxSize || 10000;
38855          var center = this.mgr.getRegion("center");
38856          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38857     },
38858
38859     onSplitMove : function(split, newSize){
38860         this.fireEvent("resized", this, newSize);
38861     },
38862     
38863     /** 
38864      * Returns the {@link Roo.SplitBar} for this region.
38865      * @return {Roo.SplitBar}
38866      */
38867     getSplitBar : function(){
38868         return this.split;
38869     },
38870     
38871     hide : function(){
38872         this.hideSplitter();
38873         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38874     },
38875
38876     hideSplitter : function(){
38877         if(this.split){
38878             this.split.el.setLocation(-2000,-2000);
38879             this.split.el.hide();
38880         }
38881     },
38882
38883     show : function(){
38884         if(this.split){
38885             this.split.el.show();
38886         }
38887         Roo.bootstrap.layout.Split.superclass.show.call(this);
38888     },
38889     
38890     beforeSlide: function(){
38891         if(Roo.isGecko){// firefox overflow auto bug workaround
38892             this.bodyEl.clip();
38893             if(this.tabs) {
38894                 this.tabs.bodyEl.clip();
38895             }
38896             if(this.activePanel){
38897                 this.activePanel.getEl().clip();
38898                 
38899                 if(this.activePanel.beforeSlide){
38900                     this.activePanel.beforeSlide();
38901                 }
38902             }
38903         }
38904     },
38905     
38906     afterSlide : function(){
38907         if(Roo.isGecko){// firefox overflow auto bug workaround
38908             this.bodyEl.unclip();
38909             if(this.tabs) {
38910                 this.tabs.bodyEl.unclip();
38911             }
38912             if(this.activePanel){
38913                 this.activePanel.getEl().unclip();
38914                 if(this.activePanel.afterSlide){
38915                     this.activePanel.afterSlide();
38916                 }
38917             }
38918         }
38919     },
38920
38921     initAutoHide : function(){
38922         if(this.autoHide !== false){
38923             if(!this.autoHideHd){
38924                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38925                 this.autoHideHd = {
38926                     "mouseout": function(e){
38927                         if(!e.within(this.el, true)){
38928                             st.delay(500);
38929                         }
38930                     },
38931                     "mouseover" : function(e){
38932                         st.cancel();
38933                     },
38934                     scope : this
38935                 };
38936             }
38937             this.el.on(this.autoHideHd);
38938         }
38939     },
38940
38941     clearAutoHide : function(){
38942         if(this.autoHide !== false){
38943             this.el.un("mouseout", this.autoHideHd.mouseout);
38944             this.el.un("mouseover", this.autoHideHd.mouseover);
38945         }
38946     },
38947
38948     clearMonitor : function(){
38949         Roo.get(document).un("click", this.slideInIf, this);
38950     },
38951
38952     // these names are backwards but not changed for compat
38953     slideOut : function(){
38954         if(this.isSlid || this.el.hasActiveFx()){
38955             return;
38956         }
38957         this.isSlid = true;
38958         if(this.collapseBtn){
38959             this.collapseBtn.hide();
38960         }
38961         this.closeBtnState = this.closeBtn.getStyle('display');
38962         this.closeBtn.hide();
38963         if(this.stickBtn){
38964             this.stickBtn.show();
38965         }
38966         this.el.show();
38967         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38968         this.beforeSlide();
38969         this.el.setStyle("z-index", 10001);
38970         this.el.slideIn(this.getSlideAnchor(), {
38971             callback: function(){
38972                 this.afterSlide();
38973                 this.initAutoHide();
38974                 Roo.get(document).on("click", this.slideInIf, this);
38975                 this.fireEvent("slideshow", this);
38976             },
38977             scope: this,
38978             block: true
38979         });
38980     },
38981
38982     afterSlideIn : function(){
38983         this.clearAutoHide();
38984         this.isSlid = false;
38985         this.clearMonitor();
38986         this.el.setStyle("z-index", "");
38987         if(this.collapseBtn){
38988             this.collapseBtn.show();
38989         }
38990         this.closeBtn.setStyle('display', this.closeBtnState);
38991         if(this.stickBtn){
38992             this.stickBtn.hide();
38993         }
38994         this.fireEvent("slidehide", this);
38995     },
38996
38997     slideIn : function(cb){
38998         if(!this.isSlid || this.el.hasActiveFx()){
38999             Roo.callback(cb);
39000             return;
39001         }
39002         this.isSlid = false;
39003         this.beforeSlide();
39004         this.el.slideOut(this.getSlideAnchor(), {
39005             callback: function(){
39006                 this.el.setLeftTop(-10000, -10000);
39007                 this.afterSlide();
39008                 this.afterSlideIn();
39009                 Roo.callback(cb);
39010             },
39011             scope: this,
39012             block: true
39013         });
39014     },
39015     
39016     slideInIf : function(e){
39017         if(!e.within(this.el)){
39018             this.slideIn();
39019         }
39020     },
39021
39022     animateCollapse : function(){
39023         this.beforeSlide();
39024         this.el.setStyle("z-index", 20000);
39025         var anchor = this.getSlideAnchor();
39026         this.el.slideOut(anchor, {
39027             callback : function(){
39028                 this.el.setStyle("z-index", "");
39029                 this.collapsedEl.slideIn(anchor, {duration:.3});
39030                 this.afterSlide();
39031                 this.el.setLocation(-10000,-10000);
39032                 this.el.hide();
39033                 this.fireEvent("collapsed", this);
39034             },
39035             scope: this,
39036             block: true
39037         });
39038     },
39039
39040     animateExpand : function(){
39041         this.beforeSlide();
39042         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39043         this.el.setStyle("z-index", 20000);
39044         this.collapsedEl.hide({
39045             duration:.1
39046         });
39047         this.el.slideIn(this.getSlideAnchor(), {
39048             callback : function(){
39049                 this.el.setStyle("z-index", "");
39050                 this.afterSlide();
39051                 if(this.split){
39052                     this.split.el.show();
39053                 }
39054                 this.fireEvent("invalidated", this);
39055                 this.fireEvent("expanded", this);
39056             },
39057             scope: this,
39058             block: true
39059         });
39060     },
39061
39062     anchors : {
39063         "west" : "left",
39064         "east" : "right",
39065         "north" : "top",
39066         "south" : "bottom"
39067     },
39068
39069     sanchors : {
39070         "west" : "l",
39071         "east" : "r",
39072         "north" : "t",
39073         "south" : "b"
39074     },
39075
39076     canchors : {
39077         "west" : "tl-tr",
39078         "east" : "tr-tl",
39079         "north" : "tl-bl",
39080         "south" : "bl-tl"
39081     },
39082
39083     getAnchor : function(){
39084         return this.anchors[this.position];
39085     },
39086
39087     getCollapseAnchor : function(){
39088         return this.canchors[this.position];
39089     },
39090
39091     getSlideAnchor : function(){
39092         return this.sanchors[this.position];
39093     },
39094
39095     getAlignAdj : function(){
39096         var cm = this.cmargins;
39097         switch(this.position){
39098             case "west":
39099                 return [0, 0];
39100             break;
39101             case "east":
39102                 return [0, 0];
39103             break;
39104             case "north":
39105                 return [0, 0];
39106             break;
39107             case "south":
39108                 return [0, 0];
39109             break;
39110         }
39111     },
39112
39113     getExpandAdj : function(){
39114         var c = this.collapsedEl, cm = this.cmargins;
39115         switch(this.position){
39116             case "west":
39117                 return [-(cm.right+c.getWidth()+cm.left), 0];
39118             break;
39119             case "east":
39120                 return [cm.right+c.getWidth()+cm.left, 0];
39121             break;
39122             case "north":
39123                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39124             break;
39125             case "south":
39126                 return [0, cm.top+cm.bottom+c.getHeight()];
39127             break;
39128         }
39129     }
39130 });/*
39131  * Based on:
39132  * Ext JS Library 1.1.1
39133  * Copyright(c) 2006-2007, Ext JS, LLC.
39134  *
39135  * Originally Released Under LGPL - original licence link has changed is not relivant.
39136  *
39137  * Fork - LGPL
39138  * <script type="text/javascript">
39139  */
39140 /*
39141  * These classes are private internal classes
39142  */
39143 Roo.bootstrap.layout.Center = function(config){
39144     config.region = "center";
39145     Roo.bootstrap.layout.Region.call(this, config);
39146     this.visible = true;
39147     this.minWidth = config.minWidth || 20;
39148     this.minHeight = config.minHeight || 20;
39149 };
39150
39151 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39152     hide : function(){
39153         // center panel can't be hidden
39154     },
39155     
39156     show : function(){
39157         // center panel can't be hidden
39158     },
39159     
39160     getMinWidth: function(){
39161         return this.minWidth;
39162     },
39163     
39164     getMinHeight: function(){
39165         return this.minHeight;
39166     }
39167 });
39168
39169
39170
39171
39172  
39173
39174
39175
39176
39177
39178
39179 Roo.bootstrap.layout.North = function(config)
39180 {
39181     config.region = 'north';
39182     config.cursor = 'n-resize';
39183     
39184     Roo.bootstrap.layout.Split.call(this, config);
39185     
39186     
39187     if(this.split){
39188         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39189         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39190         this.split.el.addClass("roo-layout-split-v");
39191     }
39192     var size = config.initialSize || config.height;
39193     if(typeof size != "undefined"){
39194         this.el.setHeight(size);
39195     }
39196 };
39197 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39198 {
39199     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39200     
39201     
39202     
39203     getBox : function(){
39204         if(this.collapsed){
39205             return this.collapsedEl.getBox();
39206         }
39207         var box = this.el.getBox();
39208         if(this.split){
39209             box.height += this.split.el.getHeight();
39210         }
39211         return box;
39212     },
39213     
39214     updateBox : function(box){
39215         if(this.split && !this.collapsed){
39216             box.height -= this.split.el.getHeight();
39217             this.split.el.setLeft(box.x);
39218             this.split.el.setTop(box.y+box.height);
39219             this.split.el.setWidth(box.width);
39220         }
39221         if(this.collapsed){
39222             this.updateBody(box.width, null);
39223         }
39224         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39225     }
39226 });
39227
39228
39229
39230
39231
39232 Roo.bootstrap.layout.South = function(config){
39233     config.region = 'south';
39234     config.cursor = 's-resize';
39235     Roo.bootstrap.layout.Split.call(this, config);
39236     if(this.split){
39237         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39238         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39239         this.split.el.addClass("roo-layout-split-v");
39240     }
39241     var size = config.initialSize || config.height;
39242     if(typeof size != "undefined"){
39243         this.el.setHeight(size);
39244     }
39245 };
39246
39247 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39248     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39249     getBox : function(){
39250         if(this.collapsed){
39251             return this.collapsedEl.getBox();
39252         }
39253         var box = this.el.getBox();
39254         if(this.split){
39255             var sh = this.split.el.getHeight();
39256             box.height += sh;
39257             box.y -= sh;
39258         }
39259         return box;
39260     },
39261     
39262     updateBox : function(box){
39263         if(this.split && !this.collapsed){
39264             var sh = this.split.el.getHeight();
39265             box.height -= sh;
39266             box.y += sh;
39267             this.split.el.setLeft(box.x);
39268             this.split.el.setTop(box.y-sh);
39269             this.split.el.setWidth(box.width);
39270         }
39271         if(this.collapsed){
39272             this.updateBody(box.width, null);
39273         }
39274         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39275     }
39276 });
39277
39278 Roo.bootstrap.layout.East = function(config){
39279     config.region = "east";
39280     config.cursor = "e-resize";
39281     Roo.bootstrap.layout.Split.call(this, config);
39282     if(this.split){
39283         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39284         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39285         this.split.el.addClass("roo-layout-split-h");
39286     }
39287     var size = config.initialSize || config.width;
39288     if(typeof size != "undefined"){
39289         this.el.setWidth(size);
39290     }
39291 };
39292 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39293     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39294     getBox : function(){
39295         if(this.collapsed){
39296             return this.collapsedEl.getBox();
39297         }
39298         var box = this.el.getBox();
39299         if(this.split){
39300             var sw = this.split.el.getWidth();
39301             box.width += sw;
39302             box.x -= sw;
39303         }
39304         return box;
39305     },
39306
39307     updateBox : function(box){
39308         if(this.split && !this.collapsed){
39309             var sw = this.split.el.getWidth();
39310             box.width -= sw;
39311             this.split.el.setLeft(box.x);
39312             this.split.el.setTop(box.y);
39313             this.split.el.setHeight(box.height);
39314             box.x += sw;
39315         }
39316         if(this.collapsed){
39317             this.updateBody(null, box.height);
39318         }
39319         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39320     }
39321 });
39322
39323 Roo.bootstrap.layout.West = function(config){
39324     config.region = "west";
39325     config.cursor = "w-resize";
39326     
39327     Roo.bootstrap.layout.Split.call(this, config);
39328     if(this.split){
39329         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39330         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39331         this.split.el.addClass("roo-layout-split-h");
39332     }
39333     
39334 };
39335 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39336     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39337     
39338     onRender: function(ctr, pos)
39339     {
39340         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39341         var size = this.config.initialSize || this.config.width;
39342         if(typeof size != "undefined"){
39343             this.el.setWidth(size);
39344         }
39345     },
39346     
39347     getBox : function(){
39348         if(this.collapsed){
39349             return this.collapsedEl.getBox();
39350         }
39351         var box = this.el.getBox();
39352         if(this.split){
39353             box.width += this.split.el.getWidth();
39354         }
39355         return box;
39356     },
39357     
39358     updateBox : function(box){
39359         if(this.split && !this.collapsed){
39360             var sw = this.split.el.getWidth();
39361             box.width -= sw;
39362             this.split.el.setLeft(box.x+box.width);
39363             this.split.el.setTop(box.y);
39364             this.split.el.setHeight(box.height);
39365         }
39366         if(this.collapsed){
39367             this.updateBody(null, box.height);
39368         }
39369         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39370     }
39371 });Roo.namespace("Roo.bootstrap.panel");/*
39372  * Based on:
39373  * Ext JS Library 1.1.1
39374  * Copyright(c) 2006-2007, Ext JS, LLC.
39375  *
39376  * Originally Released Under LGPL - original licence link has changed is not relivant.
39377  *
39378  * Fork - LGPL
39379  * <script type="text/javascript">
39380  */
39381 /**
39382  * @class Roo.ContentPanel
39383  * @extends Roo.util.Observable
39384  * A basic ContentPanel element.
39385  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39386  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39387  * @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
39388  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39389  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39390  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39391  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39392  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39393  * @cfg {String} title          The title for this panel
39394  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39395  * @cfg {String} url            Calls {@link #setUrl} with this value
39396  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39397  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39398  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39399  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39400  * @cfg {Boolean} badges render the badges
39401  * @cfg {String} cls  extra classes to use  
39402  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39403
39404  * @constructor
39405  * Create a new ContentPanel.
39406  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39407  * @param {String/Object} config A string to set only the title or a config object
39408  * @param {String} content (optional) Set the HTML content for this panel
39409  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39410  */
39411 Roo.bootstrap.panel.Content = function( config){
39412     
39413     this.tpl = config.tpl || false;
39414     
39415     var el = config.el;
39416     var content = config.content;
39417
39418     if(config.autoCreate){ // xtype is available if this is called from factory
39419         el = Roo.id();
39420     }
39421     this.el = Roo.get(el);
39422     if(!this.el && config && config.autoCreate){
39423         if(typeof config.autoCreate == "object"){
39424             if(!config.autoCreate.id){
39425                 config.autoCreate.id = config.id||el;
39426             }
39427             this.el = Roo.DomHelper.append(document.body,
39428                         config.autoCreate, true);
39429         }else{
39430             var elcfg =  {
39431                 tag: "div",
39432                 cls: (config.cls || '') +
39433                     (config.background ? ' bg-' + config.background : '') +
39434                     " roo-layout-inactive-content",
39435                 id: config.id||el
39436             };
39437             if (config.html) {
39438                 elcfg.html = config.html;
39439                 
39440             }
39441                         
39442             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39443         }
39444     } 
39445     this.closable = false;
39446     this.loaded = false;
39447     this.active = false;
39448    
39449       
39450     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39451         
39452         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39453         
39454         this.wrapEl = this.el; //this.el.wrap();
39455         var ti = [];
39456         if (config.toolbar.items) {
39457             ti = config.toolbar.items ;
39458             delete config.toolbar.items ;
39459         }
39460         
39461         var nitems = [];
39462         this.toolbar.render(this.wrapEl, 'before');
39463         for(var i =0;i < ti.length;i++) {
39464           //  Roo.log(['add child', items[i]]);
39465             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39466         }
39467         this.toolbar.items = nitems;
39468         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39469         delete config.toolbar;
39470         
39471     }
39472     /*
39473     // xtype created footer. - not sure if will work as we normally have to render first..
39474     if (this.footer && !this.footer.el && this.footer.xtype) {
39475         if (!this.wrapEl) {
39476             this.wrapEl = this.el.wrap();
39477         }
39478     
39479         this.footer.container = this.wrapEl.createChild();
39480          
39481         this.footer = Roo.factory(this.footer, Roo);
39482         
39483     }
39484     */
39485     
39486      if(typeof config == "string"){
39487         this.title = config;
39488     }else{
39489         Roo.apply(this, config);
39490     }
39491     
39492     if(this.resizeEl){
39493         this.resizeEl = Roo.get(this.resizeEl, true);
39494     }else{
39495         this.resizeEl = this.el;
39496     }
39497     // handle view.xtype
39498     
39499  
39500     
39501     
39502     this.addEvents({
39503         /**
39504          * @event activate
39505          * Fires when this panel is activated. 
39506          * @param {Roo.ContentPanel} this
39507          */
39508         "activate" : true,
39509         /**
39510          * @event deactivate
39511          * Fires when this panel is activated. 
39512          * @param {Roo.ContentPanel} this
39513          */
39514         "deactivate" : true,
39515
39516         /**
39517          * @event resize
39518          * Fires when this panel is resized if fitToFrame is true.
39519          * @param {Roo.ContentPanel} this
39520          * @param {Number} width The width after any component adjustments
39521          * @param {Number} height The height after any component adjustments
39522          */
39523         "resize" : true,
39524         
39525          /**
39526          * @event render
39527          * Fires when this tab is created
39528          * @param {Roo.ContentPanel} this
39529          */
39530         "render" : true
39531         
39532         
39533         
39534     });
39535     
39536
39537     
39538     
39539     if(this.autoScroll){
39540         this.resizeEl.setStyle("overflow", "auto");
39541     } else {
39542         // fix randome scrolling
39543         //this.el.on('scroll', function() {
39544         //    Roo.log('fix random scolling');
39545         //    this.scrollTo('top',0); 
39546         //});
39547     }
39548     content = content || this.content;
39549     if(content){
39550         this.setContent(content);
39551     }
39552     if(config && config.url){
39553         this.setUrl(this.url, this.params, this.loadOnce);
39554     }
39555     
39556     
39557     
39558     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39559     
39560     if (this.view && typeof(this.view.xtype) != 'undefined') {
39561         this.view.el = this.el.appendChild(document.createElement("div"));
39562         this.view = Roo.factory(this.view); 
39563         this.view.render  &&  this.view.render(false, '');  
39564     }
39565     
39566     
39567     this.fireEvent('render', this);
39568 };
39569
39570 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39571     
39572     cls : '',
39573     background : '',
39574     
39575     tabTip : '',
39576     
39577     setRegion : function(region){
39578         this.region = region;
39579         this.setActiveClass(region && !this.background);
39580     },
39581     
39582     
39583     setActiveClass: function(state)
39584     {
39585         if(state){
39586            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39587            this.el.setStyle('position','relative');
39588         }else{
39589            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39590            this.el.setStyle('position', 'absolute');
39591         } 
39592     },
39593     
39594     /**
39595      * Returns the toolbar for this Panel if one was configured. 
39596      * @return {Roo.Toolbar} 
39597      */
39598     getToolbar : function(){
39599         return this.toolbar;
39600     },
39601     
39602     setActiveState : function(active)
39603     {
39604         this.active = active;
39605         this.setActiveClass(active);
39606         if(!active){
39607             if(this.fireEvent("deactivate", this) === false){
39608                 return false;
39609             }
39610             return true;
39611         }
39612         this.fireEvent("activate", this);
39613         return true;
39614     },
39615     /**
39616      * Updates this panel's element
39617      * @param {String} content The new content
39618      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39619     */
39620     setContent : function(content, loadScripts){
39621         this.el.update(content, loadScripts);
39622     },
39623
39624     ignoreResize : function(w, h){
39625         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39626             return true;
39627         }else{
39628             this.lastSize = {width: w, height: h};
39629             return false;
39630         }
39631     },
39632     /**
39633      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39634      * @return {Roo.UpdateManager} The UpdateManager
39635      */
39636     getUpdateManager : function(){
39637         return this.el.getUpdateManager();
39638     },
39639      /**
39640      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39641      * @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:
39642 <pre><code>
39643 panel.load({
39644     url: "your-url.php",
39645     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39646     callback: yourFunction,
39647     scope: yourObject, //(optional scope)
39648     discardUrl: false,
39649     nocache: false,
39650     text: "Loading...",
39651     timeout: 30,
39652     scripts: false
39653 });
39654 </code></pre>
39655      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39656      * 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.
39657      * @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}
39658      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39659      * @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.
39660      * @return {Roo.ContentPanel} this
39661      */
39662     load : function(){
39663         var um = this.el.getUpdateManager();
39664         um.update.apply(um, arguments);
39665         return this;
39666     },
39667
39668
39669     /**
39670      * 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.
39671      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39672      * @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)
39673      * @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)
39674      * @return {Roo.UpdateManager} The UpdateManager
39675      */
39676     setUrl : function(url, params, loadOnce){
39677         if(this.refreshDelegate){
39678             this.removeListener("activate", this.refreshDelegate);
39679         }
39680         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39681         this.on("activate", this.refreshDelegate);
39682         return this.el.getUpdateManager();
39683     },
39684     
39685     _handleRefresh : function(url, params, loadOnce){
39686         if(!loadOnce || !this.loaded){
39687             var updater = this.el.getUpdateManager();
39688             updater.update(url, params, this._setLoaded.createDelegate(this));
39689         }
39690     },
39691     
39692     _setLoaded : function(){
39693         this.loaded = true;
39694     }, 
39695     
39696     /**
39697      * Returns this panel's id
39698      * @return {String} 
39699      */
39700     getId : function(){
39701         return this.el.id;
39702     },
39703     
39704     /** 
39705      * Returns this panel's element - used by regiosn to add.
39706      * @return {Roo.Element} 
39707      */
39708     getEl : function(){
39709         return this.wrapEl || this.el;
39710     },
39711     
39712    
39713     
39714     adjustForComponents : function(width, height)
39715     {
39716         //Roo.log('adjustForComponents ');
39717         if(this.resizeEl != this.el){
39718             width -= this.el.getFrameWidth('lr');
39719             height -= this.el.getFrameWidth('tb');
39720         }
39721         if(this.toolbar){
39722             var te = this.toolbar.getEl();
39723             te.setWidth(width);
39724             height -= te.getHeight();
39725         }
39726         if(this.footer){
39727             var te = this.footer.getEl();
39728             te.setWidth(width);
39729             height -= te.getHeight();
39730         }
39731         
39732         
39733         if(this.adjustments){
39734             width += this.adjustments[0];
39735             height += this.adjustments[1];
39736         }
39737         return {"width": width, "height": height};
39738     },
39739     
39740     setSize : function(width, height){
39741         if(this.fitToFrame && !this.ignoreResize(width, height)){
39742             if(this.fitContainer && this.resizeEl != this.el){
39743                 this.el.setSize(width, height);
39744             }
39745             var size = this.adjustForComponents(width, height);
39746             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39747             this.fireEvent('resize', this, size.width, size.height);
39748         }
39749     },
39750     
39751     /**
39752      * Returns this panel's title
39753      * @return {String} 
39754      */
39755     getTitle : function(){
39756         
39757         if (typeof(this.title) != 'object') {
39758             return this.title;
39759         }
39760         
39761         var t = '';
39762         for (var k in this.title) {
39763             if (!this.title.hasOwnProperty(k)) {
39764                 continue;
39765             }
39766             
39767             if (k.indexOf('-') >= 0) {
39768                 var s = k.split('-');
39769                 for (var i = 0; i<s.length; i++) {
39770                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39771                 }
39772             } else {
39773                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39774             }
39775         }
39776         return t;
39777     },
39778     
39779     /**
39780      * Set this panel's title
39781      * @param {String} title
39782      */
39783     setTitle : function(title){
39784         this.title = title;
39785         if(this.region){
39786             this.region.updatePanelTitle(this, title);
39787         }
39788     },
39789     
39790     /**
39791      * Returns true is this panel was configured to be closable
39792      * @return {Boolean} 
39793      */
39794     isClosable : function(){
39795         return this.closable;
39796     },
39797     
39798     beforeSlide : function(){
39799         this.el.clip();
39800         this.resizeEl.clip();
39801     },
39802     
39803     afterSlide : function(){
39804         this.el.unclip();
39805         this.resizeEl.unclip();
39806     },
39807     
39808     /**
39809      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39810      *   Will fail silently if the {@link #setUrl} method has not been called.
39811      *   This does not activate the panel, just updates its content.
39812      */
39813     refresh : function(){
39814         if(this.refreshDelegate){
39815            this.loaded = false;
39816            this.refreshDelegate();
39817         }
39818     },
39819     
39820     /**
39821      * Destroys this panel
39822      */
39823     destroy : function(){
39824         this.el.removeAllListeners();
39825         var tempEl = document.createElement("span");
39826         tempEl.appendChild(this.el.dom);
39827         tempEl.innerHTML = "";
39828         this.el.remove();
39829         this.el = null;
39830     },
39831     
39832     /**
39833      * form - if the content panel contains a form - this is a reference to it.
39834      * @type {Roo.form.Form}
39835      */
39836     form : false,
39837     /**
39838      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39839      *    This contains a reference to it.
39840      * @type {Roo.View}
39841      */
39842     view : false,
39843     
39844       /**
39845      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39846      * <pre><code>
39847
39848 layout.addxtype({
39849        xtype : 'Form',
39850        items: [ .... ]
39851    }
39852 );
39853
39854 </code></pre>
39855      * @param {Object} cfg Xtype definition of item to add.
39856      */
39857     
39858     
39859     getChildContainer: function () {
39860         return this.getEl();
39861     }
39862     
39863     
39864     /*
39865         var  ret = new Roo.factory(cfg);
39866         return ret;
39867         
39868         
39869         // add form..
39870         if (cfg.xtype.match(/^Form$/)) {
39871             
39872             var el;
39873             //if (this.footer) {
39874             //    el = this.footer.container.insertSibling(false, 'before');
39875             //} else {
39876                 el = this.el.createChild();
39877             //}
39878
39879             this.form = new  Roo.form.Form(cfg);
39880             
39881             
39882             if ( this.form.allItems.length) {
39883                 this.form.render(el.dom);
39884             }
39885             return this.form;
39886         }
39887         // should only have one of theses..
39888         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39889             // views.. should not be just added - used named prop 'view''
39890             
39891             cfg.el = this.el.appendChild(document.createElement("div"));
39892             // factory?
39893             
39894             var ret = new Roo.factory(cfg);
39895              
39896              ret.render && ret.render(false, ''); // render blank..
39897             this.view = ret;
39898             return ret;
39899         }
39900         return false;
39901     }
39902     \*/
39903 });
39904  
39905 /**
39906  * @class Roo.bootstrap.panel.Grid
39907  * @extends Roo.bootstrap.panel.Content
39908  * @constructor
39909  * Create a new GridPanel.
39910  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39911  * @param {Object} config A the config object
39912   
39913  */
39914
39915
39916
39917 Roo.bootstrap.panel.Grid = function(config)
39918 {
39919     
39920       
39921     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39922         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39923
39924     config.el = this.wrapper;
39925     //this.el = this.wrapper;
39926     
39927       if (config.container) {
39928         // ctor'ed from a Border/panel.grid
39929         
39930         
39931         this.wrapper.setStyle("overflow", "hidden");
39932         this.wrapper.addClass('roo-grid-container');
39933
39934     }
39935     
39936     
39937     if(config.toolbar){
39938         var tool_el = this.wrapper.createChild();    
39939         this.toolbar = Roo.factory(config.toolbar);
39940         var ti = [];
39941         if (config.toolbar.items) {
39942             ti = config.toolbar.items ;
39943             delete config.toolbar.items ;
39944         }
39945         
39946         var nitems = [];
39947         this.toolbar.render(tool_el);
39948         for(var i =0;i < ti.length;i++) {
39949           //  Roo.log(['add child', items[i]]);
39950             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39951         }
39952         this.toolbar.items = nitems;
39953         
39954         delete config.toolbar;
39955     }
39956     
39957     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39958     config.grid.scrollBody = true;;
39959     config.grid.monitorWindowResize = false; // turn off autosizing
39960     config.grid.autoHeight = false;
39961     config.grid.autoWidth = false;
39962     
39963     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39964     
39965     if (config.background) {
39966         // render grid on panel activation (if panel background)
39967         this.on('activate', function(gp) {
39968             if (!gp.grid.rendered) {
39969                 gp.grid.render(this.wrapper);
39970                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39971             }
39972         });
39973             
39974     } else {
39975         this.grid.render(this.wrapper);
39976         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39977
39978     }
39979     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39980     // ??? needed ??? config.el = this.wrapper;
39981     
39982     
39983     
39984   
39985     // xtype created footer. - not sure if will work as we normally have to render first..
39986     if (this.footer && !this.footer.el && this.footer.xtype) {
39987         
39988         var ctr = this.grid.getView().getFooterPanel(true);
39989         this.footer.dataSource = this.grid.dataSource;
39990         this.footer = Roo.factory(this.footer, Roo);
39991         this.footer.render(ctr);
39992         
39993     }
39994     
39995     
39996     
39997     
39998      
39999 };
40000
40001 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40002     getId : function(){
40003         return this.grid.id;
40004     },
40005     
40006     /**
40007      * Returns the grid for this panel
40008      * @return {Roo.bootstrap.Table} 
40009      */
40010     getGrid : function(){
40011         return this.grid;    
40012     },
40013     
40014     setSize : function(width, height){
40015         if(!this.ignoreResize(width, height)){
40016             var grid = this.grid;
40017             var size = this.adjustForComponents(width, height);
40018             // tfoot is not a footer?
40019           
40020             
40021             var gridel = grid.getGridEl();
40022             gridel.setSize(size.width, size.height);
40023             
40024             var tbd = grid.getGridEl().select('tbody', true).first();
40025             var thd = grid.getGridEl().select('thead',true).first();
40026             var tbf= grid.getGridEl().select('tfoot', true).first();
40027
40028             if (tbf) {
40029                 size.height -= thd.getHeight();
40030             }
40031             if (thd) {
40032                 size.height -= thd.getHeight();
40033             }
40034             
40035             tbd.setSize(size.width, size.height );
40036             // this is for the account management tab -seems to work there.
40037             var thd = grid.getGridEl().select('thead',true).first();
40038             //if (tbd) {
40039             //    tbd.setSize(size.width, size.height - thd.getHeight());
40040             //}
40041              
40042             grid.autoSize();
40043         }
40044     },
40045      
40046     
40047     
40048     beforeSlide : function(){
40049         this.grid.getView().scroller.clip();
40050     },
40051     
40052     afterSlide : function(){
40053         this.grid.getView().scroller.unclip();
40054     },
40055     
40056     destroy : function(){
40057         this.grid.destroy();
40058         delete this.grid;
40059         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40060     }
40061 });
40062
40063 /**
40064  * @class Roo.bootstrap.panel.Nest
40065  * @extends Roo.bootstrap.panel.Content
40066  * @constructor
40067  * Create a new Panel, that can contain a layout.Border.
40068  * 
40069  * 
40070  * @param {Roo.BorderLayout} layout The layout for this panel
40071  * @param {String/Object} config A string to set only the title or a config object
40072  */
40073 Roo.bootstrap.panel.Nest = function(config)
40074 {
40075     // construct with only one argument..
40076     /* FIXME - implement nicer consturctors
40077     if (layout.layout) {
40078         config = layout;
40079         layout = config.layout;
40080         delete config.layout;
40081     }
40082     if (layout.xtype && !layout.getEl) {
40083         // then layout needs constructing..
40084         layout = Roo.factory(layout, Roo);
40085     }
40086     */
40087     
40088     config.el =  config.layout.getEl();
40089     
40090     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40091     
40092     config.layout.monitorWindowResize = false; // turn off autosizing
40093     this.layout = config.layout;
40094     this.layout.getEl().addClass("roo-layout-nested-layout");
40095     this.layout.parent = this;
40096     
40097     
40098     
40099     
40100 };
40101
40102 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40103
40104     setSize : function(width, height){
40105         if(!this.ignoreResize(width, height)){
40106             var size = this.adjustForComponents(width, height);
40107             var el = this.layout.getEl();
40108             if (size.height < 1) {
40109                 el.setWidth(size.width);   
40110             } else {
40111                 el.setSize(size.width, size.height);
40112             }
40113             var touch = el.dom.offsetWidth;
40114             this.layout.layout();
40115             // ie requires a double layout on the first pass
40116             if(Roo.isIE && !this.initialized){
40117                 this.initialized = true;
40118                 this.layout.layout();
40119             }
40120         }
40121     },
40122     
40123     // activate all subpanels if not currently active..
40124     
40125     setActiveState : function(active){
40126         this.active = active;
40127         this.setActiveClass(active);
40128         
40129         if(!active){
40130             this.fireEvent("deactivate", this);
40131             return;
40132         }
40133         
40134         this.fireEvent("activate", this);
40135         // not sure if this should happen before or after..
40136         if (!this.layout) {
40137             return; // should not happen..
40138         }
40139         var reg = false;
40140         for (var r in this.layout.regions) {
40141             reg = this.layout.getRegion(r);
40142             if (reg.getActivePanel()) {
40143                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40144                 reg.setActivePanel(reg.getActivePanel());
40145                 continue;
40146             }
40147             if (!reg.panels.length) {
40148                 continue;
40149             }
40150             reg.showPanel(reg.getPanel(0));
40151         }
40152         
40153         
40154         
40155         
40156     },
40157     
40158     /**
40159      * Returns the nested BorderLayout for this panel
40160      * @return {Roo.BorderLayout} 
40161      */
40162     getLayout : function(){
40163         return this.layout;
40164     },
40165     
40166      /**
40167      * Adds a xtype elements to the layout of the nested panel
40168      * <pre><code>
40169
40170 panel.addxtype({
40171        xtype : 'ContentPanel',
40172        region: 'west',
40173        items: [ .... ]
40174    }
40175 );
40176
40177 panel.addxtype({
40178         xtype : 'NestedLayoutPanel',
40179         region: 'west',
40180         layout: {
40181            center: { },
40182            west: { }   
40183         },
40184         items : [ ... list of content panels or nested layout panels.. ]
40185    }
40186 );
40187 </code></pre>
40188      * @param {Object} cfg Xtype definition of item to add.
40189      */
40190     addxtype : function(cfg) {
40191         return this.layout.addxtype(cfg);
40192     
40193     }
40194 });/*
40195  * Based on:
40196  * Ext JS Library 1.1.1
40197  * Copyright(c) 2006-2007, Ext JS, LLC.
40198  *
40199  * Originally Released Under LGPL - original licence link has changed is not relivant.
40200  *
40201  * Fork - LGPL
40202  * <script type="text/javascript">
40203  */
40204 /**
40205  * @class Roo.TabPanel
40206  * @extends Roo.util.Observable
40207  * A lightweight tab container.
40208  * <br><br>
40209  * Usage:
40210  * <pre><code>
40211 // basic tabs 1, built from existing content
40212 var tabs = new Roo.TabPanel("tabs1");
40213 tabs.addTab("script", "View Script");
40214 tabs.addTab("markup", "View Markup");
40215 tabs.activate("script");
40216
40217 // more advanced tabs, built from javascript
40218 var jtabs = new Roo.TabPanel("jtabs");
40219 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40220
40221 // set up the UpdateManager
40222 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40223 var updater = tab2.getUpdateManager();
40224 updater.setDefaultUrl("ajax1.htm");
40225 tab2.on('activate', updater.refresh, updater, true);
40226
40227 // Use setUrl for Ajax loading
40228 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40229 tab3.setUrl("ajax2.htm", null, true);
40230
40231 // Disabled tab
40232 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40233 tab4.disable();
40234
40235 jtabs.activate("jtabs-1");
40236  * </code></pre>
40237  * @constructor
40238  * Create a new TabPanel.
40239  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40240  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40241  */
40242 Roo.bootstrap.panel.Tabs = function(config){
40243     /**
40244     * The container element for this TabPanel.
40245     * @type Roo.Element
40246     */
40247     this.el = Roo.get(config.el);
40248     delete config.el;
40249     if(config){
40250         if(typeof config == "boolean"){
40251             this.tabPosition = config ? "bottom" : "top";
40252         }else{
40253             Roo.apply(this, config);
40254         }
40255     }
40256     
40257     if(this.tabPosition == "bottom"){
40258         // if tabs are at the bottom = create the body first.
40259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40260         this.el.addClass("roo-tabs-bottom");
40261     }
40262     // next create the tabs holders
40263     
40264     if (this.tabPosition == "west"){
40265         
40266         var reg = this.region; // fake it..
40267         while (reg) {
40268             if (!reg.mgr.parent) {
40269                 break;
40270             }
40271             reg = reg.mgr.parent.region;
40272         }
40273         Roo.log("got nest?");
40274         Roo.log(reg);
40275         if (reg.mgr.getRegion('west')) {
40276             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40277             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40278             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40279             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40280             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40281         
40282             
40283         }
40284         
40285         
40286     } else {
40287      
40288         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40289         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40290         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40291         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40292     }
40293     
40294     
40295     if(Roo.isIE){
40296         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40297     }
40298     
40299     // finally - if tabs are at the top, then create the body last..
40300     if(this.tabPosition != "bottom"){
40301         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40302          * @type Roo.Element
40303          */
40304         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40305         this.el.addClass("roo-tabs-top");
40306     }
40307     this.items = [];
40308
40309     this.bodyEl.setStyle("position", "relative");
40310
40311     this.active = null;
40312     this.activateDelegate = this.activate.createDelegate(this);
40313
40314     this.addEvents({
40315         /**
40316          * @event tabchange
40317          * Fires when the active tab changes
40318          * @param {Roo.TabPanel} this
40319          * @param {Roo.TabPanelItem} activePanel The new active tab
40320          */
40321         "tabchange": true,
40322         /**
40323          * @event beforetabchange
40324          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40325          * @param {Roo.TabPanel} this
40326          * @param {Object} e Set cancel to true on this object to cancel the tab change
40327          * @param {Roo.TabPanelItem} tab The tab being changed to
40328          */
40329         "beforetabchange" : true
40330     });
40331
40332     Roo.EventManager.onWindowResize(this.onResize, this);
40333     this.cpad = this.el.getPadding("lr");
40334     this.hiddenCount = 0;
40335
40336
40337     // toolbar on the tabbar support...
40338     if (this.toolbar) {
40339         alert("no toolbar support yet");
40340         this.toolbar  = false;
40341         /*
40342         var tcfg = this.toolbar;
40343         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40344         this.toolbar = new Roo.Toolbar(tcfg);
40345         if (Roo.isSafari) {
40346             var tbl = tcfg.container.child('table', true);
40347             tbl.setAttribute('width', '100%');
40348         }
40349         */
40350         
40351     }
40352    
40353
40354
40355     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40356 };
40357
40358 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40359     /*
40360      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40361      */
40362     tabPosition : "top",
40363     /*
40364      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40365      */
40366     currentTabWidth : 0,
40367     /*
40368      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40369      */
40370     minTabWidth : 40,
40371     /*
40372      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40373      */
40374     maxTabWidth : 250,
40375     /*
40376      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40377      */
40378     preferredTabWidth : 175,
40379     /*
40380      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40381      */
40382     resizeTabs : false,
40383     /*
40384      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40385      */
40386     monitorResize : true,
40387     /*
40388      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40389      */
40390     toolbar : false,  // set by caller..
40391     
40392     region : false, /// set by caller
40393     
40394     disableTooltips : true, // not used yet...
40395
40396     /**
40397      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40398      * @param {String} id The id of the div to use <b>or create</b>
40399      * @param {String} text The text for the tab
40400      * @param {String} content (optional) Content to put in the TabPanelItem body
40401      * @param {Boolean} closable (optional) True to create a close icon on the tab
40402      * @return {Roo.TabPanelItem} The created TabPanelItem
40403      */
40404     addTab : function(id, text, content, closable, tpl)
40405     {
40406         var item = new Roo.bootstrap.panel.TabItem({
40407             panel: this,
40408             id : id,
40409             text : text,
40410             closable : closable,
40411             tpl : tpl
40412         });
40413         this.addTabItem(item);
40414         if(content){
40415             item.setContent(content);
40416         }
40417         return item;
40418     },
40419
40420     /**
40421      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40422      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40423      * @return {Roo.TabPanelItem}
40424      */
40425     getTab : function(id){
40426         return this.items[id];
40427     },
40428
40429     /**
40430      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40431      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40432      */
40433     hideTab : function(id){
40434         var t = this.items[id];
40435         if(!t.isHidden()){
40436            t.setHidden(true);
40437            this.hiddenCount++;
40438            this.autoSizeTabs();
40439         }
40440     },
40441
40442     /**
40443      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40444      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40445      */
40446     unhideTab : function(id){
40447         var t = this.items[id];
40448         if(t.isHidden()){
40449            t.setHidden(false);
40450            this.hiddenCount--;
40451            this.autoSizeTabs();
40452         }
40453     },
40454
40455     /**
40456      * Adds an existing {@link Roo.TabPanelItem}.
40457      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40458      */
40459     addTabItem : function(item)
40460     {
40461         this.items[item.id] = item;
40462         this.items.push(item);
40463         this.autoSizeTabs();
40464       //  if(this.resizeTabs){
40465     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40466   //         this.autoSizeTabs();
40467 //        }else{
40468 //            item.autoSize();
40469        // }
40470     },
40471
40472     /**
40473      * Removes a {@link Roo.TabPanelItem}.
40474      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40475      */
40476     removeTab : function(id){
40477         var items = this.items;
40478         var tab = items[id];
40479         if(!tab) { return; }
40480         var index = items.indexOf(tab);
40481         if(this.active == tab && items.length > 1){
40482             var newTab = this.getNextAvailable(index);
40483             if(newTab) {
40484                 newTab.activate();
40485             }
40486         }
40487         this.stripEl.dom.removeChild(tab.pnode.dom);
40488         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40489             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40490         }
40491         items.splice(index, 1);
40492         delete this.items[tab.id];
40493         tab.fireEvent("close", tab);
40494         tab.purgeListeners();
40495         this.autoSizeTabs();
40496     },
40497
40498     getNextAvailable : function(start){
40499         var items = this.items;
40500         var index = start;
40501         // look for a next tab that will slide over to
40502         // replace the one being removed
40503         while(index < items.length){
40504             var item = items[++index];
40505             if(item && !item.isHidden()){
40506                 return item;
40507             }
40508         }
40509         // if one isn't found select the previous tab (on the left)
40510         index = start;
40511         while(index >= 0){
40512             var item = items[--index];
40513             if(item && !item.isHidden()){
40514                 return item;
40515             }
40516         }
40517         return null;
40518     },
40519
40520     /**
40521      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40522      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40523      */
40524     disableTab : function(id){
40525         var tab = this.items[id];
40526         if(tab && this.active != tab){
40527             tab.disable();
40528         }
40529     },
40530
40531     /**
40532      * Enables a {@link Roo.TabPanelItem} that is disabled.
40533      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40534      */
40535     enableTab : function(id){
40536         var tab = this.items[id];
40537         tab.enable();
40538     },
40539
40540     /**
40541      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40542      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40543      * @return {Roo.TabPanelItem} The TabPanelItem.
40544      */
40545     activate : function(id)
40546     {
40547         //Roo.log('activite:'  + id);
40548         
40549         var tab = this.items[id];
40550         if(!tab){
40551             return null;
40552         }
40553         if(tab == this.active || tab.disabled){
40554             return tab;
40555         }
40556         var e = {};
40557         this.fireEvent("beforetabchange", this, e, tab);
40558         if(e.cancel !== true && !tab.disabled){
40559             if(this.active){
40560                 this.active.hide();
40561             }
40562             this.active = this.items[id];
40563             this.active.show();
40564             this.fireEvent("tabchange", this, this.active);
40565         }
40566         return tab;
40567     },
40568
40569     /**
40570      * Gets the active {@link Roo.TabPanelItem}.
40571      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40572      */
40573     getActiveTab : function(){
40574         return this.active;
40575     },
40576
40577     /**
40578      * Updates the tab body element to fit the height of the container element
40579      * for overflow scrolling
40580      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40581      */
40582     syncHeight : function(targetHeight){
40583         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40584         var bm = this.bodyEl.getMargins();
40585         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40586         this.bodyEl.setHeight(newHeight);
40587         return newHeight;
40588     },
40589
40590     onResize : function(){
40591         if(this.monitorResize){
40592             this.autoSizeTabs();
40593         }
40594     },
40595
40596     /**
40597      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40598      */
40599     beginUpdate : function(){
40600         this.updating = true;
40601     },
40602
40603     /**
40604      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40605      */
40606     endUpdate : function(){
40607         this.updating = false;
40608         this.autoSizeTabs();
40609     },
40610
40611     /**
40612      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40613      */
40614     autoSizeTabs : function()
40615     {
40616         var count = this.items.length;
40617         var vcount = count - this.hiddenCount;
40618         
40619         if (vcount < 2) {
40620             this.stripEl.hide();
40621         } else {
40622             this.stripEl.show();
40623         }
40624         
40625         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40626             return;
40627         }
40628         
40629         
40630         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40631         var availWidth = Math.floor(w / vcount);
40632         var b = this.stripBody;
40633         if(b.getWidth() > w){
40634             var tabs = this.items;
40635             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40636             if(availWidth < this.minTabWidth){
40637                 /*if(!this.sleft){    // incomplete scrolling code
40638                     this.createScrollButtons();
40639                 }
40640                 this.showScroll();
40641                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40642             }
40643         }else{
40644             if(this.currentTabWidth < this.preferredTabWidth){
40645                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40646             }
40647         }
40648     },
40649
40650     /**
40651      * Returns the number of tabs in this TabPanel.
40652      * @return {Number}
40653      */
40654      getCount : function(){
40655          return this.items.length;
40656      },
40657
40658     /**
40659      * Resizes all the tabs to the passed width
40660      * @param {Number} The new width
40661      */
40662     setTabWidth : function(width){
40663         this.currentTabWidth = width;
40664         for(var i = 0, len = this.items.length; i < len; i++) {
40665                 if(!this.items[i].isHidden()) {
40666                 this.items[i].setWidth(width);
40667             }
40668         }
40669     },
40670
40671     /**
40672      * Destroys this TabPanel
40673      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40674      */
40675     destroy : function(removeEl){
40676         Roo.EventManager.removeResizeListener(this.onResize, this);
40677         for(var i = 0, len = this.items.length; i < len; i++){
40678             this.items[i].purgeListeners();
40679         }
40680         if(removeEl === true){
40681             this.el.update("");
40682             this.el.remove();
40683         }
40684     },
40685     
40686     createStrip : function(container)
40687     {
40688         var strip = document.createElement("nav");
40689         strip.className = Roo.bootstrap.version == 4 ?
40690             "navbar-light bg-light" : 
40691             "navbar navbar-default"; //"x-tabs-wrap";
40692         container.appendChild(strip);
40693         return strip;
40694     },
40695     
40696     createStripList : function(strip)
40697     {
40698         // div wrapper for retard IE
40699         // returns the "tr" element.
40700         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40701         //'<div class="x-tabs-strip-wrap">'+
40702           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40703           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40704         return strip.firstChild; //.firstChild.firstChild.firstChild;
40705     },
40706     createBody : function(container)
40707     {
40708         var body = document.createElement("div");
40709         Roo.id(body, "tab-body");
40710         //Roo.fly(body).addClass("x-tabs-body");
40711         Roo.fly(body).addClass("tab-content");
40712         container.appendChild(body);
40713         return body;
40714     },
40715     createItemBody :function(bodyEl, id){
40716         var body = Roo.getDom(id);
40717         if(!body){
40718             body = document.createElement("div");
40719             body.id = id;
40720         }
40721         //Roo.fly(body).addClass("x-tabs-item-body");
40722         Roo.fly(body).addClass("tab-pane");
40723          bodyEl.insertBefore(body, bodyEl.firstChild);
40724         return body;
40725     },
40726     /** @private */
40727     createStripElements :  function(stripEl, text, closable, tpl)
40728     {
40729         var td = document.createElement("li"); // was td..
40730         td.className = 'nav-item';
40731         
40732         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40733         
40734         
40735         stripEl.appendChild(td);
40736         /*if(closable){
40737             td.className = "x-tabs-closable";
40738             if(!this.closeTpl){
40739                 this.closeTpl = new Roo.Template(
40740                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40741                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40742                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40743                 );
40744             }
40745             var el = this.closeTpl.overwrite(td, {"text": text});
40746             var close = el.getElementsByTagName("div")[0];
40747             var inner = el.getElementsByTagName("em")[0];
40748             return {"el": el, "close": close, "inner": inner};
40749         } else {
40750         */
40751         // not sure what this is..
40752 //            if(!this.tabTpl){
40753                 //this.tabTpl = new Roo.Template(
40754                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40755                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40756                 //);
40757 //                this.tabTpl = new Roo.Template(
40758 //                   '<a href="#">' +
40759 //                   '<span unselectable="on"' +
40760 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40761 //                            ' >{text}</span></a>'
40762 //                );
40763 //                
40764 //            }
40765
40766
40767             var template = tpl || this.tabTpl || false;
40768             
40769             if(!template){
40770                 template =  new Roo.Template(
40771                         Roo.bootstrap.version == 4 ? 
40772                             (
40773                                 '<a class="nav-link" href="#" unselectable="on"' +
40774                                      (this.disableTooltips ? '' : ' title="{text}"') +
40775                                      ' >{text}</a>'
40776                             ) : (
40777                                 '<a class="nav-link" href="#">' +
40778                                 '<span unselectable="on"' +
40779                                          (this.disableTooltips ? '' : ' title="{text}"') +
40780                                     ' >{text}</span></a>'
40781                             )
40782                 );
40783             }
40784             
40785             switch (typeof(template)) {
40786                 case 'object' :
40787                     break;
40788                 case 'string' :
40789                     template = new Roo.Template(template);
40790                     break;
40791                 default :
40792                     break;
40793             }
40794             
40795             var el = template.overwrite(td, {"text": text});
40796             
40797             var inner = el.getElementsByTagName("span")[0];
40798             
40799             return {"el": el, "inner": inner};
40800             
40801     }
40802         
40803     
40804 });
40805
40806 /**
40807  * @class Roo.TabPanelItem
40808  * @extends Roo.util.Observable
40809  * Represents an individual item (tab plus body) in a TabPanel.
40810  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40811  * @param {String} id The id of this TabPanelItem
40812  * @param {String} text The text for the tab of this TabPanelItem
40813  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40814  */
40815 Roo.bootstrap.panel.TabItem = function(config){
40816     /**
40817      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40818      * @type Roo.TabPanel
40819      */
40820     this.tabPanel = config.panel;
40821     /**
40822      * The id for this TabPanelItem
40823      * @type String
40824      */
40825     this.id = config.id;
40826     /** @private */
40827     this.disabled = false;
40828     /** @private */
40829     this.text = config.text;
40830     /** @private */
40831     this.loaded = false;
40832     this.closable = config.closable;
40833
40834     /**
40835      * The body element for this TabPanelItem.
40836      * @type Roo.Element
40837      */
40838     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40839     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40840     this.bodyEl.setStyle("display", "block");
40841     this.bodyEl.setStyle("zoom", "1");
40842     //this.hideAction();
40843
40844     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40845     /** @private */
40846     this.el = Roo.get(els.el);
40847     this.inner = Roo.get(els.inner, true);
40848      this.textEl = Roo.bootstrap.version == 4 ?
40849         this.el : Roo.get(this.el.dom.firstChild, true);
40850
40851     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40852     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40853
40854     
40855 //    this.el.on("mousedown", this.onTabMouseDown, this);
40856     this.el.on("click", this.onTabClick, this);
40857     /** @private */
40858     if(config.closable){
40859         var c = Roo.get(els.close, true);
40860         c.dom.title = this.closeText;
40861         c.addClassOnOver("close-over");
40862         c.on("click", this.closeClick, this);
40863      }
40864
40865     this.addEvents({
40866          /**
40867          * @event activate
40868          * Fires when this tab becomes the active tab.
40869          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40870          * @param {Roo.TabPanelItem} this
40871          */
40872         "activate": true,
40873         /**
40874          * @event beforeclose
40875          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40876          * @param {Roo.TabPanelItem} this
40877          * @param {Object} e Set cancel to true on this object to cancel the close.
40878          */
40879         "beforeclose": true,
40880         /**
40881          * @event close
40882          * Fires when this tab is closed.
40883          * @param {Roo.TabPanelItem} this
40884          */
40885          "close": true,
40886         /**
40887          * @event deactivate
40888          * Fires when this tab is no longer the active tab.
40889          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40890          * @param {Roo.TabPanelItem} this
40891          */
40892          "deactivate" : true
40893     });
40894     this.hidden = false;
40895
40896     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40897 };
40898
40899 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40900            {
40901     purgeListeners : function(){
40902        Roo.util.Observable.prototype.purgeListeners.call(this);
40903        this.el.removeAllListeners();
40904     },
40905     /**
40906      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40907      */
40908     show : function(){
40909         this.status_node.addClass("active");
40910         this.showAction();
40911         if(Roo.isOpera){
40912             this.tabPanel.stripWrap.repaint();
40913         }
40914         this.fireEvent("activate", this.tabPanel, this);
40915     },
40916
40917     /**
40918      * Returns true if this tab is the active tab.
40919      * @return {Boolean}
40920      */
40921     isActive : function(){
40922         return this.tabPanel.getActiveTab() == this;
40923     },
40924
40925     /**
40926      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40927      */
40928     hide : function(){
40929         this.status_node.removeClass("active");
40930         this.hideAction();
40931         this.fireEvent("deactivate", this.tabPanel, this);
40932     },
40933
40934     hideAction : function(){
40935         this.bodyEl.hide();
40936         this.bodyEl.setStyle("position", "absolute");
40937         this.bodyEl.setLeft("-20000px");
40938         this.bodyEl.setTop("-20000px");
40939     },
40940
40941     showAction : function(){
40942         this.bodyEl.setStyle("position", "relative");
40943         this.bodyEl.setTop("");
40944         this.bodyEl.setLeft("");
40945         this.bodyEl.show();
40946     },
40947
40948     /**
40949      * Set the tooltip for the tab.
40950      * @param {String} tooltip The tab's tooltip
40951      */
40952     setTooltip : function(text){
40953         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40954             this.textEl.dom.qtip = text;
40955             this.textEl.dom.removeAttribute('title');
40956         }else{
40957             this.textEl.dom.title = text;
40958         }
40959     },
40960
40961     onTabClick : function(e){
40962         e.preventDefault();
40963         this.tabPanel.activate(this.id);
40964     },
40965
40966     onTabMouseDown : function(e){
40967         e.preventDefault();
40968         this.tabPanel.activate(this.id);
40969     },
40970 /*
40971     getWidth : function(){
40972         return this.inner.getWidth();
40973     },
40974
40975     setWidth : function(width){
40976         var iwidth = width - this.linode.getPadding("lr");
40977         this.inner.setWidth(iwidth);
40978         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40979         this.linode.setWidth(width);
40980     },
40981 */
40982     /**
40983      * Show or hide the tab
40984      * @param {Boolean} hidden True to hide or false to show.
40985      */
40986     setHidden : function(hidden){
40987         this.hidden = hidden;
40988         this.linode.setStyle("display", hidden ? "none" : "");
40989     },
40990
40991     /**
40992      * Returns true if this tab is "hidden"
40993      * @return {Boolean}
40994      */
40995     isHidden : function(){
40996         return this.hidden;
40997     },
40998
40999     /**
41000      * Returns the text for this tab
41001      * @return {String}
41002      */
41003     getText : function(){
41004         return this.text;
41005     },
41006     /*
41007     autoSize : function(){
41008         //this.el.beginMeasure();
41009         this.textEl.setWidth(1);
41010         /*
41011          *  #2804 [new] Tabs in Roojs
41012          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41013          */
41014         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41015         //this.el.endMeasure();
41016     //},
41017
41018     /**
41019      * Sets the text for the tab (Note: this also sets the tooltip text)
41020      * @param {String} text The tab's text and tooltip
41021      */
41022     setText : function(text){
41023         this.text = text;
41024         this.textEl.update(text);
41025         this.setTooltip(text);
41026         //if(!this.tabPanel.resizeTabs){
41027         //    this.autoSize();
41028         //}
41029     },
41030     /**
41031      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41032      */
41033     activate : function(){
41034         this.tabPanel.activate(this.id);
41035     },
41036
41037     /**
41038      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41039      */
41040     disable : function(){
41041         if(this.tabPanel.active != this){
41042             this.disabled = true;
41043             this.status_node.addClass("disabled");
41044         }
41045     },
41046
41047     /**
41048      * Enables this TabPanelItem if it was previously disabled.
41049      */
41050     enable : function(){
41051         this.disabled = false;
41052         this.status_node.removeClass("disabled");
41053     },
41054
41055     /**
41056      * Sets the content for this TabPanelItem.
41057      * @param {String} content The content
41058      * @param {Boolean} loadScripts true to look for and load scripts
41059      */
41060     setContent : function(content, loadScripts){
41061         this.bodyEl.update(content, loadScripts);
41062     },
41063
41064     /**
41065      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41066      * @return {Roo.UpdateManager} The UpdateManager
41067      */
41068     getUpdateManager : function(){
41069         return this.bodyEl.getUpdateManager();
41070     },
41071
41072     /**
41073      * Set a URL to be used to load the content for this TabPanelItem.
41074      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41075      * @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)
41076      * @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)
41077      * @return {Roo.UpdateManager} The UpdateManager
41078      */
41079     setUrl : function(url, params, loadOnce){
41080         if(this.refreshDelegate){
41081             this.un('activate', this.refreshDelegate);
41082         }
41083         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41084         this.on("activate", this.refreshDelegate);
41085         return this.bodyEl.getUpdateManager();
41086     },
41087
41088     /** @private */
41089     _handleRefresh : function(url, params, loadOnce){
41090         if(!loadOnce || !this.loaded){
41091             var updater = this.bodyEl.getUpdateManager();
41092             updater.update(url, params, this._setLoaded.createDelegate(this));
41093         }
41094     },
41095
41096     /**
41097      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41098      *   Will fail silently if the setUrl method has not been called.
41099      *   This does not activate the panel, just updates its content.
41100      */
41101     refresh : function(){
41102         if(this.refreshDelegate){
41103            this.loaded = false;
41104            this.refreshDelegate();
41105         }
41106     },
41107
41108     /** @private */
41109     _setLoaded : function(){
41110         this.loaded = true;
41111     },
41112
41113     /** @private */
41114     closeClick : function(e){
41115         var o = {};
41116         e.stopEvent();
41117         this.fireEvent("beforeclose", this, o);
41118         if(o.cancel !== true){
41119             this.tabPanel.removeTab(this.id);
41120         }
41121     },
41122     /**
41123      * The text displayed in the tooltip for the close icon.
41124      * @type String
41125      */
41126     closeText : "Close this tab"
41127 });
41128 /**
41129 *    This script refer to:
41130 *    Title: International Telephone Input
41131 *    Author: Jack O'Connor
41132 *    Code version:  v12.1.12
41133 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41134 **/
41135
41136 Roo.bootstrap.PhoneInputData = function() {
41137     var d = [
41138       [
41139         "Afghanistan (‫افغانستان‬‎)",
41140         "af",
41141         "93"
41142       ],
41143       [
41144         "Albania (Shqipëri)",
41145         "al",
41146         "355"
41147       ],
41148       [
41149         "Algeria (‫الجزائر‬‎)",
41150         "dz",
41151         "213"
41152       ],
41153       [
41154         "American Samoa",
41155         "as",
41156         "1684"
41157       ],
41158       [
41159         "Andorra",
41160         "ad",
41161         "376"
41162       ],
41163       [
41164         "Angola",
41165         "ao",
41166         "244"
41167       ],
41168       [
41169         "Anguilla",
41170         "ai",
41171         "1264"
41172       ],
41173       [
41174         "Antigua and Barbuda",
41175         "ag",
41176         "1268"
41177       ],
41178       [
41179         "Argentina",
41180         "ar",
41181         "54"
41182       ],
41183       [
41184         "Armenia (Հայաստան)",
41185         "am",
41186         "374"
41187       ],
41188       [
41189         "Aruba",
41190         "aw",
41191         "297"
41192       ],
41193       [
41194         "Australia",
41195         "au",
41196         "61",
41197         0
41198       ],
41199       [
41200         "Austria (Österreich)",
41201         "at",
41202         "43"
41203       ],
41204       [
41205         "Azerbaijan (Azərbaycan)",
41206         "az",
41207         "994"
41208       ],
41209       [
41210         "Bahamas",
41211         "bs",
41212         "1242"
41213       ],
41214       [
41215         "Bahrain (‫البحرين‬‎)",
41216         "bh",
41217         "973"
41218       ],
41219       [
41220         "Bangladesh (বাংলাদেশ)",
41221         "bd",
41222         "880"
41223       ],
41224       [
41225         "Barbados",
41226         "bb",
41227         "1246"
41228       ],
41229       [
41230         "Belarus (Беларусь)",
41231         "by",
41232         "375"
41233       ],
41234       [
41235         "Belgium (België)",
41236         "be",
41237         "32"
41238       ],
41239       [
41240         "Belize",
41241         "bz",
41242         "501"
41243       ],
41244       [
41245         "Benin (Bénin)",
41246         "bj",
41247         "229"
41248       ],
41249       [
41250         "Bermuda",
41251         "bm",
41252         "1441"
41253       ],
41254       [
41255         "Bhutan (འབྲུག)",
41256         "bt",
41257         "975"
41258       ],
41259       [
41260         "Bolivia",
41261         "bo",
41262         "591"
41263       ],
41264       [
41265         "Bosnia and Herzegovina (Босна и Херцеговина)",
41266         "ba",
41267         "387"
41268       ],
41269       [
41270         "Botswana",
41271         "bw",
41272         "267"
41273       ],
41274       [
41275         "Brazil (Brasil)",
41276         "br",
41277         "55"
41278       ],
41279       [
41280         "British Indian Ocean Territory",
41281         "io",
41282         "246"
41283       ],
41284       [
41285         "British Virgin Islands",
41286         "vg",
41287         "1284"
41288       ],
41289       [
41290         "Brunei",
41291         "bn",
41292         "673"
41293       ],
41294       [
41295         "Bulgaria (България)",
41296         "bg",
41297         "359"
41298       ],
41299       [
41300         "Burkina Faso",
41301         "bf",
41302         "226"
41303       ],
41304       [
41305         "Burundi (Uburundi)",
41306         "bi",
41307         "257"
41308       ],
41309       [
41310         "Cambodia (កម្ពុជា)",
41311         "kh",
41312         "855"
41313       ],
41314       [
41315         "Cameroon (Cameroun)",
41316         "cm",
41317         "237"
41318       ],
41319       [
41320         "Canada",
41321         "ca",
41322         "1",
41323         1,
41324         ["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"]
41325       ],
41326       [
41327         "Cape Verde (Kabu Verdi)",
41328         "cv",
41329         "238"
41330       ],
41331       [
41332         "Caribbean Netherlands",
41333         "bq",
41334         "599",
41335         1
41336       ],
41337       [
41338         "Cayman Islands",
41339         "ky",
41340         "1345"
41341       ],
41342       [
41343         "Central African Republic (République centrafricaine)",
41344         "cf",
41345         "236"
41346       ],
41347       [
41348         "Chad (Tchad)",
41349         "td",
41350         "235"
41351       ],
41352       [
41353         "Chile",
41354         "cl",
41355         "56"
41356       ],
41357       [
41358         "China (中国)",
41359         "cn",
41360         "86"
41361       ],
41362       [
41363         "Christmas Island",
41364         "cx",
41365         "61",
41366         2
41367       ],
41368       [
41369         "Cocos (Keeling) Islands",
41370         "cc",
41371         "61",
41372         1
41373       ],
41374       [
41375         "Colombia",
41376         "co",
41377         "57"
41378       ],
41379       [
41380         "Comoros (‫جزر القمر‬‎)",
41381         "km",
41382         "269"
41383       ],
41384       [
41385         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41386         "cd",
41387         "243"
41388       ],
41389       [
41390         "Congo (Republic) (Congo-Brazzaville)",
41391         "cg",
41392         "242"
41393       ],
41394       [
41395         "Cook Islands",
41396         "ck",
41397         "682"
41398       ],
41399       [
41400         "Costa Rica",
41401         "cr",
41402         "506"
41403       ],
41404       [
41405         "Côte d’Ivoire",
41406         "ci",
41407         "225"
41408       ],
41409       [
41410         "Croatia (Hrvatska)",
41411         "hr",
41412         "385"
41413       ],
41414       [
41415         "Cuba",
41416         "cu",
41417         "53"
41418       ],
41419       [
41420         "Curaçao",
41421         "cw",
41422         "599",
41423         0
41424       ],
41425       [
41426         "Cyprus (Κύπρος)",
41427         "cy",
41428         "357"
41429       ],
41430       [
41431         "Czech Republic (Česká republika)",
41432         "cz",
41433         "420"
41434       ],
41435       [
41436         "Denmark (Danmark)",
41437         "dk",
41438         "45"
41439       ],
41440       [
41441         "Djibouti",
41442         "dj",
41443         "253"
41444       ],
41445       [
41446         "Dominica",
41447         "dm",
41448         "1767"
41449       ],
41450       [
41451         "Dominican Republic (República Dominicana)",
41452         "do",
41453         "1",
41454         2,
41455         ["809", "829", "849"]
41456       ],
41457       [
41458         "Ecuador",
41459         "ec",
41460         "593"
41461       ],
41462       [
41463         "Egypt (‫مصر‬‎)",
41464         "eg",
41465         "20"
41466       ],
41467       [
41468         "El Salvador",
41469         "sv",
41470         "503"
41471       ],
41472       [
41473         "Equatorial Guinea (Guinea Ecuatorial)",
41474         "gq",
41475         "240"
41476       ],
41477       [
41478         "Eritrea",
41479         "er",
41480         "291"
41481       ],
41482       [
41483         "Estonia (Eesti)",
41484         "ee",
41485         "372"
41486       ],
41487       [
41488         "Ethiopia",
41489         "et",
41490         "251"
41491       ],
41492       [
41493         "Falkland Islands (Islas Malvinas)",
41494         "fk",
41495         "500"
41496       ],
41497       [
41498         "Faroe Islands (Føroyar)",
41499         "fo",
41500         "298"
41501       ],
41502       [
41503         "Fiji",
41504         "fj",
41505         "679"
41506       ],
41507       [
41508         "Finland (Suomi)",
41509         "fi",
41510         "358",
41511         0
41512       ],
41513       [
41514         "France",
41515         "fr",
41516         "33"
41517       ],
41518       [
41519         "French Guiana (Guyane française)",
41520         "gf",
41521         "594"
41522       ],
41523       [
41524         "French Polynesia (Polynésie française)",
41525         "pf",
41526         "689"
41527       ],
41528       [
41529         "Gabon",
41530         "ga",
41531         "241"
41532       ],
41533       [
41534         "Gambia",
41535         "gm",
41536         "220"
41537       ],
41538       [
41539         "Georgia (საქართველო)",
41540         "ge",
41541         "995"
41542       ],
41543       [
41544         "Germany (Deutschland)",
41545         "de",
41546         "49"
41547       ],
41548       [
41549         "Ghana (Gaana)",
41550         "gh",
41551         "233"
41552       ],
41553       [
41554         "Gibraltar",
41555         "gi",
41556         "350"
41557       ],
41558       [
41559         "Greece (Ελλάδα)",
41560         "gr",
41561         "30"
41562       ],
41563       [
41564         "Greenland (Kalaallit Nunaat)",
41565         "gl",
41566         "299"
41567       ],
41568       [
41569         "Grenada",
41570         "gd",
41571         "1473"
41572       ],
41573       [
41574         "Guadeloupe",
41575         "gp",
41576         "590",
41577         0
41578       ],
41579       [
41580         "Guam",
41581         "gu",
41582         "1671"
41583       ],
41584       [
41585         "Guatemala",
41586         "gt",
41587         "502"
41588       ],
41589       [
41590         "Guernsey",
41591         "gg",
41592         "44",
41593         1
41594       ],
41595       [
41596         "Guinea (Guinée)",
41597         "gn",
41598         "224"
41599       ],
41600       [
41601         "Guinea-Bissau (Guiné Bissau)",
41602         "gw",
41603         "245"
41604       ],
41605       [
41606         "Guyana",
41607         "gy",
41608         "592"
41609       ],
41610       [
41611         "Haiti",
41612         "ht",
41613         "509"
41614       ],
41615       [
41616         "Honduras",
41617         "hn",
41618         "504"
41619       ],
41620       [
41621         "Hong Kong (香港)",
41622         "hk",
41623         "852"
41624       ],
41625       [
41626         "Hungary (Magyarország)",
41627         "hu",
41628         "36"
41629       ],
41630       [
41631         "Iceland (Ísland)",
41632         "is",
41633         "354"
41634       ],
41635       [
41636         "India (भारत)",
41637         "in",
41638         "91"
41639       ],
41640       [
41641         "Indonesia",
41642         "id",
41643         "62"
41644       ],
41645       [
41646         "Iran (‫ایران‬‎)",
41647         "ir",
41648         "98"
41649       ],
41650       [
41651         "Iraq (‫العراق‬‎)",
41652         "iq",
41653         "964"
41654       ],
41655       [
41656         "Ireland",
41657         "ie",
41658         "353"
41659       ],
41660       [
41661         "Isle of Man",
41662         "im",
41663         "44",
41664         2
41665       ],
41666       [
41667         "Israel (‫ישראל‬‎)",
41668         "il",
41669         "972"
41670       ],
41671       [
41672         "Italy (Italia)",
41673         "it",
41674         "39",
41675         0
41676       ],
41677       [
41678         "Jamaica",
41679         "jm",
41680         "1876"
41681       ],
41682       [
41683         "Japan (日本)",
41684         "jp",
41685         "81"
41686       ],
41687       [
41688         "Jersey",
41689         "je",
41690         "44",
41691         3
41692       ],
41693       [
41694         "Jordan (‫الأردن‬‎)",
41695         "jo",
41696         "962"
41697       ],
41698       [
41699         "Kazakhstan (Казахстан)",
41700         "kz",
41701         "7",
41702         1
41703       ],
41704       [
41705         "Kenya",
41706         "ke",
41707         "254"
41708       ],
41709       [
41710         "Kiribati",
41711         "ki",
41712         "686"
41713       ],
41714       [
41715         "Kosovo",
41716         "xk",
41717         "383"
41718       ],
41719       [
41720         "Kuwait (‫الكويت‬‎)",
41721         "kw",
41722         "965"
41723       ],
41724       [
41725         "Kyrgyzstan (Кыргызстан)",
41726         "kg",
41727         "996"
41728       ],
41729       [
41730         "Laos (ລາວ)",
41731         "la",
41732         "856"
41733       ],
41734       [
41735         "Latvia (Latvija)",
41736         "lv",
41737         "371"
41738       ],
41739       [
41740         "Lebanon (‫لبنان‬‎)",
41741         "lb",
41742         "961"
41743       ],
41744       [
41745         "Lesotho",
41746         "ls",
41747         "266"
41748       ],
41749       [
41750         "Liberia",
41751         "lr",
41752         "231"
41753       ],
41754       [
41755         "Libya (‫ليبيا‬‎)",
41756         "ly",
41757         "218"
41758       ],
41759       [
41760         "Liechtenstein",
41761         "li",
41762         "423"
41763       ],
41764       [
41765         "Lithuania (Lietuva)",
41766         "lt",
41767         "370"
41768       ],
41769       [
41770         "Luxembourg",
41771         "lu",
41772         "352"
41773       ],
41774       [
41775         "Macau (澳門)",
41776         "mo",
41777         "853"
41778       ],
41779       [
41780         "Macedonia (FYROM) (Македонија)",
41781         "mk",
41782         "389"
41783       ],
41784       [
41785         "Madagascar (Madagasikara)",
41786         "mg",
41787         "261"
41788       ],
41789       [
41790         "Malawi",
41791         "mw",
41792         "265"
41793       ],
41794       [
41795         "Malaysia",
41796         "my",
41797         "60"
41798       ],
41799       [
41800         "Maldives",
41801         "mv",
41802         "960"
41803       ],
41804       [
41805         "Mali",
41806         "ml",
41807         "223"
41808       ],
41809       [
41810         "Malta",
41811         "mt",
41812         "356"
41813       ],
41814       [
41815         "Marshall Islands",
41816         "mh",
41817         "692"
41818       ],
41819       [
41820         "Martinique",
41821         "mq",
41822         "596"
41823       ],
41824       [
41825         "Mauritania (‫موريتانيا‬‎)",
41826         "mr",
41827         "222"
41828       ],
41829       [
41830         "Mauritius (Moris)",
41831         "mu",
41832         "230"
41833       ],
41834       [
41835         "Mayotte",
41836         "yt",
41837         "262",
41838         1
41839       ],
41840       [
41841         "Mexico (México)",
41842         "mx",
41843         "52"
41844       ],
41845       [
41846         "Micronesia",
41847         "fm",
41848         "691"
41849       ],
41850       [
41851         "Moldova (Republica Moldova)",
41852         "md",
41853         "373"
41854       ],
41855       [
41856         "Monaco",
41857         "mc",
41858         "377"
41859       ],
41860       [
41861         "Mongolia (Монгол)",
41862         "mn",
41863         "976"
41864       ],
41865       [
41866         "Montenegro (Crna Gora)",
41867         "me",
41868         "382"
41869       ],
41870       [
41871         "Montserrat",
41872         "ms",
41873         "1664"
41874       ],
41875       [
41876         "Morocco (‫المغرب‬‎)",
41877         "ma",
41878         "212",
41879         0
41880       ],
41881       [
41882         "Mozambique (Moçambique)",
41883         "mz",
41884         "258"
41885       ],
41886       [
41887         "Myanmar (Burma) (မြန်မာ)",
41888         "mm",
41889         "95"
41890       ],
41891       [
41892         "Namibia (Namibië)",
41893         "na",
41894         "264"
41895       ],
41896       [
41897         "Nauru",
41898         "nr",
41899         "674"
41900       ],
41901       [
41902         "Nepal (नेपाल)",
41903         "np",
41904         "977"
41905       ],
41906       [
41907         "Netherlands (Nederland)",
41908         "nl",
41909         "31"
41910       ],
41911       [
41912         "New Caledonia (Nouvelle-Calédonie)",
41913         "nc",
41914         "687"
41915       ],
41916       [
41917         "New Zealand",
41918         "nz",
41919         "64"
41920       ],
41921       [
41922         "Nicaragua",
41923         "ni",
41924         "505"
41925       ],
41926       [
41927         "Niger (Nijar)",
41928         "ne",
41929         "227"
41930       ],
41931       [
41932         "Nigeria",
41933         "ng",
41934         "234"
41935       ],
41936       [
41937         "Niue",
41938         "nu",
41939         "683"
41940       ],
41941       [
41942         "Norfolk Island",
41943         "nf",
41944         "672"
41945       ],
41946       [
41947         "North Korea (조선 민주주의 인민 공화국)",
41948         "kp",
41949         "850"
41950       ],
41951       [
41952         "Northern Mariana Islands",
41953         "mp",
41954         "1670"
41955       ],
41956       [
41957         "Norway (Norge)",
41958         "no",
41959         "47",
41960         0
41961       ],
41962       [
41963         "Oman (‫عُمان‬‎)",
41964         "om",
41965         "968"
41966       ],
41967       [
41968         "Pakistan (‫پاکستان‬‎)",
41969         "pk",
41970         "92"
41971       ],
41972       [
41973         "Palau",
41974         "pw",
41975         "680"
41976       ],
41977       [
41978         "Palestine (‫فلسطين‬‎)",
41979         "ps",
41980         "970"
41981       ],
41982       [
41983         "Panama (Panamá)",
41984         "pa",
41985         "507"
41986       ],
41987       [
41988         "Papua New Guinea",
41989         "pg",
41990         "675"
41991       ],
41992       [
41993         "Paraguay",
41994         "py",
41995         "595"
41996       ],
41997       [
41998         "Peru (Perú)",
41999         "pe",
42000         "51"
42001       ],
42002       [
42003         "Philippines",
42004         "ph",
42005         "63"
42006       ],
42007       [
42008         "Poland (Polska)",
42009         "pl",
42010         "48"
42011       ],
42012       [
42013         "Portugal",
42014         "pt",
42015         "351"
42016       ],
42017       [
42018         "Puerto Rico",
42019         "pr",
42020         "1",
42021         3,
42022         ["787", "939"]
42023       ],
42024       [
42025         "Qatar (‫قطر‬‎)",
42026         "qa",
42027         "974"
42028       ],
42029       [
42030         "Réunion (La Réunion)",
42031         "re",
42032         "262",
42033         0
42034       ],
42035       [
42036         "Romania (România)",
42037         "ro",
42038         "40"
42039       ],
42040       [
42041         "Russia (Россия)",
42042         "ru",
42043         "7",
42044         0
42045       ],
42046       [
42047         "Rwanda",
42048         "rw",
42049         "250"
42050       ],
42051       [
42052         "Saint Barthélemy",
42053         "bl",
42054         "590",
42055         1
42056       ],
42057       [
42058         "Saint Helena",
42059         "sh",
42060         "290"
42061       ],
42062       [
42063         "Saint Kitts and Nevis",
42064         "kn",
42065         "1869"
42066       ],
42067       [
42068         "Saint Lucia",
42069         "lc",
42070         "1758"
42071       ],
42072       [
42073         "Saint Martin (Saint-Martin (partie française))",
42074         "mf",
42075         "590",
42076         2
42077       ],
42078       [
42079         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42080         "pm",
42081         "508"
42082       ],
42083       [
42084         "Saint Vincent and the Grenadines",
42085         "vc",
42086         "1784"
42087       ],
42088       [
42089         "Samoa",
42090         "ws",
42091         "685"
42092       ],
42093       [
42094         "San Marino",
42095         "sm",
42096         "378"
42097       ],
42098       [
42099         "São Tomé and Príncipe (São Tomé e Príncipe)",
42100         "st",
42101         "239"
42102       ],
42103       [
42104         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42105         "sa",
42106         "966"
42107       ],
42108       [
42109         "Senegal (Sénégal)",
42110         "sn",
42111         "221"
42112       ],
42113       [
42114         "Serbia (Србија)",
42115         "rs",
42116         "381"
42117       ],
42118       [
42119         "Seychelles",
42120         "sc",
42121         "248"
42122       ],
42123       [
42124         "Sierra Leone",
42125         "sl",
42126         "232"
42127       ],
42128       [
42129         "Singapore",
42130         "sg",
42131         "65"
42132       ],
42133       [
42134         "Sint Maarten",
42135         "sx",
42136         "1721"
42137       ],
42138       [
42139         "Slovakia (Slovensko)",
42140         "sk",
42141         "421"
42142       ],
42143       [
42144         "Slovenia (Slovenija)",
42145         "si",
42146         "386"
42147       ],
42148       [
42149         "Solomon Islands",
42150         "sb",
42151         "677"
42152       ],
42153       [
42154         "Somalia (Soomaaliya)",
42155         "so",
42156         "252"
42157       ],
42158       [
42159         "South Africa",
42160         "za",
42161         "27"
42162       ],
42163       [
42164         "South Korea (대한민국)",
42165         "kr",
42166         "82"
42167       ],
42168       [
42169         "South Sudan (‫جنوب السودان‬‎)",
42170         "ss",
42171         "211"
42172       ],
42173       [
42174         "Spain (España)",
42175         "es",
42176         "34"
42177       ],
42178       [
42179         "Sri Lanka (ශ්‍රී ලංකාව)",
42180         "lk",
42181         "94"
42182       ],
42183       [
42184         "Sudan (‫السودان‬‎)",
42185         "sd",
42186         "249"
42187       ],
42188       [
42189         "Suriname",
42190         "sr",
42191         "597"
42192       ],
42193       [
42194         "Svalbard and Jan Mayen",
42195         "sj",
42196         "47",
42197         1
42198       ],
42199       [
42200         "Swaziland",
42201         "sz",
42202         "268"
42203       ],
42204       [
42205         "Sweden (Sverige)",
42206         "se",
42207         "46"
42208       ],
42209       [
42210         "Switzerland (Schweiz)",
42211         "ch",
42212         "41"
42213       ],
42214       [
42215         "Syria (‫سوريا‬‎)",
42216         "sy",
42217         "963"
42218       ],
42219       [
42220         "Taiwan (台灣)",
42221         "tw",
42222         "886"
42223       ],
42224       [
42225         "Tajikistan",
42226         "tj",
42227         "992"
42228       ],
42229       [
42230         "Tanzania",
42231         "tz",
42232         "255"
42233       ],
42234       [
42235         "Thailand (ไทย)",
42236         "th",
42237         "66"
42238       ],
42239       [
42240         "Timor-Leste",
42241         "tl",
42242         "670"
42243       ],
42244       [
42245         "Togo",
42246         "tg",
42247         "228"
42248       ],
42249       [
42250         "Tokelau",
42251         "tk",
42252         "690"
42253       ],
42254       [
42255         "Tonga",
42256         "to",
42257         "676"
42258       ],
42259       [
42260         "Trinidad and Tobago",
42261         "tt",
42262         "1868"
42263       ],
42264       [
42265         "Tunisia (‫تونس‬‎)",
42266         "tn",
42267         "216"
42268       ],
42269       [
42270         "Turkey (Türkiye)",
42271         "tr",
42272         "90"
42273       ],
42274       [
42275         "Turkmenistan",
42276         "tm",
42277         "993"
42278       ],
42279       [
42280         "Turks and Caicos Islands",
42281         "tc",
42282         "1649"
42283       ],
42284       [
42285         "Tuvalu",
42286         "tv",
42287         "688"
42288       ],
42289       [
42290         "U.S. Virgin Islands",
42291         "vi",
42292         "1340"
42293       ],
42294       [
42295         "Uganda",
42296         "ug",
42297         "256"
42298       ],
42299       [
42300         "Ukraine (Україна)",
42301         "ua",
42302         "380"
42303       ],
42304       [
42305         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42306         "ae",
42307         "971"
42308       ],
42309       [
42310         "United Kingdom",
42311         "gb",
42312         "44",
42313         0
42314       ],
42315       [
42316         "United States",
42317         "us",
42318         "1",
42319         0
42320       ],
42321       [
42322         "Uruguay",
42323         "uy",
42324         "598"
42325       ],
42326       [
42327         "Uzbekistan (Oʻzbekiston)",
42328         "uz",
42329         "998"
42330       ],
42331       [
42332         "Vanuatu",
42333         "vu",
42334         "678"
42335       ],
42336       [
42337         "Vatican City (Città del Vaticano)",
42338         "va",
42339         "39",
42340         1
42341       ],
42342       [
42343         "Venezuela",
42344         "ve",
42345         "58"
42346       ],
42347       [
42348         "Vietnam (Việt Nam)",
42349         "vn",
42350         "84"
42351       ],
42352       [
42353         "Wallis and Futuna (Wallis-et-Futuna)",
42354         "wf",
42355         "681"
42356       ],
42357       [
42358         "Western Sahara (‫الصحراء الغربية‬‎)",
42359         "eh",
42360         "212",
42361         1
42362       ],
42363       [
42364         "Yemen (‫اليمن‬‎)",
42365         "ye",
42366         "967"
42367       ],
42368       [
42369         "Zambia",
42370         "zm",
42371         "260"
42372       ],
42373       [
42374         "Zimbabwe",
42375         "zw",
42376         "263"
42377       ],
42378       [
42379         "Åland Islands",
42380         "ax",
42381         "358",
42382         1
42383       ]
42384   ];
42385   
42386   return d;
42387 }/**
42388 *    This script refer to:
42389 *    Title: International Telephone Input
42390 *    Author: Jack O'Connor
42391 *    Code version:  v12.1.12
42392 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42393 **/
42394
42395 /**
42396  * @class Roo.bootstrap.PhoneInput
42397  * @extends Roo.bootstrap.TriggerField
42398  * An input with International dial-code selection
42399  
42400  * @cfg {String} defaultDialCode default '+852'
42401  * @cfg {Array} preferedCountries default []
42402   
42403  * @constructor
42404  * Create a new PhoneInput.
42405  * @param {Object} config Configuration options
42406  */
42407
42408 Roo.bootstrap.PhoneInput = function(config) {
42409     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42410 };
42411
42412 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42413         
42414         listWidth: undefined,
42415         
42416         selectedClass: 'active',
42417         
42418         invalidClass : "has-warning",
42419         
42420         validClass: 'has-success',
42421         
42422         allowed: '0123456789',
42423         
42424         max_length: 15,
42425         
42426         /**
42427          * @cfg {String} defaultDialCode The default dial code when initializing the input
42428          */
42429         defaultDialCode: '+852',
42430         
42431         /**
42432          * @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
42433          */
42434         preferedCountries: false,
42435         
42436         getAutoCreate : function()
42437         {
42438             var data = Roo.bootstrap.PhoneInputData();
42439             var align = this.labelAlign || this.parentLabelAlign();
42440             var id = Roo.id();
42441             
42442             this.allCountries = [];
42443             this.dialCodeMapping = [];
42444             
42445             for (var i = 0; i < data.length; i++) {
42446               var c = data[i];
42447               this.allCountries[i] = {
42448                 name: c[0],
42449                 iso2: c[1],
42450                 dialCode: c[2],
42451                 priority: c[3] || 0,
42452                 areaCodes: c[4] || null
42453               };
42454               this.dialCodeMapping[c[2]] = {
42455                   name: c[0],
42456                   iso2: c[1],
42457                   priority: c[3] || 0,
42458                   areaCodes: c[4] || null
42459               };
42460             }
42461             
42462             var cfg = {
42463                 cls: 'form-group',
42464                 cn: []
42465             };
42466             
42467             var input =  {
42468                 tag: 'input',
42469                 id : id,
42470                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42471                 maxlength: this.max_length,
42472                 cls : 'form-control tel-input',
42473                 autocomplete: 'new-password'
42474             };
42475             
42476             var hiddenInput = {
42477                 tag: 'input',
42478                 type: 'hidden',
42479                 cls: 'hidden-tel-input'
42480             };
42481             
42482             if (this.name) {
42483                 hiddenInput.name = this.name;
42484             }
42485             
42486             if (this.disabled) {
42487                 input.disabled = true;
42488             }
42489             
42490             var flag_container = {
42491                 tag: 'div',
42492                 cls: 'flag-box',
42493                 cn: [
42494                     {
42495                         tag: 'div',
42496                         cls: 'flag'
42497                     },
42498                     {
42499                         tag: 'div',
42500                         cls: 'caret'
42501                     }
42502                 ]
42503             };
42504             
42505             var box = {
42506                 tag: 'div',
42507                 cls: this.hasFeedback ? 'has-feedback' : '',
42508                 cn: [
42509                     hiddenInput,
42510                     input,
42511                     {
42512                         tag: 'input',
42513                         cls: 'dial-code-holder',
42514                         disabled: true
42515                     }
42516                 ]
42517             };
42518             
42519             var container = {
42520                 cls: 'roo-select2-container input-group',
42521                 cn: [
42522                     flag_container,
42523                     box
42524                 ]
42525             };
42526             
42527             if (this.fieldLabel.length) {
42528                 var indicator = {
42529                     tag: 'i',
42530                     tooltip: 'This field is required'
42531                 };
42532                 
42533                 var label = {
42534                     tag: 'label',
42535                     'for':  id,
42536                     cls: 'control-label',
42537                     cn: []
42538                 };
42539                 
42540                 var label_text = {
42541                     tag: 'span',
42542                     html: this.fieldLabel
42543                 };
42544                 
42545                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42546                 label.cn = [
42547                     indicator,
42548                     label_text
42549                 ];
42550                 
42551                 if(this.indicatorpos == 'right') {
42552                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42553                     label.cn = [
42554                         label_text,
42555                         indicator
42556                     ];
42557                 }
42558                 
42559                 if(align == 'left') {
42560                     container = {
42561                         tag: 'div',
42562                         cn: [
42563                             container
42564                         ]
42565                     };
42566                     
42567                     if(this.labelWidth > 12){
42568                         label.style = "width: " + this.labelWidth + 'px';
42569                     }
42570                     if(this.labelWidth < 13 && this.labelmd == 0){
42571                         this.labelmd = this.labelWidth;
42572                     }
42573                     if(this.labellg > 0){
42574                         label.cls += ' col-lg-' + this.labellg;
42575                         input.cls += ' col-lg-' + (12 - this.labellg);
42576                     }
42577                     if(this.labelmd > 0){
42578                         label.cls += ' col-md-' + this.labelmd;
42579                         container.cls += ' col-md-' + (12 - this.labelmd);
42580                     }
42581                     if(this.labelsm > 0){
42582                         label.cls += ' col-sm-' + this.labelsm;
42583                         container.cls += ' col-sm-' + (12 - this.labelsm);
42584                     }
42585                     if(this.labelxs > 0){
42586                         label.cls += ' col-xs-' + this.labelxs;
42587                         container.cls += ' col-xs-' + (12 - this.labelxs);
42588                     }
42589                 }
42590             }
42591             
42592             cfg.cn = [
42593                 label,
42594                 container
42595             ];
42596             
42597             var settings = this;
42598             
42599             ['xs','sm','md','lg'].map(function(size){
42600                 if (settings[size]) {
42601                     cfg.cls += ' col-' + size + '-' + settings[size];
42602                 }
42603             });
42604             
42605             this.store = new Roo.data.Store({
42606                 proxy : new Roo.data.MemoryProxy({}),
42607                 reader : new Roo.data.JsonReader({
42608                     fields : [
42609                         {
42610                             'name' : 'name',
42611                             'type' : 'string'
42612                         },
42613                         {
42614                             'name' : 'iso2',
42615                             'type' : 'string'
42616                         },
42617                         {
42618                             'name' : 'dialCode',
42619                             'type' : 'string'
42620                         },
42621                         {
42622                             'name' : 'priority',
42623                             'type' : 'string'
42624                         },
42625                         {
42626                             'name' : 'areaCodes',
42627                             'type' : 'string'
42628                         }
42629                     ]
42630                 })
42631             });
42632             
42633             if(!this.preferedCountries) {
42634                 this.preferedCountries = [
42635                     'hk',
42636                     'gb',
42637                     'us'
42638                 ];
42639             }
42640             
42641             var p = this.preferedCountries.reverse();
42642             
42643             if(p) {
42644                 for (var i = 0; i < p.length; i++) {
42645                     for (var j = 0; j < this.allCountries.length; j++) {
42646                         if(this.allCountries[j].iso2 == p[i]) {
42647                             var t = this.allCountries[j];
42648                             this.allCountries.splice(j,1);
42649                             this.allCountries.unshift(t);
42650                         }
42651                     } 
42652                 }
42653             }
42654             
42655             this.store.proxy.data = {
42656                 success: true,
42657                 data: this.allCountries
42658             };
42659             
42660             return cfg;
42661         },
42662         
42663         initEvents : function()
42664         {
42665             this.createList();
42666             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42667             
42668             this.indicator = this.indicatorEl();
42669             this.flag = this.flagEl();
42670             this.dialCodeHolder = this.dialCodeHolderEl();
42671             
42672             this.trigger = this.el.select('div.flag-box',true).first();
42673             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42674             
42675             var _this = this;
42676             
42677             (function(){
42678                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42679                 _this.list.setWidth(lw);
42680             }).defer(100);
42681             
42682             this.list.on('mouseover', this.onViewOver, this);
42683             this.list.on('mousemove', this.onViewMove, this);
42684             this.inputEl().on("keyup", this.onKeyUp, this);
42685             this.inputEl().on("keypress", this.onKeyPress, this);
42686             
42687             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42688
42689             this.view = new Roo.View(this.list, this.tpl, {
42690                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42691             });
42692             
42693             this.view.on('click', this.onViewClick, this);
42694             this.setValue(this.defaultDialCode);
42695         },
42696         
42697         onTriggerClick : function(e)
42698         {
42699             Roo.log('trigger click');
42700             if(this.disabled){
42701                 return;
42702             }
42703             
42704             if(this.isExpanded()){
42705                 this.collapse();
42706                 this.hasFocus = false;
42707             }else {
42708                 this.store.load({});
42709                 this.hasFocus = true;
42710                 this.expand();
42711             }
42712         },
42713         
42714         isExpanded : function()
42715         {
42716             return this.list.isVisible();
42717         },
42718         
42719         collapse : function()
42720         {
42721             if(!this.isExpanded()){
42722                 return;
42723             }
42724             this.list.hide();
42725             Roo.get(document).un('mousedown', this.collapseIf, this);
42726             Roo.get(document).un('mousewheel', this.collapseIf, this);
42727             this.fireEvent('collapse', this);
42728             this.validate();
42729         },
42730         
42731         expand : function()
42732         {
42733             Roo.log('expand');
42734
42735             if(this.isExpanded() || !this.hasFocus){
42736                 return;
42737             }
42738             
42739             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42740             this.list.setWidth(lw);
42741             
42742             this.list.show();
42743             this.restrictHeight();
42744             
42745             Roo.get(document).on('mousedown', this.collapseIf, this);
42746             Roo.get(document).on('mousewheel', this.collapseIf, this);
42747             
42748             this.fireEvent('expand', this);
42749         },
42750         
42751         restrictHeight : function()
42752         {
42753             this.list.alignTo(this.inputEl(), this.listAlign);
42754             this.list.alignTo(this.inputEl(), this.listAlign);
42755         },
42756         
42757         onViewOver : function(e, t)
42758         {
42759             if(this.inKeyMode){
42760                 return;
42761             }
42762             var item = this.view.findItemFromChild(t);
42763             
42764             if(item){
42765                 var index = this.view.indexOf(item);
42766                 this.select(index, false);
42767             }
42768         },
42769
42770         // private
42771         onViewClick : function(view, doFocus, el, e)
42772         {
42773             var index = this.view.getSelectedIndexes()[0];
42774             
42775             var r = this.store.getAt(index);
42776             
42777             if(r){
42778                 this.onSelect(r, index);
42779             }
42780             if(doFocus !== false && !this.blockFocus){
42781                 this.inputEl().focus();
42782             }
42783         },
42784         
42785         onViewMove : function(e, t)
42786         {
42787             this.inKeyMode = false;
42788         },
42789         
42790         select : function(index, scrollIntoView)
42791         {
42792             this.selectedIndex = index;
42793             this.view.select(index);
42794             if(scrollIntoView !== false){
42795                 var el = this.view.getNode(index);
42796                 if(el){
42797                     this.list.scrollChildIntoView(el, false);
42798                 }
42799             }
42800         },
42801         
42802         createList : function()
42803         {
42804             this.list = Roo.get(document.body).createChild({
42805                 tag: 'ul',
42806                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42807                 style: 'display:none'
42808             });
42809             
42810             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42811         },
42812         
42813         collapseIf : function(e)
42814         {
42815             var in_combo  = e.within(this.el);
42816             var in_list =  e.within(this.list);
42817             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42818             
42819             if (in_combo || in_list || is_list) {
42820                 return;
42821             }
42822             this.collapse();
42823         },
42824         
42825         onSelect : function(record, index)
42826         {
42827             if(this.fireEvent('beforeselect', this, record, index) !== false){
42828                 
42829                 this.setFlagClass(record.data.iso2);
42830                 this.setDialCode(record.data.dialCode);
42831                 this.hasFocus = false;
42832                 this.collapse();
42833                 this.fireEvent('select', this, record, index);
42834             }
42835         },
42836         
42837         flagEl : function()
42838         {
42839             var flag = this.el.select('div.flag',true).first();
42840             if(!flag){
42841                 return false;
42842             }
42843             return flag;
42844         },
42845         
42846         dialCodeHolderEl : function()
42847         {
42848             var d = this.el.select('input.dial-code-holder',true).first();
42849             if(!d){
42850                 return false;
42851             }
42852             return d;
42853         },
42854         
42855         setDialCode : function(v)
42856         {
42857             this.dialCodeHolder.dom.value = '+'+v;
42858         },
42859         
42860         setFlagClass : function(n)
42861         {
42862             this.flag.dom.className = 'flag '+n;
42863         },
42864         
42865         getValue : function()
42866         {
42867             var v = this.inputEl().getValue();
42868             if(this.dialCodeHolder) {
42869                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42870             }
42871             return v;
42872         },
42873         
42874         setValue : function(v)
42875         {
42876             var d = this.getDialCode(v);
42877             
42878             //invalid dial code
42879             if(v.length == 0 || !d || d.length == 0) {
42880                 if(this.rendered){
42881                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42882                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42883                 }
42884                 return;
42885             }
42886             
42887             //valid dial code
42888             this.setFlagClass(this.dialCodeMapping[d].iso2);
42889             this.setDialCode(d);
42890             this.inputEl().dom.value = v.replace('+'+d,'');
42891             this.hiddenEl().dom.value = this.getValue();
42892             
42893             this.validate();
42894         },
42895         
42896         getDialCode : function(v)
42897         {
42898             v = v ||  '';
42899             
42900             if (v.length == 0) {
42901                 return this.dialCodeHolder.dom.value;
42902             }
42903             
42904             var dialCode = "";
42905             if (v.charAt(0) != "+") {
42906                 return false;
42907             }
42908             var numericChars = "";
42909             for (var i = 1; i < v.length; i++) {
42910               var c = v.charAt(i);
42911               if (!isNaN(c)) {
42912                 numericChars += c;
42913                 if (this.dialCodeMapping[numericChars]) {
42914                   dialCode = v.substr(1, i);
42915                 }
42916                 if (numericChars.length == 4) {
42917                   break;
42918                 }
42919               }
42920             }
42921             return dialCode;
42922         },
42923         
42924         reset : function()
42925         {
42926             this.setValue(this.defaultDialCode);
42927             this.validate();
42928         },
42929         
42930         hiddenEl : function()
42931         {
42932             return this.el.select('input.hidden-tel-input',true).first();
42933         },
42934         
42935         // after setting val
42936         onKeyUp : function(e){
42937             this.setValue(this.getValue());
42938         },
42939         
42940         onKeyPress : function(e){
42941             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42942                 e.stopEvent();
42943             }
42944         }
42945         
42946 });
42947 /**
42948  * @class Roo.bootstrap.MoneyField
42949  * @extends Roo.bootstrap.ComboBox
42950  * Bootstrap MoneyField class
42951  * 
42952  * @constructor
42953  * Create a new MoneyField.
42954  * @param {Object} config Configuration options
42955  */
42956
42957 Roo.bootstrap.MoneyField = function(config) {
42958     
42959     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42960     
42961 };
42962
42963 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42964     
42965     /**
42966      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42967      */
42968     allowDecimals : true,
42969     /**
42970      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42971      */
42972     decimalSeparator : ".",
42973     /**
42974      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42975      */
42976     decimalPrecision : 0,
42977     /**
42978      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42979      */
42980     allowNegative : true,
42981     /**
42982      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42983      */
42984     allowZero: true,
42985     /**
42986      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42987      */
42988     minValue : Number.NEGATIVE_INFINITY,
42989     /**
42990      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42991      */
42992     maxValue : Number.MAX_VALUE,
42993     /**
42994      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42995      */
42996     minText : "The minimum value for this field is {0}",
42997     /**
42998      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42999      */
43000     maxText : "The maximum value for this field is {0}",
43001     /**
43002      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43003      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43004      */
43005     nanText : "{0} is not a valid number",
43006     /**
43007      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43008      */
43009     castInt : true,
43010     /**
43011      * @cfg {String} defaults currency of the MoneyField
43012      * value should be in lkey
43013      */
43014     defaultCurrency : false,
43015     /**
43016      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43017      */
43018     thousandsDelimiter : false,
43019     /**
43020      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43021      */
43022     max_length: false,
43023     
43024     inputlg : 9,
43025     inputmd : 9,
43026     inputsm : 9,
43027     inputxs : 6,
43028     
43029     store : false,
43030     
43031     getAutoCreate : function()
43032     {
43033         var align = this.labelAlign || this.parentLabelAlign();
43034         
43035         var id = Roo.id();
43036
43037         var cfg = {
43038             cls: 'form-group',
43039             cn: []
43040         };
43041
43042         var input =  {
43043             tag: 'input',
43044             id : id,
43045             cls : 'form-control roo-money-amount-input',
43046             autocomplete: 'new-password'
43047         };
43048         
43049         var hiddenInput = {
43050             tag: 'input',
43051             type: 'hidden',
43052             id: Roo.id(),
43053             cls: 'hidden-number-input'
43054         };
43055         
43056         if(this.max_length) {
43057             input.maxlength = this.max_length; 
43058         }
43059         
43060         if (this.name) {
43061             hiddenInput.name = this.name;
43062         }
43063
43064         if (this.disabled) {
43065             input.disabled = true;
43066         }
43067
43068         var clg = 12 - this.inputlg;
43069         var cmd = 12 - this.inputmd;
43070         var csm = 12 - this.inputsm;
43071         var cxs = 12 - this.inputxs;
43072         
43073         var container = {
43074             tag : 'div',
43075             cls : 'row roo-money-field',
43076             cn : [
43077                 {
43078                     tag : 'div',
43079                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43080                     cn : [
43081                         {
43082                             tag : 'div',
43083                             cls: 'roo-select2-container input-group',
43084                             cn: [
43085                                 {
43086                                     tag : 'input',
43087                                     cls : 'form-control roo-money-currency-input',
43088                                     autocomplete: 'new-password',
43089                                     readOnly : 1,
43090                                     name : this.currencyName
43091                                 },
43092                                 {
43093                                     tag :'span',
43094                                     cls : 'input-group-addon',
43095                                     cn : [
43096                                         {
43097                                             tag: 'span',
43098                                             cls: 'caret'
43099                                         }
43100                                     ]
43101                                 }
43102                             ]
43103                         }
43104                     ]
43105                 },
43106                 {
43107                     tag : 'div',
43108                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43109                     cn : [
43110                         {
43111                             tag: 'div',
43112                             cls: this.hasFeedback ? 'has-feedback' : '',
43113                             cn: [
43114                                 input
43115                             ]
43116                         }
43117                     ]
43118                 }
43119             ]
43120             
43121         };
43122         
43123         if (this.fieldLabel.length) {
43124             var indicator = {
43125                 tag: 'i',
43126                 tooltip: 'This field is required'
43127             };
43128
43129             var label = {
43130                 tag: 'label',
43131                 'for':  id,
43132                 cls: 'control-label',
43133                 cn: []
43134             };
43135
43136             var label_text = {
43137                 tag: 'span',
43138                 html: this.fieldLabel
43139             };
43140
43141             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43142             label.cn = [
43143                 indicator,
43144                 label_text
43145             ];
43146
43147             if(this.indicatorpos == 'right') {
43148                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43149                 label.cn = [
43150                     label_text,
43151                     indicator
43152                 ];
43153             }
43154
43155             if(align == 'left') {
43156                 container = {
43157                     tag: 'div',
43158                     cn: [
43159                         container
43160                     ]
43161                 };
43162
43163                 if(this.labelWidth > 12){
43164                     label.style = "width: " + this.labelWidth + 'px';
43165                 }
43166                 if(this.labelWidth < 13 && this.labelmd == 0){
43167                     this.labelmd = this.labelWidth;
43168                 }
43169                 if(this.labellg > 0){
43170                     label.cls += ' col-lg-' + this.labellg;
43171                     input.cls += ' col-lg-' + (12 - this.labellg);
43172                 }
43173                 if(this.labelmd > 0){
43174                     label.cls += ' col-md-' + this.labelmd;
43175                     container.cls += ' col-md-' + (12 - this.labelmd);
43176                 }
43177                 if(this.labelsm > 0){
43178                     label.cls += ' col-sm-' + this.labelsm;
43179                     container.cls += ' col-sm-' + (12 - this.labelsm);
43180                 }
43181                 if(this.labelxs > 0){
43182                     label.cls += ' col-xs-' + this.labelxs;
43183                     container.cls += ' col-xs-' + (12 - this.labelxs);
43184                 }
43185             }
43186         }
43187
43188         cfg.cn = [
43189             label,
43190             container,
43191             hiddenInput
43192         ];
43193         
43194         var settings = this;
43195
43196         ['xs','sm','md','lg'].map(function(size){
43197             if (settings[size]) {
43198                 cfg.cls += ' col-' + size + '-' + settings[size];
43199             }
43200         });
43201         
43202         return cfg;
43203     },
43204     
43205     initEvents : function()
43206     {
43207         this.indicator = this.indicatorEl();
43208         
43209         this.initCurrencyEvent();
43210         
43211         this.initNumberEvent();
43212     },
43213     
43214     initCurrencyEvent : function()
43215     {
43216         if (!this.store) {
43217             throw "can not find store for combo";
43218         }
43219         
43220         this.store = Roo.factory(this.store, Roo.data);
43221         this.store.parent = this;
43222         
43223         this.createList();
43224         
43225         this.triggerEl = this.el.select('.input-group-addon', true).first();
43226         
43227         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43228         
43229         var _this = this;
43230         
43231         (function(){
43232             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43233             _this.list.setWidth(lw);
43234         }).defer(100);
43235         
43236         this.list.on('mouseover', this.onViewOver, this);
43237         this.list.on('mousemove', this.onViewMove, this);
43238         this.list.on('scroll', this.onViewScroll, this);
43239         
43240         if(!this.tpl){
43241             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43242         }
43243         
43244         this.view = new Roo.View(this.list, this.tpl, {
43245             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43246         });
43247         
43248         this.view.on('click', this.onViewClick, this);
43249         
43250         this.store.on('beforeload', this.onBeforeLoad, this);
43251         this.store.on('load', this.onLoad, this);
43252         this.store.on('loadexception', this.onLoadException, this);
43253         
43254         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43255             "up" : function(e){
43256                 this.inKeyMode = true;
43257                 this.selectPrev();
43258             },
43259
43260             "down" : function(e){
43261                 if(!this.isExpanded()){
43262                     this.onTriggerClick();
43263                 }else{
43264                     this.inKeyMode = true;
43265                     this.selectNext();
43266                 }
43267             },
43268
43269             "enter" : function(e){
43270                 this.collapse();
43271                 
43272                 if(this.fireEvent("specialkey", this, e)){
43273                     this.onViewClick(false);
43274                 }
43275                 
43276                 return true;
43277             },
43278
43279             "esc" : function(e){
43280                 this.collapse();
43281             },
43282
43283             "tab" : function(e){
43284                 this.collapse();
43285                 
43286                 if(this.fireEvent("specialkey", this, e)){
43287                     this.onViewClick(false);
43288                 }
43289                 
43290                 return true;
43291             },
43292
43293             scope : this,
43294
43295             doRelay : function(foo, bar, hname){
43296                 if(hname == 'down' || this.scope.isExpanded()){
43297                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43298                 }
43299                 return true;
43300             },
43301
43302             forceKeyDown: true
43303         });
43304         
43305         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43306         
43307     },
43308     
43309     initNumberEvent : function(e)
43310     {
43311         this.inputEl().on("keydown" , this.fireKey,  this);
43312         this.inputEl().on("focus", this.onFocus,  this);
43313         this.inputEl().on("blur", this.onBlur,  this);
43314         
43315         this.inputEl().relayEvent('keyup', this);
43316         
43317         if(this.indicator){
43318             this.indicator.addClass('invisible');
43319         }
43320  
43321         this.originalValue = this.getValue();
43322         
43323         if(this.validationEvent == 'keyup'){
43324             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43325             this.inputEl().on('keyup', this.filterValidation, this);
43326         }
43327         else if(this.validationEvent !== false){
43328             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43329         }
43330         
43331         if(this.selectOnFocus){
43332             this.on("focus", this.preFocus, this);
43333             
43334         }
43335         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43336             this.inputEl().on("keypress", this.filterKeys, this);
43337         } else {
43338             this.inputEl().relayEvent('keypress', this);
43339         }
43340         
43341         var allowed = "0123456789";
43342         
43343         if(this.allowDecimals){
43344             allowed += this.decimalSeparator;
43345         }
43346         
43347         if(this.allowNegative){
43348             allowed += "-";
43349         }
43350         
43351         if(this.thousandsDelimiter) {
43352             allowed += ",";
43353         }
43354         
43355         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43356         
43357         var keyPress = function(e){
43358             
43359             var k = e.getKey();
43360             
43361             var c = e.getCharCode();
43362             
43363             if(
43364                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43365                     allowed.indexOf(String.fromCharCode(c)) === -1
43366             ){
43367                 e.stopEvent();
43368                 return;
43369             }
43370             
43371             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43372                 return;
43373             }
43374             
43375             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43376                 e.stopEvent();
43377             }
43378         };
43379         
43380         this.inputEl().on("keypress", keyPress, this);
43381         
43382     },
43383     
43384     onTriggerClick : function(e)
43385     {   
43386         if(this.disabled){
43387             return;
43388         }
43389         
43390         this.page = 0;
43391         this.loadNext = false;
43392         
43393         if(this.isExpanded()){
43394             this.collapse();
43395             return;
43396         }
43397         
43398         this.hasFocus = true;
43399         
43400         if(this.triggerAction == 'all') {
43401             this.doQuery(this.allQuery, true);
43402             return;
43403         }
43404         
43405         this.doQuery(this.getRawValue());
43406     },
43407     
43408     getCurrency : function()
43409     {   
43410         var v = this.currencyEl().getValue();
43411         
43412         return v;
43413     },
43414     
43415     restrictHeight : function()
43416     {
43417         this.list.alignTo(this.currencyEl(), this.listAlign);
43418         this.list.alignTo(this.currencyEl(), this.listAlign);
43419     },
43420     
43421     onViewClick : function(view, doFocus, el, e)
43422     {
43423         var index = this.view.getSelectedIndexes()[0];
43424         
43425         var r = this.store.getAt(index);
43426         
43427         if(r){
43428             this.onSelect(r, index);
43429         }
43430     },
43431     
43432     onSelect : function(record, index){
43433         
43434         if(this.fireEvent('beforeselect', this, record, index) !== false){
43435         
43436             this.setFromCurrencyData(index > -1 ? record.data : false);
43437             
43438             this.collapse();
43439             
43440             this.fireEvent('select', this, record, index);
43441         }
43442     },
43443     
43444     setFromCurrencyData : function(o)
43445     {
43446         var currency = '';
43447         
43448         this.lastCurrency = o;
43449         
43450         if (this.currencyField) {
43451             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43452         } else {
43453             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43454         }
43455         
43456         this.lastSelectionText = currency;
43457         
43458         //setting default currency
43459         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43460             this.setCurrency(this.defaultCurrency);
43461             return;
43462         }
43463         
43464         this.setCurrency(currency);
43465     },
43466     
43467     setFromData : function(o)
43468     {
43469         var c = {};
43470         
43471         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43472         
43473         this.setFromCurrencyData(c);
43474         
43475         var value = '';
43476         
43477         if (this.name) {
43478             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43479         } else {
43480             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43481         }
43482         
43483         this.setValue(value);
43484         
43485     },
43486     
43487     setCurrency : function(v)
43488     {   
43489         this.currencyValue = v;
43490         
43491         if(this.rendered){
43492             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43493             this.validate();
43494         }
43495     },
43496     
43497     setValue : function(v)
43498     {
43499         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43500         
43501         this.value = v;
43502         
43503         if(this.rendered){
43504             
43505             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43506             
43507             this.inputEl().dom.value = (v == '') ? '' :
43508                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43509             
43510             if(!this.allowZero && v === '0') {
43511                 this.hiddenEl().dom.value = '';
43512                 this.inputEl().dom.value = '';
43513             }
43514             
43515             this.validate();
43516         }
43517     },
43518     
43519     getRawValue : function()
43520     {
43521         var v = this.inputEl().getValue();
43522         
43523         return v;
43524     },
43525     
43526     getValue : function()
43527     {
43528         return this.fixPrecision(this.parseValue(this.getRawValue()));
43529     },
43530     
43531     parseValue : function(value)
43532     {
43533         if(this.thousandsDelimiter) {
43534             value += "";
43535             r = new RegExp(",", "g");
43536             value = value.replace(r, "");
43537         }
43538         
43539         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43540         return isNaN(value) ? '' : value;
43541         
43542     },
43543     
43544     fixPrecision : function(value)
43545     {
43546         if(this.thousandsDelimiter) {
43547             value += "";
43548             r = new RegExp(",", "g");
43549             value = value.replace(r, "");
43550         }
43551         
43552         var nan = isNaN(value);
43553         
43554         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43555             return nan ? '' : value;
43556         }
43557         return parseFloat(value).toFixed(this.decimalPrecision);
43558     },
43559     
43560     decimalPrecisionFcn : function(v)
43561     {
43562         return Math.floor(v);
43563     },
43564     
43565     validateValue : function(value)
43566     {
43567         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43568             return false;
43569         }
43570         
43571         var num = this.parseValue(value);
43572         
43573         if(isNaN(num)){
43574             this.markInvalid(String.format(this.nanText, value));
43575             return false;
43576         }
43577         
43578         if(num < this.minValue){
43579             this.markInvalid(String.format(this.minText, this.minValue));
43580             return false;
43581         }
43582         
43583         if(num > this.maxValue){
43584             this.markInvalid(String.format(this.maxText, this.maxValue));
43585             return false;
43586         }
43587         
43588         return true;
43589     },
43590     
43591     validate : function()
43592     {
43593         if(this.disabled || this.allowBlank){
43594             this.markValid();
43595             return true;
43596         }
43597         
43598         var currency = this.getCurrency();
43599         
43600         if(this.validateValue(this.getRawValue()) && currency.length){
43601             this.markValid();
43602             return true;
43603         }
43604         
43605         this.markInvalid();
43606         return false;
43607     },
43608     
43609     getName: function()
43610     {
43611         return this.name;
43612     },
43613     
43614     beforeBlur : function()
43615     {
43616         if(!this.castInt){
43617             return;
43618         }
43619         
43620         var v = this.parseValue(this.getRawValue());
43621         
43622         if(v || v == 0){
43623             this.setValue(v);
43624         }
43625     },
43626     
43627     onBlur : function()
43628     {
43629         this.beforeBlur();
43630         
43631         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43632             //this.el.removeClass(this.focusClass);
43633         }
43634         
43635         this.hasFocus = false;
43636         
43637         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43638             this.validate();
43639         }
43640         
43641         var v = this.getValue();
43642         
43643         if(String(v) !== String(this.startValue)){
43644             this.fireEvent('change', this, v, this.startValue);
43645         }
43646         
43647         this.fireEvent("blur", this);
43648     },
43649     
43650     inputEl : function()
43651     {
43652         return this.el.select('.roo-money-amount-input', true).first();
43653     },
43654     
43655     currencyEl : function()
43656     {
43657         return this.el.select('.roo-money-currency-input', true).first();
43658     },
43659     
43660     hiddenEl : function()
43661     {
43662         return this.el.select('input.hidden-number-input',true).first();
43663     }
43664     
43665 });/**
43666  * @class Roo.bootstrap.BezierSignature
43667  * @extends Roo.bootstrap.Component
43668  * Bootstrap BezierSignature class
43669  * This script refer to:
43670  *    Title: Signature Pad
43671  *    Author: szimek
43672  *    Availability: https://github.com/szimek/signature_pad
43673  *
43674  * @constructor
43675  * Create a new BezierSignature
43676  * @param {Object} config The config object
43677  */
43678
43679 Roo.bootstrap.BezierSignature = function(config){
43680     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43681     this.addEvents({
43682         "resize" : true
43683     });
43684 };
43685
43686 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43687 {
43688      
43689     curve_data: [],
43690     
43691     is_empty: true,
43692     
43693     mouse_btn_down: true,
43694     
43695     /**
43696      * @cfg {int} canvas height
43697      */
43698     canvas_height: '200px',
43699     
43700     /**
43701      * @cfg {float|function} Radius of a single dot.
43702      */ 
43703     dot_size: false,
43704     
43705     /**
43706      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43707      */
43708     min_width: 0.5,
43709     
43710     /**
43711      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43712      */
43713     max_width: 2.5,
43714     
43715     /**
43716      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43717      */
43718     throttle: 16,
43719     
43720     /**
43721      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43722      */
43723     min_distance: 5,
43724     
43725     /**
43726      * @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.
43727      */
43728     bg_color: 'rgba(0, 0, 0, 0)',
43729     
43730     /**
43731      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43732      */
43733     dot_color: 'black',
43734     
43735     /**
43736      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43737      */ 
43738     velocity_filter_weight: 0.7,
43739     
43740     /**
43741      * @cfg {function} Callback when stroke begin. 
43742      */
43743     onBegin: false,
43744     
43745     /**
43746      * @cfg {function} Callback when stroke end.
43747      */
43748     onEnd: false,
43749     
43750     getAutoCreate : function()
43751     {
43752         var cls = 'roo-signature column';
43753         
43754         if(this.cls){
43755             cls += ' ' + this.cls;
43756         }
43757         
43758         var col_sizes = [
43759             'lg',
43760             'md',
43761             'sm',
43762             'xs'
43763         ];
43764         
43765         for(var i = 0; i < col_sizes.length; i++) {
43766             if(this[col_sizes[i]]) {
43767                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43768             }
43769         }
43770         
43771         var cfg = {
43772             tag: 'div',
43773             cls: cls,
43774             cn: [
43775                 {
43776                     tag: 'div',
43777                     cls: 'roo-signature-body',
43778                     cn: [
43779                         {
43780                             tag: 'canvas',
43781                             cls: 'roo-signature-body-canvas',
43782                             height: this.canvas_height,
43783                             width: this.canvas_width
43784                         }
43785                     ]
43786                 },
43787                 {
43788                     tag: 'input',
43789                     type: 'file',
43790                     style: 'display: none'
43791                 }
43792             ]
43793         };
43794         
43795         return cfg;
43796     },
43797     
43798     initEvents: function() 
43799     {
43800         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43801         
43802         var canvas = this.canvasEl();
43803         
43804         // mouse && touch event swapping...
43805         canvas.dom.style.touchAction = 'none';
43806         canvas.dom.style.msTouchAction = 'none';
43807         
43808         this.mouse_btn_down = false;
43809         canvas.on('mousedown', this._handleMouseDown, this);
43810         canvas.on('mousemove', this._handleMouseMove, this);
43811         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43812         
43813         if (window.PointerEvent) {
43814             canvas.on('pointerdown', this._handleMouseDown, this);
43815             canvas.on('pointermove', this._handleMouseMove, this);
43816             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43817         }
43818         
43819         if ('ontouchstart' in window) {
43820             canvas.on('touchstart', this._handleTouchStart, this);
43821             canvas.on('touchmove', this._handleTouchMove, this);
43822             canvas.on('touchend', this._handleTouchEnd, this);
43823         }
43824         
43825         Roo.EventManager.onWindowResize(this.resize, this, true);
43826         
43827         // file input event
43828         this.fileEl().on('change', this.uploadImage, this);
43829         
43830         this.clear();
43831         
43832         this.resize();
43833     },
43834     
43835     resize: function(){
43836         
43837         var canvas = this.canvasEl().dom;
43838         var ctx = this.canvasElCtx();
43839         var img_data = false;
43840         
43841         if(canvas.width > 0) {
43842             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43843         }
43844         // setting canvas width will clean img data
43845         canvas.width = 0;
43846         
43847         var style = window.getComputedStyle ? 
43848             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43849             
43850         var padding_left = parseInt(style.paddingLeft) || 0;
43851         var padding_right = parseInt(style.paddingRight) || 0;
43852         
43853         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43854         
43855         if(img_data) {
43856             ctx.putImageData(img_data, 0, 0);
43857         }
43858     },
43859     
43860     _handleMouseDown: function(e)
43861     {
43862         if (e.browserEvent.which === 1) {
43863             this.mouse_btn_down = true;
43864             this.strokeBegin(e);
43865         }
43866     },
43867     
43868     _handleMouseMove: function (e)
43869     {
43870         if (this.mouse_btn_down) {
43871             this.strokeMoveUpdate(e);
43872         }
43873     },
43874     
43875     _handleMouseUp: function (e)
43876     {
43877         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43878             this.mouse_btn_down = false;
43879             this.strokeEnd(e);
43880         }
43881     },
43882     
43883     _handleTouchStart: function (e) {
43884         
43885         e.preventDefault();
43886         if (e.browserEvent.targetTouches.length === 1) {
43887             // var touch = e.browserEvent.changedTouches[0];
43888             // this.strokeBegin(touch);
43889             
43890              this.strokeBegin(e); // assume e catching the correct xy...
43891         }
43892     },
43893     
43894     _handleTouchMove: function (e) {
43895         e.preventDefault();
43896         // var touch = event.targetTouches[0];
43897         // _this._strokeMoveUpdate(touch);
43898         this.strokeMoveUpdate(e);
43899     },
43900     
43901     _handleTouchEnd: function (e) {
43902         var wasCanvasTouched = e.target === this.canvasEl().dom;
43903         if (wasCanvasTouched) {
43904             e.preventDefault();
43905             // var touch = event.changedTouches[0];
43906             // _this._strokeEnd(touch);
43907             this.strokeEnd(e);
43908         }
43909     },
43910     
43911     reset: function () {
43912         this._lastPoints = [];
43913         this._lastVelocity = 0;
43914         this._lastWidth = (this.min_width + this.max_width) / 2;
43915         this.canvasElCtx().fillStyle = this.dot_color;
43916     },
43917     
43918     strokeMoveUpdate: function(e)
43919     {
43920         this.strokeUpdate(e);
43921         
43922         if (this.throttle) {
43923             this.throttleStroke(this.strokeUpdate, this.throttle);
43924         }
43925         else {
43926             this.strokeUpdate(e);
43927         }
43928     },
43929     
43930     strokeBegin: function(e)
43931     {
43932         var newPointGroup = {
43933             color: this.dot_color,
43934             points: []
43935         };
43936         
43937         if (typeof this.onBegin === 'function') {
43938             this.onBegin(e);
43939         }
43940         
43941         this.curve_data.push(newPointGroup);
43942         this.reset();
43943         this.strokeUpdate(e);
43944     },
43945     
43946     strokeUpdate: function(e)
43947     {
43948         var rect = this.canvasEl().dom.getBoundingClientRect();
43949         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43950         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43951         var lastPoints = lastPointGroup.points;
43952         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43953         var isLastPointTooClose = lastPoint
43954             ? point.distanceTo(lastPoint) <= this.min_distance
43955             : false;
43956         var color = lastPointGroup.color;
43957         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43958             var curve = this.addPoint(point);
43959             if (!lastPoint) {
43960                 this.drawDot({color: color, point: point});
43961             }
43962             else if (curve) {
43963                 this.drawCurve({color: color, curve: curve});
43964             }
43965             lastPoints.push({
43966                 time: point.time,
43967                 x: point.x,
43968                 y: point.y
43969             });
43970         }
43971     },
43972     
43973     strokeEnd: function(e)
43974     {
43975         this.strokeUpdate(e);
43976         if (typeof this.onEnd === 'function') {
43977             this.onEnd(e);
43978         }
43979     },
43980     
43981     addPoint:  function (point) {
43982         var _lastPoints = this._lastPoints;
43983         _lastPoints.push(point);
43984         if (_lastPoints.length > 2) {
43985             if (_lastPoints.length === 3) {
43986                 _lastPoints.unshift(_lastPoints[0]);
43987             }
43988             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43989             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43990             _lastPoints.shift();
43991             return curve;
43992         }
43993         return null;
43994     },
43995     
43996     calculateCurveWidths: function (startPoint, endPoint) {
43997         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43998             (1 - this.velocity_filter_weight) * this._lastVelocity;
43999
44000         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44001         var widths = {
44002             end: newWidth,
44003             start: this._lastWidth
44004         };
44005         
44006         this._lastVelocity = velocity;
44007         this._lastWidth = newWidth;
44008         return widths;
44009     },
44010     
44011     drawDot: function (_a) {
44012         var color = _a.color, point = _a.point;
44013         var ctx = this.canvasElCtx();
44014         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44015         ctx.beginPath();
44016         this.drawCurveSegment(point.x, point.y, width);
44017         ctx.closePath();
44018         ctx.fillStyle = color;
44019         ctx.fill();
44020     },
44021     
44022     drawCurve: function (_a) {
44023         var color = _a.color, curve = _a.curve;
44024         var ctx = this.canvasElCtx();
44025         var widthDelta = curve.endWidth - curve.startWidth;
44026         var drawSteps = Math.floor(curve.length()) * 2;
44027         ctx.beginPath();
44028         ctx.fillStyle = color;
44029         for (var i = 0; i < drawSteps; i += 1) {
44030         var t = i / drawSteps;
44031         var tt = t * t;
44032         var ttt = tt * t;
44033         var u = 1 - t;
44034         var uu = u * u;
44035         var uuu = uu * u;
44036         var x = uuu * curve.startPoint.x;
44037         x += 3 * uu * t * curve.control1.x;
44038         x += 3 * u * tt * curve.control2.x;
44039         x += ttt * curve.endPoint.x;
44040         var y = uuu * curve.startPoint.y;
44041         y += 3 * uu * t * curve.control1.y;
44042         y += 3 * u * tt * curve.control2.y;
44043         y += ttt * curve.endPoint.y;
44044         var width = curve.startWidth + ttt * widthDelta;
44045         this.drawCurveSegment(x, y, width);
44046         }
44047         ctx.closePath();
44048         ctx.fill();
44049     },
44050     
44051     drawCurveSegment: function (x, y, width) {
44052         var ctx = this.canvasElCtx();
44053         ctx.moveTo(x, y);
44054         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44055         this.is_empty = false;
44056     },
44057     
44058     clear: function()
44059     {
44060         var ctx = this.canvasElCtx();
44061         var canvas = this.canvasEl().dom;
44062         ctx.fillStyle = this.bg_color;
44063         ctx.clearRect(0, 0, canvas.width, canvas.height);
44064         ctx.fillRect(0, 0, canvas.width, canvas.height);
44065         this.curve_data = [];
44066         this.reset();
44067         this.is_empty = true;
44068     },
44069     
44070     fileEl: function()
44071     {
44072         return  this.el.select('input',true).first();
44073     },
44074     
44075     canvasEl: function()
44076     {
44077         return this.el.select('canvas',true).first();
44078     },
44079     
44080     canvasElCtx: function()
44081     {
44082         return this.el.select('canvas',true).first().dom.getContext('2d');
44083     },
44084     
44085     getImage: function(type)
44086     {
44087         if(this.is_empty) {
44088             return false;
44089         }
44090         
44091         // encryption ?
44092         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44093     },
44094     
44095     drawFromImage: function(img_src)
44096     {
44097         var img = new Image();
44098         
44099         img.onload = function(){
44100             this.canvasElCtx().drawImage(img, 0, 0);
44101         }.bind(this);
44102         
44103         img.src = img_src;
44104         
44105         this.is_empty = false;
44106     },
44107     
44108     selectImage: function()
44109     {
44110         this.fileEl().dom.click();
44111     },
44112     
44113     uploadImage: function(e)
44114     {
44115         var reader = new FileReader();
44116         
44117         reader.onload = function(e){
44118             var img = new Image();
44119             img.onload = function(){
44120                 this.reset();
44121                 this.canvasElCtx().drawImage(img, 0, 0);
44122             }.bind(this);
44123             img.src = e.target.result;
44124         }.bind(this);
44125         
44126         reader.readAsDataURL(e.target.files[0]);
44127     },
44128     
44129     // Bezier Point Constructor
44130     Point: (function () {
44131         function Point(x, y, time) {
44132             this.x = x;
44133             this.y = y;
44134             this.time = time || Date.now();
44135         }
44136         Point.prototype.distanceTo = function (start) {
44137             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44138         };
44139         Point.prototype.equals = function (other) {
44140             return this.x === other.x && this.y === other.y && this.time === other.time;
44141         };
44142         Point.prototype.velocityFrom = function (start) {
44143             return this.time !== start.time
44144             ? this.distanceTo(start) / (this.time - start.time)
44145             : 0;
44146         };
44147         return Point;
44148     }()),
44149     
44150     
44151     // Bezier Constructor
44152     Bezier: (function () {
44153         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44154             this.startPoint = startPoint;
44155             this.control2 = control2;
44156             this.control1 = control1;
44157             this.endPoint = endPoint;
44158             this.startWidth = startWidth;
44159             this.endWidth = endWidth;
44160         }
44161         Bezier.fromPoints = function (points, widths, scope) {
44162             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44163             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44164             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44165         };
44166         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44167             var dx1 = s1.x - s2.x;
44168             var dy1 = s1.y - s2.y;
44169             var dx2 = s2.x - s3.x;
44170             var dy2 = s2.y - s3.y;
44171             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44172             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44173             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44174             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44175             var dxm = m1.x - m2.x;
44176             var dym = m1.y - m2.y;
44177             var k = l2 / (l1 + l2);
44178             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44179             var tx = s2.x - cm.x;
44180             var ty = s2.y - cm.y;
44181             return {
44182                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44183                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44184             };
44185         };
44186         Bezier.prototype.length = function () {
44187             var steps = 10;
44188             var length = 0;
44189             var px;
44190             var py;
44191             for (var i = 0; i <= steps; i += 1) {
44192                 var t = i / steps;
44193                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44194                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44195                 if (i > 0) {
44196                     var xdiff = cx - px;
44197                     var ydiff = cy - py;
44198                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44199                 }
44200                 px = cx;
44201                 py = cy;
44202             }
44203             return length;
44204         };
44205         Bezier.prototype.point = function (t, start, c1, c2, end) {
44206             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44207             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44208             + (3.0 * c2 * (1.0 - t) * t * t)
44209             + (end * t * t * t);
44210         };
44211         return Bezier;
44212     }()),
44213     
44214     throttleStroke: function(fn, wait) {
44215       if (wait === void 0) { wait = 250; }
44216       var previous = 0;
44217       var timeout = null;
44218       var result;
44219       var storedContext;
44220       var storedArgs;
44221       var later = function () {
44222           previous = Date.now();
44223           timeout = null;
44224           result = fn.apply(storedContext, storedArgs);
44225           if (!timeout) {
44226               storedContext = null;
44227               storedArgs = [];
44228           }
44229       };
44230       return function wrapper() {
44231           var args = [];
44232           for (var _i = 0; _i < arguments.length; _i++) {
44233               args[_i] = arguments[_i];
44234           }
44235           var now = Date.now();
44236           var remaining = wait - (now - previous);
44237           storedContext = this;
44238           storedArgs = args;
44239           if (remaining <= 0 || remaining > wait) {
44240               if (timeout) {
44241                   clearTimeout(timeout);
44242                   timeout = null;
44243               }
44244               previous = now;
44245               result = fn.apply(storedContext, storedArgs);
44246               if (!timeout) {
44247                   storedContext = null;
44248                   storedArgs = [];
44249               }
44250           }
44251           else if (!timeout) {
44252               timeout = window.setTimeout(later, remaining);
44253           }
44254           return result;
44255       };
44256   }
44257   
44258 });
44259
44260  
44261
44262