roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         
2523         var dom = move_card.el.dom;
2524         dom.parentNode.removeChild(dom);
2525         dom.style.width = ''; // clear with - which is set by drag.
2526         
2527         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528             var cardel = next_to_card.el.dom;
2529             
2530             if (position == 'above' ) {
2531                 cardel.parentNode.insertBefore(dom, cardel);
2532             } else if (cardel.nextSibling) {
2533                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534             } else {
2535                 cardel.parentNode.append(dom);
2536             }
2537         } else {
2538             // card container???
2539             this.containerEl.dom.append(dom);
2540         }
2541         
2542         //FIXME HANDLE card = true 
2543         
2544         // add this to the correct place in items.
2545         
2546         
2547         
2548         // remove Card from items.
2549         
2550         var old_parent = move_card.parent();
2551         
2552         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553         
2554         if (this.items.length) {
2555             var nitems = [];
2556             //Roo.log([info.items_n, info.position, this.items.length]);
2557             for (var i =0; i < this.items.length; i++) {
2558                 if (i == to_items_n && position == 'above') {
2559                     nitems.push(move_card);
2560                 }
2561                 nitems.push(this.items[i]);
2562                 if (i == to_items_n && position == 'below') {
2563                     nitems.push(move_card);
2564                 }
2565             }
2566             this.items = nitems;
2567             Roo.log(this.items);
2568         } else {
2569             this.items.push(move_card);
2570         }
2571         
2572         move_card.parentId = this.id;
2573         
2574         return true;
2575         
2576         
2577     },
2578     
2579     
2580     /**    Decide whether to drop above or below a View node. */
2581     getDropPoint : function(e, n, dd)
2582     {
2583         if (dd) {
2584              return false;
2585         }
2586         if (n == this.containerEl.dom) {
2587             return "above";
2588         }
2589         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590         var c = t + (b - t) / 2;
2591         var y = Roo.lib.Event.getPageY(e);
2592         if(y <= c) {
2593             return "above";
2594         }else{
2595             return "below";
2596         }
2597     },
2598     onToggleCollapse : function(e)
2599         {
2600         if (this.collapsed) {
2601             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602             this.collapsableEl.addClass('show');
2603             this.collapsed = false;
2604             return;
2605         }
2606         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607         this.collapsableEl.removeClass('show');
2608         this.collapsed = true;
2609         
2610     
2611     },
2612     
2613     onToggleRotate : function(e)
2614     {
2615         this.collapsableEl.removeClass('show');
2616         this.footerEl.removeClass('d-none');
2617         this.el.removeClass('roo-card-rotated');
2618         this.el.removeClass('d-none');
2619         if (this.rotated) {
2620             
2621             this.collapsableEl.addClass('show');
2622             this.rotated = false;
2623             this.fireEvent('rotate', this, this.rotated);
2624             return;
2625         }
2626         this.el.addClass('roo-card-rotated');
2627         this.footerEl.addClass('d-none');
2628         this.el.select('.roo-collapsable').removeClass('show');
2629         
2630         this.rotated = true;
2631         this.fireEvent('rotate', this, this.rotated);
2632     
2633     },
2634     
2635     dropPlaceHolder: function (action, info, data)
2636     {
2637         if (this.dropEl === false) {
2638             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2639             cls : 'd-none'
2640             },true);
2641         }
2642         this.dropEl.removeClass(['d-none', 'd-block']);        
2643         if (action == 'hide') {
2644             
2645             this.dropEl.addClass('d-none');
2646             return;
2647         }
2648         // FIXME - info.card == true!!!
2649         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650         
2651         if (info.card !== true) {
2652             var cardel = info.card.el.dom;
2653             
2654             if (info.position == 'above') {
2655                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656             } else if (cardel.nextSibling) {
2657                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658             } else {
2659                 cardel.parentNode.append(this.dropEl.dom);
2660             }
2661         } else {
2662             // card container???
2663             this.containerEl.dom.append(this.dropEl.dom);
2664         }
2665         
2666         this.dropEl.addClass('d-block roo-card-dropzone');
2667         
2668         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2669         
2670         
2671     
2672     
2673     
2674     },
2675     setHeaderText: function(html)
2676     {
2677         this.headerContainerEl.dom.innerHTML = html;
2678     }
2679
2680     
2681 });
2682
2683 /*
2684  * - LGPL
2685  *
2686  * Card header - holder for the card header elements.
2687  * 
2688  */
2689
2690 /**
2691  * @class Roo.bootstrap.CardHeader
2692  * @extends Roo.bootstrap.Element
2693  * Bootstrap CardHeader class
2694  * @constructor
2695  * Create a new Card Header - that you can embed children into
2696  * @param {Object} config The config object
2697  */
2698
2699 Roo.bootstrap.CardHeader = function(config){
2700     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2701 };
2702
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2704     
2705     
2706     container_method : 'getCardHeader' 
2707     
2708      
2709     
2710     
2711    
2712 });
2713
2714  
2715
2716  /*
2717  * - LGPL
2718  *
2719  * Card footer - holder for the card footer elements.
2720  * 
2721  */
2722
2723 /**
2724  * @class Roo.bootstrap.CardFooter
2725  * @extends Roo.bootstrap.Element
2726  * Bootstrap CardFooter class
2727  * @constructor
2728  * Create a new Card Footer - that you can embed children into
2729  * @param {Object} config The config object
2730  */
2731
2732 Roo.bootstrap.CardFooter = function(config){
2733     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2734 };
2735
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2737     
2738     
2739     container_method : 'getCardFooter' 
2740     
2741      
2742     
2743     
2744    
2745 });
2746
2747  
2748
2749  /*
2750  * - LGPL
2751  *
2752  * Card header - holder for the card header elements.
2753  * 
2754  */
2755
2756 /**
2757  * @class Roo.bootstrap.CardImageTop
2758  * @extends Roo.bootstrap.Element
2759  * Bootstrap CardImageTop class
2760  * @constructor
2761  * Create a new Card Image Top container
2762  * @param {Object} config The config object
2763  */
2764
2765 Roo.bootstrap.CardImageTop = function(config){
2766     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2767 };
2768
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2770     
2771    
2772     container_method : 'getCardImageTop' 
2773     
2774      
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * image
2785  * 
2786  */
2787
2788
2789 /**
2790  * @class Roo.bootstrap.Img
2791  * @extends Roo.bootstrap.Component
2792  * Bootstrap Img class
2793  * @cfg {Boolean} imgResponsive false | true
2794  * @cfg {String} border rounded | circle | thumbnail
2795  * @cfg {String} src image source
2796  * @cfg {String} alt image alternative text
2797  * @cfg {String} href a tag href
2798  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799  * @cfg {String} xsUrl xs image source
2800  * @cfg {String} smUrl sm image source
2801  * @cfg {String} mdUrl md image source
2802  * @cfg {String} lgUrl lg image source
2803  * 
2804  * @constructor
2805  * Create a new Input
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.Img = function(config){
2810     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2811     
2812     this.addEvents({
2813         // img events
2814         /**
2815          * @event click
2816          * The img click event for the img.
2817          * @param {Roo.EventObject} e
2818          */
2819         "click" : true
2820     });
2821 };
2822
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2824     
2825     imgResponsive: true,
2826     border: '',
2827     src: 'about:blank',
2828     href: false,
2829     target: false,
2830     xsUrl: '',
2831     smUrl: '',
2832     mdUrl: '',
2833     lgUrl: '',
2834
2835     getAutoCreate : function()
2836     {   
2837         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838             return this.createSingleImg();
2839         }
2840         
2841         var cfg = {
2842             tag: 'div',
2843             cls: 'roo-image-responsive-group',
2844             cn: []
2845         };
2846         var _this = this;
2847         
2848         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849             
2850             if(!_this[size + 'Url']){
2851                 return;
2852             }
2853             
2854             var img = {
2855                 tag: 'img',
2856                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857                 html: _this.html || cfg.html,
2858                 src: _this[size + 'Url']
2859             };
2860             
2861             img.cls += ' roo-image-responsive-' + size;
2862             
2863             var s = ['xs', 'sm', 'md', 'lg'];
2864             
2865             s.splice(s.indexOf(size), 1);
2866             
2867             Roo.each(s, function(ss){
2868                 img.cls += ' hidden-' + ss;
2869             });
2870             
2871             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872                 cfg.cls += ' img-' + _this.border;
2873             }
2874             
2875             if(_this.alt){
2876                 cfg.alt = _this.alt;
2877             }
2878             
2879             if(_this.href){
2880                 var a = {
2881                     tag: 'a',
2882                     href: _this.href,
2883                     cn: [
2884                         img
2885                     ]
2886                 };
2887
2888                 if(this.target){
2889                     a.target = _this.target;
2890                 }
2891             }
2892             
2893             cfg.cn.push((_this.href) ? a : img);
2894             
2895         });
2896         
2897         return cfg;
2898     },
2899     
2900     createSingleImg : function()
2901     {
2902         var cfg = {
2903             tag: 'img',
2904             cls: (this.imgResponsive) ? 'img-responsive' : '',
2905             html : null,
2906             src : 'about:blank'  // just incase src get's set to undefined?!?
2907         };
2908         
2909         cfg.html = this.html || cfg.html;
2910         
2911         cfg.src = this.src || cfg.src;
2912         
2913         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914             cfg.cls += ' img-' + this.border;
2915         }
2916         
2917         if(this.alt){
2918             cfg.alt = this.alt;
2919         }
2920         
2921         if(this.href){
2922             var a = {
2923                 tag: 'a',
2924                 href: this.href,
2925                 cn: [
2926                     cfg
2927                 ]
2928             };
2929             
2930             if(this.target){
2931                 a.target = this.target;
2932             }
2933             
2934         }
2935         
2936         return (this.href) ? a : cfg;
2937     },
2938     
2939     initEvents: function() 
2940     {
2941         if(!this.href){
2942             this.el.on('click', this.onClick, this);
2943         }
2944         
2945     },
2946     
2947     onClick : function(e)
2948     {
2949         Roo.log('img onclick');
2950         this.fireEvent('click', this, e);
2951     },
2952     /**
2953      * Sets the url of the image - used to update it
2954      * @param {String} url the url of the image
2955      */
2956     
2957     setSrc : function(url)
2958     {
2959         this.src =  url;
2960         
2961         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962             this.el.dom.src =  url;
2963             return;
2964         }
2965         
2966         this.el.select('img', true).first().dom.src =  url;
2967     }
2968     
2969     
2970    
2971 });
2972
2973  /*
2974  * - LGPL
2975  *
2976  * image
2977  * 
2978  */
2979
2980
2981 /**
2982  * @class Roo.bootstrap.Link
2983  * @extends Roo.bootstrap.Component
2984  * Bootstrap Link Class
2985  * @cfg {String} alt image alternative text
2986  * @cfg {String} href a tag href
2987  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988  * @cfg {String} html the content of the link.
2989  * @cfg {String} anchor name for the anchor link
2990  * @cfg {String} fa - favicon
2991
2992  * @cfg {Boolean} preventDefault (true | false) default false
2993
2994  * 
2995  * @constructor
2996  * Create a new Input
2997  * @param {Object} config The config object
2998  */
2999
3000 Roo.bootstrap.Link = function(config){
3001     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3002     
3003     this.addEvents({
3004         // img events
3005         /**
3006          * @event click
3007          * The img click event for the img.
3008          * @param {Roo.EventObject} e
3009          */
3010         "click" : true
3011     });
3012 };
3013
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3015     
3016     href: false,
3017     target: false,
3018     preventDefault: false,
3019     anchor : false,
3020     alt : false,
3021     fa: false,
3022
3023
3024     getAutoCreate : function()
3025     {
3026         var html = this.html || '';
3027         
3028         if (this.fa !== false) {
3029             html = '<i class="fa fa-' + this.fa + '"></i>';
3030         }
3031         var cfg = {
3032             tag: 'a'
3033         };
3034         // anchor's do not require html/href...
3035         if (this.anchor === false) {
3036             cfg.html = html;
3037             cfg.href = this.href || '#';
3038         } else {
3039             cfg.name = this.anchor;
3040             if (this.html !== false || this.fa !== false) {
3041                 cfg.html = html;
3042             }
3043             if (this.href !== false) {
3044                 cfg.href = this.href;
3045             }
3046         }
3047         
3048         if(this.alt !== false){
3049             cfg.alt = this.alt;
3050         }
3051         
3052         
3053         if(this.target !== false) {
3054             cfg.target = this.target;
3055         }
3056         
3057         return cfg;
3058     },
3059     
3060     initEvents: function() {
3061         
3062         if(!this.href || this.preventDefault){
3063             this.el.on('click', this.onClick, this);
3064         }
3065     },
3066     
3067     onClick : function(e)
3068     {
3069         if(this.preventDefault){
3070             e.preventDefault();
3071         }
3072         //Roo.log('img onclick');
3073         this.fireEvent('click', this, e);
3074     }
3075    
3076 });
3077
3078  /*
3079  * - LGPL
3080  *
3081  * header
3082  * 
3083  */
3084
3085 /**
3086  * @class Roo.bootstrap.Header
3087  * @extends Roo.bootstrap.Component
3088  * Bootstrap Header class
3089  * @cfg {String} html content of header
3090  * @cfg {Number} level (1|2|3|4|5|6) default 1
3091  * 
3092  * @constructor
3093  * Create a new Header
3094  * @param {Object} config The config object
3095  */
3096
3097
3098 Roo.bootstrap.Header  = function(config){
3099     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3103     
3104     //href : false,
3105     html : false,
3106     level : 1,
3107     
3108     
3109     
3110     getAutoCreate : function(){
3111         
3112         
3113         
3114         var cfg = {
3115             tag: 'h' + (1 *this.level),
3116             html: this.html || ''
3117         } ;
3118         
3119         return cfg;
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * Based on:
3128  * Ext JS Library 1.1.1
3129  * Copyright(c) 2006-2007, Ext JS, LLC.
3130  *
3131  * Originally Released Under LGPL - original licence link has changed is not relivant.
3132  *
3133  * Fork - LGPL
3134  * <script type="text/javascript">
3135  */
3136  
3137 /**
3138  * @class Roo.bootstrap.MenuMgr
3139  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3140  * @singleton
3141  */
3142 Roo.bootstrap.MenuMgr = function(){
3143    var menus, active, groups = {}, attached = false, lastShow = new Date();
3144
3145    // private - called when first menu is created
3146    function init(){
3147        menus = {};
3148        active = new Roo.util.MixedCollection();
3149        Roo.get(document).addKeyListener(27, function(){
3150            if(active.length > 0){
3151                hideAll();
3152            }
3153        });
3154    }
3155
3156    // private
3157    function hideAll(){
3158        if(active && active.length > 0){
3159            var c = active.clone();
3160            c.each(function(m){
3161                m.hide();
3162            });
3163        }
3164    }
3165
3166    // private
3167    function onHide(m){
3168        active.remove(m);
3169        if(active.length < 1){
3170            Roo.get(document).un("mouseup", onMouseDown);
3171             
3172            attached = false;
3173        }
3174    }
3175
3176    // private
3177    function onShow(m){
3178        var last = active.last();
3179        lastShow = new Date();
3180        active.add(m);
3181        if(!attached){
3182           Roo.get(document).on("mouseup", onMouseDown);
3183            
3184            attached = true;
3185        }
3186        if(m.parentMenu){
3187           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188           m.parentMenu.activeChild = m;
3189        }else if(last && last.isVisible()){
3190           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3191        }
3192    }
3193
3194    // private
3195    function onBeforeHide(m){
3196        if(m.activeChild){
3197            m.activeChild.hide();
3198        }
3199        if(m.autoHideTimer){
3200            clearTimeout(m.autoHideTimer);
3201            delete m.autoHideTimer;
3202        }
3203    }
3204
3205    // private
3206    function onBeforeShow(m){
3207        var pm = m.parentMenu;
3208        if(!pm && !m.allowOtherMenus){
3209            hideAll();
3210        }else if(pm && pm.activeChild && active != m){
3211            pm.activeChild.hide();
3212        }
3213    }
3214
3215    // private this should really trigger on mouseup..
3216    function onMouseDown(e){
3217         Roo.log("on Mouse Up");
3218         
3219         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220             Roo.log("MenuManager hideAll");
3221             hideAll();
3222             e.stopEvent();
3223         }
3224         
3225         
3226    }
3227
3228    // private
3229    function onBeforeCheck(mi, state){
3230        if(state){
3231            var g = groups[mi.group];
3232            for(var i = 0, l = g.length; i < l; i++){
3233                if(g[i] != mi){
3234                    g[i].setChecked(false);
3235                }
3236            }
3237        }
3238    }
3239
3240    return {
3241
3242        /**
3243         * Hides all menus that are currently visible
3244         */
3245        hideAll : function(){
3246             hideAll();  
3247        },
3248
3249        // private
3250        register : function(menu){
3251            if(!menus){
3252                init();
3253            }
3254            menus[menu.id] = menu;
3255            menu.on("beforehide", onBeforeHide);
3256            menu.on("hide", onHide);
3257            menu.on("beforeshow", onBeforeShow);
3258            menu.on("show", onShow);
3259            var g = menu.group;
3260            if(g && menu.events["checkchange"]){
3261                if(!groups[g]){
3262                    groups[g] = [];
3263                }
3264                groups[g].push(menu);
3265                menu.on("checkchange", onCheck);
3266            }
3267        },
3268
3269         /**
3270          * Returns a {@link Roo.menu.Menu} object
3271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272          * be used to generate and return a new Menu instance.
3273          */
3274        get : function(menu){
3275            if(typeof menu == "string"){ // menu id
3276                return menus[menu];
3277            }else if(menu.events){  // menu instance
3278                return menu;
3279            }
3280            /*else if(typeof menu.length == 'number'){ // array of menu items?
3281                return new Roo.bootstrap.Menu({items:menu});
3282            }else{ // otherwise, must be a config
3283                return new Roo.bootstrap.Menu(menu);
3284            }
3285            */
3286            return false;
3287        },
3288
3289        // private
3290        unregister : function(menu){
3291            delete menus[menu.id];
3292            menu.un("beforehide", onBeforeHide);
3293            menu.un("hide", onHide);
3294            menu.un("beforeshow", onBeforeShow);
3295            menu.un("show", onShow);
3296            var g = menu.group;
3297            if(g && menu.events["checkchange"]){
3298                groups[g].remove(menu);
3299                menu.un("checkchange", onCheck);
3300            }
3301        },
3302
3303        // private
3304        registerCheckable : function(menuItem){
3305            var g = menuItem.group;
3306            if(g){
3307                if(!groups[g]){
3308                    groups[g] = [];
3309                }
3310                groups[g].push(menuItem);
3311                menuItem.on("beforecheckchange", onBeforeCheck);
3312            }
3313        },
3314
3315        // private
3316        unregisterCheckable : function(menuItem){
3317            var g = menuItem.group;
3318            if(g){
3319                groups[g].remove(menuItem);
3320                menuItem.un("beforecheckchange", onBeforeCheck);
3321            }
3322        }
3323    };
3324 }();/*
3325  * - LGPL
3326  *
3327  * menu
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Menu
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Menu class - container for MenuItems
3335  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3337  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3338  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3339  * 
3340  * @constructor
3341  * Create a new Menu
3342  * @param {Object} config The config object
3343  */
3344
3345
3346 Roo.bootstrap.Menu = function(config){
3347     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348     if (this.registerMenu && this.type != 'treeview')  {
3349         Roo.bootstrap.MenuMgr.register(this);
3350     }
3351     
3352     
3353     this.addEvents({
3354         /**
3355          * @event beforeshow
3356          * Fires before this menu is displayed (return false to block)
3357          * @param {Roo.menu.Menu} this
3358          */
3359         beforeshow : true,
3360         /**
3361          * @event beforehide
3362          * Fires before this menu is hidden (return false to block)
3363          * @param {Roo.menu.Menu} this
3364          */
3365         beforehide : true,
3366         /**
3367          * @event show
3368          * Fires after this menu is displayed
3369          * @param {Roo.menu.Menu} this
3370          */
3371         show : true,
3372         /**
3373          * @event hide
3374          * Fires after this menu is hidden
3375          * @param {Roo.menu.Menu} this
3376          */
3377         hide : true,
3378         /**
3379          * @event click
3380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381          * @param {Roo.menu.Menu} this
3382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383          * @param {Roo.EventObject} e
3384          */
3385         click : true,
3386         /**
3387          * @event mouseover
3388          * Fires when the mouse is hovering over this menu
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.EventObject} e
3391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3392          */
3393         mouseover : true,
3394         /**
3395          * @event mouseout
3396          * Fires when the mouse exits this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseout : true,
3402         /**
3403          * @event itemclick
3404          * Fires when a menu item contained in this menu is clicked
3405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406          * @param {Roo.EventObject} e
3407          */
3408         itemclick: true
3409     });
3410     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3411 };
3412
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3414     
3415    /// html : false,
3416     //align : '',
3417     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3418     type: false,
3419     /**
3420      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421      */
3422     registerMenu : true,
3423     
3424     menuItems :false, // stores the menu items..
3425     
3426     hidden:true,
3427         
3428     parentMenu : false,
3429     
3430     stopEvent : true,
3431     
3432     isLink : false,
3433     
3434     getChildContainer : function() {
3435         return this.el;  
3436     },
3437     
3438     getAutoCreate : function(){
3439          
3440         //if (['right'].indexOf(this.align)!==-1) {
3441         //    cfg.cn[1].cls += ' pull-right'
3442         //}
3443         
3444         
3445         var cfg = {
3446             tag : 'ul',
3447             cls : 'dropdown-menu' ,
3448             style : 'z-index:1000'
3449             
3450         };
3451         
3452         if (this.type === 'submenu') {
3453             cfg.cls = 'submenu active';
3454         }
3455         if (this.type === 'treeview') {
3456             cfg.cls = 'treeview-menu';
3457         }
3458         
3459         return cfg;
3460     },
3461     initEvents : function() {
3462         
3463        // Roo.log("ADD event");
3464        // Roo.log(this.triggerEl.dom);
3465         
3466         this.triggerEl.on('click', this.onTriggerClick, this);
3467         
3468         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3469         
3470         
3471         if (this.triggerEl.hasClass('nav-item')) {
3472             // dropdown toggle on the 'a' in BS4?
3473             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474         } else {
3475             this.triggerEl.addClass('dropdown-toggle');
3476         }
3477         if (Roo.isTouch) {
3478             this.el.on('touchstart'  , this.onTouch, this);
3479         }
3480         this.el.on('click' , this.onClick, this);
3481
3482         this.el.on("mouseover", this.onMouseOver, this);
3483         this.el.on("mouseout", this.onMouseOut, this);
3484         
3485     },
3486     
3487     findTargetItem : function(e)
3488     {
3489         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3490         if(!t){
3491             return false;
3492         }
3493         //Roo.log(t);         Roo.log(t.id);
3494         if(t && t.id){
3495             //Roo.log(this.menuitems);
3496             return this.menuitems.get(t.id);
3497             
3498             //return this.items.get(t.menuItemId);
3499         }
3500         
3501         return false;
3502     },
3503     
3504     onTouch : function(e) 
3505     {
3506         Roo.log("menu.onTouch");
3507         //e.stopEvent(); this make the user popdown broken
3508         this.onClick(e);
3509     },
3510     
3511     onClick : function(e)
3512     {
3513         Roo.log("menu.onClick");
3514         
3515         var t = this.findTargetItem(e);
3516         if(!t || t.isContainer){
3517             return;
3518         }
3519         Roo.log(e);
3520         /*
3521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3522             if(t == this.activeItem && t.shouldDeactivate(e)){
3523                 this.activeItem.deactivate();
3524                 delete this.activeItem;
3525                 return;
3526             }
3527             if(t.canActivate){
3528                 this.setActiveItem(t, true);
3529             }
3530             return;
3531             
3532             
3533         }
3534         */
3535        
3536         Roo.log('pass click event');
3537         
3538         t.onClick(e);
3539         
3540         this.fireEvent("click", this, t, e);
3541         
3542         var _this = this;
3543         
3544         if(!t.href.length || t.href == '#'){
3545             (function() { _this.hide(); }).defer(100);
3546         }
3547         
3548     },
3549     
3550     onMouseOver : function(e){
3551         var t  = this.findTargetItem(e);
3552         //Roo.log(t);
3553         //if(t){
3554         //    if(t.canActivate && !t.disabled){
3555         //        this.setActiveItem(t, true);
3556         //    }
3557         //}
3558         
3559         this.fireEvent("mouseover", this, e, t);
3560     },
3561     isVisible : function(){
3562         return !this.hidden;
3563     },
3564     onMouseOut : function(e){
3565         var t  = this.findTargetItem(e);
3566         
3567         //if(t ){
3568         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3569         //        this.activeItem.deactivate();
3570         //        delete this.activeItem;
3571         //    }
3572         //}
3573         this.fireEvent("mouseout", this, e, t);
3574     },
3575     
3576     
3577     /**
3578      * Displays this menu relative to another element
3579      * @param {String/HTMLElement/Roo.Element} element The element to align to
3580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581      * the element (defaults to this.defaultAlign)
3582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583      */
3584     show : function(el, pos, parentMenu)
3585     {
3586         if (false === this.fireEvent("beforeshow", this)) {
3587             Roo.log("show canceled");
3588             return;
3589         }
3590         this.parentMenu = parentMenu;
3591         if(!this.el){
3592             this.render();
3593         }
3594         
3595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3596     },
3597      /**
3598      * Displays this menu at a specific xy position
3599      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601      */
3602     showAt : function(xy, parentMenu, /* private: */_e){
3603         this.parentMenu = parentMenu;
3604         if(!this.el){
3605             this.render();
3606         }
3607         if(_e !== false){
3608             this.fireEvent("beforeshow", this);
3609             //xy = this.el.adjustForConstraints(xy);
3610         }
3611         
3612         //this.el.show();
3613         this.hideMenuItems();
3614         this.hidden = false;
3615         this.triggerEl.addClass('open');
3616         this.el.addClass('show');
3617         
3618         // reassign x when hitting right
3619         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3621         }
3622         
3623         // reassign y when hitting bottom
3624         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3626         }
3627         
3628         // but the list may align on trigger left or trigger top... should it be a properity?
3629         
3630         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3631             this.el.setXY(xy);
3632         }
3633         
3634         this.focus();
3635         this.fireEvent("show", this);
3636     },
3637     
3638     focus : function(){
3639         return;
3640         if(!this.hidden){
3641             this.doFocus.defer(50, this);
3642         }
3643     },
3644
3645     doFocus : function(){
3646         if(!this.hidden){
3647             this.focusEl.focus();
3648         }
3649     },
3650
3651     /**
3652      * Hides this menu and optionally all parent menus
3653      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654      */
3655     hide : function(deep)
3656     {
3657         if (false === this.fireEvent("beforehide", this)) {
3658             Roo.log("hide canceled");
3659             return;
3660         }
3661         this.hideMenuItems();
3662         if(this.el && this.isVisible()){
3663            
3664             if(this.activeItem){
3665                 this.activeItem.deactivate();
3666                 this.activeItem = null;
3667             }
3668             this.triggerEl.removeClass('open');;
3669             this.el.removeClass('show');
3670             this.hidden = true;
3671             this.fireEvent("hide", this);
3672         }
3673         if(deep === true && this.parentMenu){
3674             this.parentMenu.hide(true);
3675         }
3676     },
3677     
3678     onTriggerClick : function(e)
3679     {
3680         Roo.log('trigger click');
3681         
3682         var target = e.getTarget();
3683         
3684         Roo.log(target.nodeName.toLowerCase());
3685         
3686         if(target.nodeName.toLowerCase() === 'i'){
3687             e.preventDefault();
3688         }
3689         
3690     },
3691     
3692     onTriggerPress  : function(e)
3693     {
3694         Roo.log('trigger press');
3695         //Roo.log(e.getTarget());
3696        // Roo.log(this.triggerEl.dom);
3697        
3698         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699         var pel = Roo.get(e.getTarget());
3700         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701             Roo.log('is treeview or dropdown?');
3702             return;
3703         }
3704         
3705         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3706             return;
3707         }
3708         
3709         if (this.isVisible()) {
3710             Roo.log('hide');
3711             this.hide();
3712         } else {
3713             Roo.log('show');
3714             this.show(this.triggerEl, '?', false);
3715         }
3716         
3717         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3718             e.stopEvent();
3719         }
3720         
3721     },
3722        
3723     
3724     hideMenuItems : function()
3725     {
3726         Roo.log("hide Menu Items");
3727         if (!this.el) { 
3728             return;
3729         }
3730         
3731         this.el.select('.open',true).each(function(aa) {
3732             
3733             aa.removeClass('open');
3734          
3735         });
3736     },
3737     addxtypeChild : function (tree, cntr) {
3738         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739           
3740         this.menuitems.add(comp);
3741         return comp;
3742
3743     },
3744     getEl : function()
3745     {
3746         Roo.log(this.el);
3747         return this.el;
3748     },
3749     
3750     clear : function()
3751     {
3752         this.getEl().dom.innerHTML = '';
3753         this.menuitems.clear();
3754     }
3755 });
3756
3757  
3758  /*
3759  * - LGPL
3760  *
3761  * menu item
3762  * 
3763  */
3764
3765
3766 /**
3767  * @class Roo.bootstrap.MenuItem
3768  * @extends Roo.bootstrap.Component
3769  * Bootstrap MenuItem class
3770  * @cfg {String} html the menu label
3771  * @cfg {String} href the link
3772  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3775  * @cfg {String} fa favicon to show on left of menu item.
3776  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new MenuItem
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.MenuItem = function(config){
3786     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3787     this.addEvents({
3788         // raw events
3789         /**
3790          * @event click
3791          * The raw click event for the entire grid.
3792          * @param {Roo.bootstrap.MenuItem} this
3793          * @param {Roo.EventObject} e
3794          */
3795         "click" : true
3796     });
3797 };
3798
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3800     
3801     href : false,
3802     html : false,
3803     preventDefault: false,
3804     isContainer : false,
3805     active : false,
3806     fa: false,
3807     
3808     getAutoCreate : function(){
3809         
3810         if(this.isContainer){
3811             return {
3812                 tag: 'li',
3813                 cls: 'dropdown-menu-item '
3814             };
3815         }
3816         var ctag = {
3817             tag: 'span',
3818             html: 'Link'
3819         };
3820         
3821         var anc = {
3822             tag : 'a',
3823             cls : 'dropdown-item',
3824             href : '#',
3825             cn : [  ]
3826         };
3827         
3828         if (this.fa !== false) {
3829             anc.cn.push({
3830                 tag : 'i',
3831                 cls : 'fa fa-' + this.fa
3832             });
3833         }
3834         
3835         anc.cn.push(ctag);
3836         
3837         
3838         var cfg= {
3839             tag: 'li',
3840             cls: 'dropdown-menu-item',
3841             cn: [ anc ]
3842         };
3843         if (this.parent().type == 'treeview') {
3844             cfg.cls = 'treeview-menu';
3845         }
3846         if (this.active) {
3847             cfg.cls += ' active';
3848         }
3849         
3850         
3851         
3852         anc.href = this.href || cfg.cn[0].href ;
3853         ctag.html = this.html || cfg.cn[0].html ;
3854         return cfg;
3855     },
3856     
3857     initEvents: function()
3858     {
3859         if (this.parent().type == 'treeview') {
3860             this.el.select('a').on('click', this.onClick, this);
3861         }
3862         
3863         if (this.menu) {
3864             this.menu.parentType = this.xtype;
3865             this.menu.triggerEl = this.el;
3866             this.menu = this.addxtype(Roo.apply({}, this.menu));
3867         }
3868         
3869     },
3870     onClick : function(e)
3871     {
3872         Roo.log('item on click ');
3873         
3874         if(this.preventDefault){
3875             e.preventDefault();
3876         }
3877         //this.parent().hideMenuItems();
3878         
3879         this.fireEvent('click', this, e);
3880     },
3881     getEl : function()
3882     {
3883         return this.el;
3884     } 
3885 });
3886
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * menu separator
3893  * 
3894  */
3895
3896
3897 /**
3898  * @class Roo.bootstrap.MenuSeparator
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap MenuSeparator class
3901  * 
3902  * @constructor
3903  * Create a new MenuItem
3904  * @param {Object} config The config object
3905  */
3906
3907
3908 Roo.bootstrap.MenuSeparator = function(config){
3909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915         var cfg = {
3916             cls: 'divider',
3917             tag : 'li'
3918         };
3919         
3920         return cfg;
3921     }
3922    
3923 });
3924
3925  
3926
3927  
3928 /*
3929 * Licence: LGPL
3930 */
3931
3932 /**
3933  * @class Roo.bootstrap.Modal
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Modal class
3936  * @cfg {String} title Title of dialog
3937  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3939  * @cfg {Boolean} specificTitle default false
3940  * @cfg {Array} buttons Array of buttons or standard button set..
3941  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942  * @cfg {Boolean} animate default true
3943  * @cfg {Boolean} allow_close default true
3944  * @cfg {Boolean} fitwindow default false
3945  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947  * @cfg {String} size (sm|lg|xl) default empty
3948  * @cfg {Number} max_width set the max width of modal
3949  * @cfg {Boolean} editableTitle can the title be edited
3950
3951  *
3952  *
3953  * @constructor
3954  * Create a new Modal Dialog
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Modal = function(config){
3959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3960     this.addEvents({
3961         // raw events
3962         /**
3963          * @event btnclick
3964          * The raw btnclick event for the button
3965          * @param {Roo.EventObject} e
3966          */
3967         "btnclick" : true,
3968         /**
3969          * @event resize
3970          * Fire when dialog resize
3971          * @param {Roo.bootstrap.Modal} this
3972          * @param {Roo.EventObject} e
3973          */
3974         "resize" : true,
3975         /**
3976          * @event titlechanged
3977          * Fire when the editable title has been changed
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} value
3980          */
3981         "titlechanged" : true 
3982         
3983     });
3984     this.buttons = this.buttons || [];
3985
3986     if (this.tmpl) {
3987         this.tmpl = Roo.factory(this.tmpl);
3988     }
3989
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3993
3994     title : 'test dialog',
3995
3996     buttons : false,
3997
3998     // set on load...
3999
4000     html: false,
4001
4002     tmp: false,
4003
4004     specificTitle: false,
4005
4006     buttonPosition: 'right',
4007
4008     allow_close : true,
4009
4010     animate : true,
4011
4012     fitwindow: false,
4013     
4014      // private
4015     dialogEl: false,
4016     bodyEl:  false,
4017     footerEl:  false,
4018     titleEl:  false,
4019     closeEl:  false,
4020
4021     size: '',
4022     
4023     max_width: 0,
4024     
4025     max_height: 0,
4026     
4027     fit_content: false,
4028     editableTitle  : false,
4029
4030     onRender : function(ct, position)
4031     {
4032         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4033
4034         if(!this.el){
4035             var cfg = Roo.apply({},  this.getAutoCreate());
4036             cfg.id = Roo.id();
4037             //if(!cfg.name){
4038             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039             //}
4040             //if (!cfg.name.length) {
4041             //    delete cfg.name;
4042            // }
4043             if (this.cls) {
4044                 cfg.cls += ' ' + this.cls;
4045             }
4046             if (this.style) {
4047                 cfg.style = this.style;
4048             }
4049             this.el = Roo.get(document.body).createChild(cfg, position);
4050         }
4051         //var type = this.el.dom.type;
4052
4053
4054         if(this.tabIndex !== undefined){
4055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4056         }
4057
4058         this.dialogEl = this.el.select('.modal-dialog',true).first();
4059         this.bodyEl = this.el.select('.modal-body',true).first();
4060         this.closeEl = this.el.select('.modal-header .close', true).first();
4061         this.headerEl = this.el.select('.modal-header',true).first();
4062         this.titleEl = this.el.select('.modal-title',true).first();
4063         this.footerEl = this.el.select('.modal-footer',true).first();
4064
4065         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066         
4067         //this.el.addClass("x-dlg-modal");
4068
4069         if (this.buttons.length) {
4070             Roo.each(this.buttons, function(bb) {
4071                 var b = Roo.apply({}, bb);
4072                 b.xns = b.xns || Roo.bootstrap;
4073                 b.xtype = b.xtype || 'Button';
4074                 if (typeof(b.listeners) == 'undefined') {
4075                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4076                 }
4077
4078                 var btn = Roo.factory(b);
4079
4080                 btn.render(this.getButtonContainer());
4081
4082             },this);
4083         }
4084         // render the children.
4085         var nitems = [];
4086
4087         if(typeof(this.items) != 'undefined'){
4088             var items = this.items;
4089             delete this.items;
4090
4091             for(var i =0;i < items.length;i++) {
4092                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4093             }
4094         }
4095
4096         this.items = nitems;
4097
4098         // where are these used - they used to be body/close/footer
4099
4100
4101         this.initEvents();
4102         //this.el.addClass([this.fieldClass, this.cls]);
4103
4104     },
4105
4106     getAutoCreate : function()
4107     {
4108         // we will default to modal-body-overflow - might need to remove or make optional later.
4109         var bdy = {
4110                 cls : 'modal-body enable-modal-body-overflow ', 
4111                 html : this.html || ''
4112         };
4113
4114         var title = {
4115             tag: 'h4',
4116             cls : 'modal-title',
4117             html : this.title
4118         };
4119
4120         if(this.specificTitle){ // WTF is this?
4121             title = this.title;
4122         }
4123
4124         var header = [];
4125         if (this.allow_close && Roo.bootstrap.version == 3) {
4126             header.push({
4127                 tag: 'button',
4128                 cls : 'close',
4129                 html : '&times'
4130             });
4131         }
4132
4133         header.push(title);
4134
4135         if (this.editableTitle) {
4136             header.push({
4137                 cls: 'form-control roo-editable-title d-none',
4138                 tag: 'input',
4139                 type: 'text'
4140             });
4141         }
4142         
4143         if (this.allow_close && Roo.bootstrap.version == 4) {
4144             header.push({
4145                 tag: 'button',
4146                 cls : 'close',
4147                 html : '&times'
4148             });
4149         }
4150         
4151         var size = '';
4152
4153         if(this.size.length){
4154             size = 'modal-' + this.size;
4155         }
4156         
4157         var footer = Roo.bootstrap.version == 3 ?
4158             {
4159                 cls : 'modal-footer',
4160                 cn : [
4161                     {
4162                         tag: 'div',
4163                         cls: 'btn-' + this.buttonPosition
4164                     }
4165                 ]
4166
4167             } :
4168             {  // BS4 uses mr-auto on left buttons....
4169                 cls : 'modal-footer'
4170             };
4171
4172             
4173
4174         
4175         
4176         var modal = {
4177             cls: "modal",
4178              cn : [
4179                 {
4180                     cls: "modal-dialog " + size,
4181                     cn : [
4182                         {
4183                             cls : "modal-content",
4184                             cn : [
4185                                 {
4186                                     cls : 'modal-header',
4187                                     cn : header
4188                                 },
4189                                 bdy,
4190                                 footer
4191                             ]
4192
4193                         }
4194                     ]
4195
4196                 }
4197             ]
4198         };
4199
4200         if(this.animate){
4201             modal.cls += ' fade';
4202         }
4203
4204         return modal;
4205
4206     },
4207     getChildContainer : function() {
4208
4209          return this.bodyEl;
4210
4211     },
4212     getButtonContainer : function() {
4213         
4214          return Roo.bootstrap.version == 4 ?
4215             this.el.select('.modal-footer',true).first()
4216             : this.el.select('.modal-footer div',true).first();
4217
4218     },
4219     initEvents : function()
4220     {
4221         if (this.allow_close) {
4222             this.closeEl.on('click', this.hide, this);
4223         }
4224         Roo.EventManager.onWindowResize(this.resize, this, true);
4225         if (this.editableTitle) {
4226             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4227             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228             this.headerEditEl.on('keyup', function(e) {
4229                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230                         this.toggleHeaderInput(false)
4231                     }
4232                 }, this);
4233             this.headerEditEl.on('blur', function(e) {
4234                 this.toggleHeaderInput(false)
4235             },this);
4236         }
4237
4238     },
4239   
4240
4241     resize : function()
4242     {
4243         this.maskEl.setSize(
4244             Roo.lib.Dom.getViewWidth(true),
4245             Roo.lib.Dom.getViewHeight(true)
4246         );
4247         
4248         if (this.fitwindow) {
4249             
4250            
4251             this.setSize(
4252                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4254             );
4255             return;
4256         }
4257         
4258         if(this.max_width !== 0) {
4259             
4260             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4261             
4262             if(this.height) {
4263                 this.setSize(w, this.height);
4264                 return;
4265             }
4266             
4267             if(this.max_height) {
4268                 this.setSize(w,Math.min(
4269                     this.max_height,
4270                     Roo.lib.Dom.getViewportHeight(true) - 60
4271                 ));
4272                 
4273                 return;
4274             }
4275             
4276             if(!this.fit_content) {
4277                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4278                 return;
4279             }
4280             
4281             this.setSize(w, Math.min(
4282                 60 +
4283                 this.headerEl.getHeight() + 
4284                 this.footerEl.getHeight() + 
4285                 this.getChildHeight(this.bodyEl.dom.childNodes),
4286                 Roo.lib.Dom.getViewportHeight(true) - 60)
4287             );
4288         }
4289         
4290     },
4291
4292     setSize : function(w,h)
4293     {
4294         if (!w && !h) {
4295             return;
4296         }
4297         
4298         this.resizeTo(w,h);
4299     },
4300
4301     show : function() {
4302
4303         if (!this.rendered) {
4304             this.render();
4305         }
4306         this.toggleHeaderInput(false);
4307         //this.el.setStyle('display', 'block');
4308         this.el.removeClass('hideing');
4309         this.el.dom.style.display='block';
4310         
4311         Roo.get(document.body).addClass('modal-open');
4312  
4313         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4314             
4315             (function(){
4316                 this.el.addClass('show');
4317                 this.el.addClass('in');
4318             }).defer(50, this);
4319         }else{
4320             this.el.addClass('show');
4321             this.el.addClass('in');
4322         }
4323
4324         // not sure how we can show data in here..
4325         //if (this.tmpl) {
4326         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4327         //}
4328
4329         Roo.get(document.body).addClass("x-body-masked");
4330         
4331         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4332         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333         this.maskEl.dom.style.display = 'block';
4334         this.maskEl.addClass('show');
4335         
4336         
4337         this.resize();
4338         
4339         this.fireEvent('show', this);
4340
4341         // set zindex here - otherwise it appears to be ignored...
4342         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4343
4344         (function () {
4345             this.items.forEach( function(e) {
4346                 e.layout ? e.layout() : false;
4347
4348             });
4349         }).defer(100,this);
4350
4351     },
4352     hide : function()
4353     {
4354         if(this.fireEvent("beforehide", this) !== false){
4355             
4356             this.maskEl.removeClass('show');
4357             
4358             this.maskEl.dom.style.display = '';
4359             Roo.get(document.body).removeClass("x-body-masked");
4360             this.el.removeClass('in');
4361             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362
4363             if(this.animate){ // why
4364                 this.el.addClass('hideing');
4365                 this.el.removeClass('show');
4366                 (function(){
4367                     if (!this.el.hasClass('hideing')) {
4368                         return; // it's been shown again...
4369                     }
4370                     
4371                     this.el.dom.style.display='';
4372
4373                     Roo.get(document.body).removeClass('modal-open');
4374                     this.el.removeClass('hideing');
4375                 }).defer(150,this);
4376                 
4377             }else{
4378                 this.el.removeClass('show');
4379                 this.el.dom.style.display='';
4380                 Roo.get(document.body).removeClass('modal-open');
4381
4382             }
4383             this.fireEvent('hide', this);
4384         }
4385     },
4386     isVisible : function()
4387     {
4388         
4389         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4390         
4391     },
4392
4393     addButton : function(str, cb)
4394     {
4395
4396
4397         var b = Roo.apply({}, { html : str } );
4398         b.xns = b.xns || Roo.bootstrap;
4399         b.xtype = b.xtype || 'Button';
4400         if (typeof(b.listeners) == 'undefined') {
4401             b.listeners = { click : cb.createDelegate(this)  };
4402         }
4403
4404         var btn = Roo.factory(b);
4405
4406         btn.render(this.getButtonContainer());
4407
4408         return btn;
4409
4410     },
4411
4412     setDefaultButton : function(btn)
4413     {
4414         //this.el.select('.modal-footer').()
4415     },
4416
4417     resizeTo: function(w,h)
4418     {
4419         this.dialogEl.setWidth(w);
4420         
4421         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4422
4423         this.bodyEl.setHeight(h - diff);
4424         
4425         this.fireEvent('resize', this);
4426     },
4427     
4428     setContentSize  : function(w, h)
4429     {
4430
4431     },
4432     onButtonClick: function(btn,e)
4433     {
4434         //Roo.log([a,b,c]);
4435         this.fireEvent('btnclick', btn.name, e);
4436     },
4437      /**
4438      * Set the title of the Dialog
4439      * @param {String} str new Title
4440      */
4441     setTitle: function(str) {
4442         this.titleEl.dom.innerHTML = str;
4443         this.title = str;
4444     },
4445     /**
4446      * Set the body of the Dialog
4447      * @param {String} str new Title
4448      */
4449     setBody: function(str) {
4450         this.bodyEl.dom.innerHTML = str;
4451     },
4452     /**
4453      * Set the body of the Dialog using the template
4454      * @param {Obj} data - apply this data to the template and replace the body contents.
4455      */
4456     applyBody: function(obj)
4457     {
4458         if (!this.tmpl) {
4459             Roo.log("Error - using apply Body without a template");
4460             //code
4461         }
4462         this.tmpl.overwrite(this.bodyEl, obj);
4463     },
4464     
4465     getChildHeight : function(child_nodes)
4466     {
4467         if(
4468             !child_nodes ||
4469             child_nodes.length == 0
4470         ) {
4471             return 0;
4472         }
4473         
4474         var child_height = 0;
4475         
4476         for(var i = 0; i < child_nodes.length; i++) {
4477             
4478             /*
4479             * for modal with tabs...
4480             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481                 
4482                 var layout_childs = child_nodes[i].childNodes;
4483                 
4484                 for(var j = 0; j < layout_childs.length; j++) {
4485                     
4486                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487                         
4488                         var layout_body_childs = layout_childs[j].childNodes;
4489                         
4490                         for(var k = 0; k < layout_body_childs.length; k++) {
4491                             
4492                             if(layout_body_childs[k].classList.contains('navbar')) {
4493                                 child_height += layout_body_childs[k].offsetHeight;
4494                                 continue;
4495                             }
4496                             
4497                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498                                 
4499                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500                                 
4501                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502                                     
4503                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4505                                         continue;
4506                                     }
4507                                     
4508                                 }
4509                                 
4510                             }
4511                             
4512                         }
4513                     }
4514                 }
4515                 continue;
4516             }
4517             */
4518             
4519             child_height += child_nodes[i].offsetHeight;
4520             // Roo.log(child_nodes[i].offsetHeight);
4521         }
4522         
4523         return child_height;
4524     },
4525     toggleHeaderInput : function(is_edit)
4526     {
4527         if (!this.editableTitle) {
4528             return; // not editable.
4529         }
4530         if (is_edit && this.is_header_editing) {
4531             return; // already editing..
4532         }
4533         if (is_edit) {
4534     
4535             this.headerEditEl.dom.value = this.title;
4536             this.headerEditEl.removeClass('d-none');
4537             this.headerEditEl.dom.focus();
4538             this.titleEl.addClass('d-none');
4539             
4540             this.is_header_editing = true;
4541             return
4542         }
4543         // flip back to not editing.
4544         this.title = this.headerEditEl.dom.value;
4545         this.headerEditEl.addClass('d-none');
4546         this.titleEl.removeClass('d-none');
4547         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548         this.is_header_editing = false;
4549         this.fireEvent('titlechanged', this, this.title);
4550     
4551             
4552         
4553     }
4554
4555 });
4556
4557
4558 Roo.apply(Roo.bootstrap.Modal,  {
4559     /**
4560          * Button config that displays a single OK button
4561          * @type Object
4562          */
4563         OK :  [{
4564             name : 'ok',
4565             weight : 'primary',
4566             html : 'OK'
4567         }],
4568         /**
4569          * Button config that displays Yes and No buttons
4570          * @type Object
4571          */
4572         YESNO : [
4573             {
4574                 name  : 'no',
4575                 html : 'No'
4576             },
4577             {
4578                 name  :'yes',
4579                 weight : 'primary',
4580                 html : 'Yes'
4581             }
4582         ],
4583
4584         /**
4585          * Button config that displays OK and Cancel buttons
4586          * @type Object
4587          */
4588         OKCANCEL : [
4589             {
4590                name : 'cancel',
4591                 html : 'Cancel'
4592             },
4593             {
4594                 name : 'ok',
4595                 weight : 'primary',
4596                 html : 'OK'
4597             }
4598         ],
4599         /**
4600          * Button config that displays Yes, No and Cancel buttons
4601          * @type Object
4602          */
4603         YESNOCANCEL : [
4604             {
4605                 name : 'yes',
4606                 weight : 'primary',
4607                 html : 'Yes'
4608             },
4609             {
4610                 name : 'no',
4611                 html : 'No'
4612             },
4613             {
4614                 name : 'cancel',
4615                 html : 'Cancel'
4616             }
4617         ],
4618         
4619         zIndex : 10001
4620 });
4621
4622 /*
4623  * - LGPL
4624  *
4625  * messagebox - can be used as a replace
4626  * 
4627  */
4628 /**
4629  * @class Roo.MessageBox
4630  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4631  * Example usage:
4632  *<pre><code>
4633 // Basic alert:
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638     if (btn == 'ok'){
4639         // process text value...
4640     }
4641 });
4642
4643 // Show a dialog using config options:
4644 Roo.Msg.show({
4645    title:'Save Changes?',
4646    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647    buttons: Roo.Msg.YESNOCANCEL,
4648    fn: processResult,
4649    animEl: 'elId'
4650 });
4651 </code></pre>
4652  * @singleton
4653  */
4654 Roo.bootstrap.MessageBox = function(){
4655     var dlg, opt, mask, waitTimer;
4656     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657     var buttons, activeTextEl, bwidth;
4658
4659     
4660     // private
4661     var handleButton = function(button){
4662         dlg.hide();
4663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664     };
4665
4666     // private
4667     var handleHide = function(){
4668         if(opt && opt.cls){
4669             dlg.el.removeClass(opt.cls);
4670         }
4671         //if(waitTimer){
4672         //    Roo.TaskMgr.stop(waitTimer);
4673         //    waitTimer = null;
4674         //}
4675     };
4676
4677     // private
4678     var updateButtons = function(b){
4679         var width = 0;
4680         if(!b){
4681             buttons["ok"].hide();
4682             buttons["cancel"].hide();
4683             buttons["yes"].hide();
4684             buttons["no"].hide();
4685             dlg.footerEl.hide();
4686             
4687             return width;
4688         }
4689         dlg.footerEl.show();
4690         for(var k in buttons){
4691             if(typeof buttons[k] != "function"){
4692                 if(b[k]){
4693                     buttons[k].show();
4694                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695                     width += buttons[k].el.getWidth()+15;
4696                 }else{
4697                     buttons[k].hide();
4698                 }
4699             }
4700         }
4701         return width;
4702     };
4703
4704     // private
4705     var handleEsc = function(d, k, e){
4706         if(opt && opt.closable !== false){
4707             dlg.hide();
4708         }
4709         if(e){
4710             e.stopEvent();
4711         }
4712     };
4713
4714     return {
4715         /**
4716          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717          * @return {Roo.BasicDialog} The BasicDialog element
4718          */
4719         getDialog : function(){
4720            if(!dlg){
4721                 dlg = new Roo.bootstrap.Modal( {
4722                     //draggable: true,
4723                     //resizable:false,
4724                     //constraintoviewport:false,
4725                     //fixedcenter:true,
4726                     //collapsible : false,
4727                     //shim:true,
4728                     //modal: true,
4729                 //    width: 'auto',
4730                   //  height:100,
4731                     //buttonAlign:"center",
4732                     closeClick : function(){
4733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4734                             handleButton("no");
4735                         }else{
4736                             handleButton("cancel");
4737                         }
4738                     }
4739                 });
4740                 dlg.render();
4741                 dlg.on("hide", handleHide);
4742                 mask = dlg.mask;
4743                 //dlg.addKeyListener(27, handleEsc);
4744                 buttons = {};
4745                 this.buttons = buttons;
4746                 var bt = this.buttonText;
4747                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751                 //Roo.log(buttons);
4752                 bodyEl = dlg.bodyEl.createChild({
4753
4754                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755                         '<textarea class="roo-mb-textarea"></textarea>' +
4756                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4757                 });
4758                 msgEl = bodyEl.dom.firstChild;
4759                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760                 textboxEl.enableDisplayMode();
4761                 textboxEl.addKeyListener([10,13], function(){
4762                     if(dlg.isVisible() && opt && opt.buttons){
4763                         if(opt.buttons.ok){
4764                             handleButton("ok");
4765                         }else if(opt.buttons.yes){
4766                             handleButton("yes");
4767                         }
4768                     }
4769                 });
4770                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771                 textareaEl.enableDisplayMode();
4772                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773                 progressEl.enableDisplayMode();
4774                 
4775                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776                 var pf = progressEl.dom.firstChild;
4777                 if (pf) {
4778                     pp = Roo.get(pf.firstChild);
4779                     pp.setHeight(pf.offsetHeight);
4780                 }
4781                 
4782             }
4783             return dlg;
4784         },
4785
4786         /**
4787          * Updates the message box body text
4788          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789          * the XHTML-compliant non-breaking space character '&amp;#160;')
4790          * @return {Roo.MessageBox} This message box
4791          */
4792         updateText : function(text)
4793         {
4794             if(!dlg.isVisible() && !opt.width){
4795                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797             }
4798             msgEl.innerHTML = text || '&#160;';
4799       
4800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802             var w = Math.max(
4803                     Math.min(opt.width || cw , this.maxWidth), 
4804                     Math.max(opt.minWidth || this.minWidth, bwidth)
4805             );
4806             if(opt.prompt){
4807                 activeTextEl.setWidth(w);
4808             }
4809             if(dlg.isVisible()){
4810                 dlg.fixedcenter = false;
4811             }
4812             // to big, make it scroll. = But as usual stupid IE does not support
4813             // !important..
4814             
4815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818             } else {
4819                 bodyEl.dom.style.height = '';
4820                 bodyEl.dom.style.overflowY = '';
4821             }
4822             if (cw > w) {
4823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.overflowX = '';
4826             }
4827             
4828             dlg.setContentSize(w, bodyEl.getHeight());
4829             if(dlg.isVisible()){
4830                 dlg.fixedcenter = true;
4831             }
4832             return this;
4833         },
4834
4835         /**
4836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840          * @return {Roo.MessageBox} This message box
4841          */
4842         updateProgress : function(value, text){
4843             if(text){
4844                 this.updateText(text);
4845             }
4846             
4847             if (pp) { // weird bug on my firefox - for some reason this is not defined
4848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4850             }
4851             return this;
4852         },        
4853
4854         /**
4855          * Returns true if the message box is currently displayed
4856          * @return {Boolean} True if the message box is visible, else false
4857          */
4858         isVisible : function(){
4859             return dlg && dlg.isVisible();  
4860         },
4861
4862         /**
4863          * Hides the message box if it is displayed
4864          */
4865         hide : function(){
4866             if(this.isVisible()){
4867                 dlg.hide();
4868             }  
4869         },
4870
4871         /**
4872          * Displays a new message box, or reinitializes an existing message box, based on the config options
4873          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874          * The following config object properties are supported:
4875          * <pre>
4876 Property    Type             Description
4877 ----------  ---------------  ------------------------------------------------------------------------------------
4878 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4879                                    closes (defaults to undefined)
4880 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4883                                    progress and wait dialogs will ignore this property and always hide the
4884                                    close button as they can only be closed programmatically.
4885 cls               String           A custom CSS class to apply to the message box element
4886 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4887                                    displayed (defaults to 75)
4888 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4889                                    function will be btn (the name of the button that was clicked, if applicable,
4890                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4891                                    Progress and wait dialogs will ignore this option since they do not respond to
4892                                    user actions and can only be closed programmatically, so any required function
4893                                    should be called by the same code after it closes the dialog.
4894 icon              String           A CSS class that provides a background image to be used as an icon for
4895                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4897 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4898 modal             Boolean          False to allow user interaction with the page while the message box is
4899                                    displayed (defaults to true)
4900 msg               String           A string that will replace the existing message box body text (defaults
4901                                    to the XHTML-compliant non-breaking space character '&#160;')
4902 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4903 progress          Boolean          True to display a progress bar (defaults to false)
4904 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4907 title             String           The title text
4908 value             String           The string value to set into the active textbox element if displayed
4909 wait              Boolean          True to display a progress bar (defaults to false)
4910 width             Number           The width of the dialog in pixels
4911 </pre>
4912          *
4913          * Example usage:
4914          * <pre><code>
4915 Roo.Msg.show({
4916    title: 'Address',
4917    msg: 'Please enter your address:',
4918    width: 300,
4919    buttons: Roo.MessageBox.OKCANCEL,
4920    multiline: true,
4921    fn: saveAddress,
4922    animEl: 'addAddressBtn'
4923 });
4924 </code></pre>
4925          * @param {Object} config Configuration options
4926          * @return {Roo.MessageBox} This message box
4927          */
4928         show : function(options)
4929         {
4930             
4931             // this causes nightmares if you show one dialog after another
4932             // especially on callbacks..
4933              
4934             if(this.isVisible()){
4935                 
4936                 this.hide();
4937                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4939                 Roo.log("New Dialog Message:" +  options.msg )
4940                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4942                 
4943             }
4944             var d = this.getDialog();
4945             opt = options;
4946             d.setTitle(opt.title || "&#160;");
4947             d.closeEl.setDisplayed(opt.closable !== false);
4948             activeTextEl = textboxEl;
4949             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4950             if(opt.prompt){
4951                 if(opt.multiline){
4952                     textboxEl.hide();
4953                     textareaEl.show();
4954                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4955                         opt.multiline : this.defaultTextHeight);
4956                     activeTextEl = textareaEl;
4957                 }else{
4958                     textboxEl.show();
4959                     textareaEl.hide();
4960                 }
4961             }else{
4962                 textboxEl.hide();
4963                 textareaEl.hide();
4964             }
4965             progressEl.setDisplayed(opt.progress === true);
4966             if (opt.progress) {
4967                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968             }
4969             this.updateProgress(0);
4970             activeTextEl.dom.value = opt.value || "";
4971             if(opt.prompt){
4972                 dlg.setDefaultButton(activeTextEl);
4973             }else{
4974                 var bs = opt.buttons;
4975                 var db = null;
4976                 if(bs && bs.ok){
4977                     db = buttons["ok"];
4978                 }else if(bs && bs.yes){
4979                     db = buttons["yes"];
4980                 }
4981                 dlg.setDefaultButton(db);
4982             }
4983             bwidth = updateButtons(opt.buttons);
4984             this.updateText(opt.msg);
4985             if(opt.cls){
4986                 d.el.addClass(opt.cls);
4987             }
4988             d.proxyDrag = opt.proxyDrag === true;
4989             d.modal = opt.modal !== false;
4990             d.mask = opt.modal !== false ? mask : false;
4991             if(!d.isVisible()){
4992                 // force it to the end of the z-index stack so it gets a cursor in FF
4993                 document.body.appendChild(dlg.el.dom);
4994                 d.animateTarget = null;
4995                 d.show(options.animEl);
4996             }
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5002          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003          * and closing the message box when the process is complete.
5004          * @param {String} title The title bar text
5005          * @param {String} msg The message box body text
5006          * @return {Roo.MessageBox} This message box
5007          */
5008         progress : function(title, msg){
5009             this.show({
5010                 title : title,
5011                 msg : msg,
5012                 buttons: false,
5013                 progress:true,
5014                 closable:false,
5015                 minWidth: this.minProgressWidth,
5016                 modal : true
5017             });
5018             return this;
5019         },
5020
5021         /**
5022          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023          * If a callback function is passed it will be called after the user clicks the button, and the
5024          * id of the button that was clicked will be passed as the only parameter to the callback
5025          * (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         alert : function(title, msg, fn, scope)
5033         {
5034             this.show({
5035                 title : title,
5036                 msg : msg,
5037                 buttons: this.OK,
5038                 fn: fn,
5039                 closable : false,
5040                 scope : scope,
5041                 modal : true
5042             });
5043             return this;
5044         },
5045
5046         /**
5047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049          * You are responsible for closing the message box when the process is complete.
5050          * @param {String} msg The message box body text
5051          * @param {String} title (optional) The title bar text
5052          * @return {Roo.MessageBox} This message box
5053          */
5054         wait : function(msg, title){
5055             this.show({
5056                 title : title,
5057                 msg : msg,
5058                 buttons: false,
5059                 closable:false,
5060                 progress:true,
5061                 modal:true,
5062                 width:300,
5063                 wait:true
5064             });
5065             waitTimer = Roo.TaskMgr.start({
5066                 run: function(i){
5067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5068                 },
5069                 interval: 1000
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078          * @param {String} title The title bar text
5079          * @param {String} msg The message box body text
5080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081          * @param {Object} scope (optional) The scope of the callback function
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         confirm : function(title, msg, fn, scope){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: this.YESNO,
5089                 fn: fn,
5090                 scope : scope,
5091                 modal : true
5092             });
5093             return this;
5094         },
5095
5096         /**
5097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100          * (could also be the top-right close button) and the text that was entered will be passed as the two
5101          * parameters to the callback.
5102          * @param {String} title The title bar text
5103          * @param {String} msg The message box body text
5104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105          * @param {Object} scope (optional) The scope of the callback function
5106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108          * @return {Roo.MessageBox} This message box
5109          */
5110         prompt : function(title, msg, fn, scope, multiline){
5111             this.show({
5112                 title : title,
5113                 msg : msg,
5114                 buttons: this.OKCANCEL,
5115                 fn: fn,
5116                 minWidth:250,
5117                 scope : scope,
5118                 prompt:true,
5119                 multiline: multiline,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Button config that displays a single OK button
5127          * @type Object
5128          */
5129         OK : {ok:true},
5130         /**
5131          * Button config that displays Yes and No buttons
5132          * @type Object
5133          */
5134         YESNO : {yes:true, no:true},
5135         /**
5136          * Button config that displays OK and Cancel buttons
5137          * @type Object
5138          */
5139         OKCANCEL : {ok:true, cancel:true},
5140         /**
5141          * Button config that displays Yes, No and Cancel buttons
5142          * @type Object
5143          */
5144         YESNOCANCEL : {yes:true, no:true, cancel:true},
5145
5146         /**
5147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5148          * @type Number
5149          */
5150         defaultTextHeight : 75,
5151         /**
5152          * The maximum width in pixels of the message box (defaults to 600)
5153          * @type Number
5154          */
5155         maxWidth : 600,
5156         /**
5157          * The minimum width in pixels of the message box (defaults to 100)
5158          * @type Number
5159          */
5160         minWidth : 100,
5161         /**
5162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5164          * @type Number
5165          */
5166         minProgressWidth : 250,
5167         /**
5168          * An object containing the default button text strings that can be overriden for localized language support.
5169          * Supported properties are: ok, cancel, yes and no.
5170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5171          * @type Object
5172          */
5173         buttonText : {
5174             ok : "OK",
5175             cancel : "Cancel",
5176             yes : "Yes",
5177             no : "No"
5178         }
5179     };
5180 }();
5181
5182 /**
5183  * Shorthand for {@link Roo.MessageBox}
5184  */
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5187 /*
5188  * - LGPL
5189  *
5190  * navbar
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Navbar
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Navbar class
5198
5199  * @constructor
5200  * Create a new Navbar
5201  * @param {Object} config The config object
5202  */
5203
5204
5205 Roo.bootstrap.Navbar = function(config){
5206     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207     this.addEvents({
5208         // raw events
5209         /**
5210          * @event beforetoggle
5211          * Fire before toggle the menu
5212          * @param {Roo.EventObject} e
5213          */
5214         "beforetoggle" : true
5215     });
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5219     
5220     
5221    
5222     // private
5223     navItems : false,
5224     loadMask : false,
5225     
5226     
5227     getAutoCreate : function(){
5228         
5229         
5230         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231         
5232     },
5233     
5234     initEvents :function ()
5235     {
5236         //Roo.log(this.el.select('.navbar-toggle',true));
5237         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5238         
5239         var mark = {
5240             tag: "div",
5241             cls:"x-dlg-mask"
5242         };
5243         
5244         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245         
5246         var size = this.el.getSize();
5247         this.maskEl.setSize(size.width, size.height);
5248         this.maskEl.enableDisplayMode("block");
5249         this.maskEl.hide();
5250         
5251         if(this.loadMask){
5252             this.maskEl.show();
5253         }
5254     },
5255     
5256     
5257     getChildContainer : function()
5258     {
5259         if (this.el && this.el.select('.collapse').getCount()) {
5260             return this.el.select('.collapse',true).first();
5261         }
5262         
5263         return this.el;
5264     },
5265     
5266     mask : function()
5267     {
5268         this.maskEl.show();
5269     },
5270     
5271     unmask : function()
5272     {
5273         this.maskEl.hide();
5274     },
5275     onToggle : function()
5276     {
5277         
5278         if(this.fireEvent('beforetoggle', this) === false){
5279             return;
5280         }
5281         var ce = this.el.select('.navbar-collapse',true).first();
5282       
5283         if (!ce.hasClass('show')) {
5284            this.expand();
5285         } else {
5286             this.collapse();
5287         }
5288         
5289         
5290     
5291     },
5292     /**
5293      * Expand the navbar pulldown 
5294      */
5295     expand : function ()
5296     {
5297        
5298         var ce = this.el.select('.navbar-collapse',true).first();
5299         if (ce.hasClass('collapsing')) {
5300             return;
5301         }
5302         ce.dom.style.height = '';
5303                // show it...
5304         ce.addClass('in'); // old...
5305         ce.removeClass('collapse');
5306         ce.addClass('show');
5307         var h = ce.getHeight();
5308         Roo.log(h);
5309         ce.removeClass('show');
5310         // at this point we should be able to see it..
5311         ce.addClass('collapsing');
5312         
5313         ce.setHeight(0); // resize it ...
5314         ce.on('transitionend', function() {
5315             //Roo.log('done transition');
5316             ce.removeClass('collapsing');
5317             ce.addClass('show');
5318             ce.removeClass('collapse');
5319
5320             ce.dom.style.height = '';
5321         }, this, { single: true} );
5322         ce.setHeight(h);
5323         ce.dom.scrollTop = 0;
5324     },
5325     /**
5326      * Collapse the navbar pulldown 
5327      */
5328     collapse : function()
5329     {
5330          var ce = this.el.select('.navbar-collapse',true).first();
5331        
5332         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333             // it's collapsed or collapsing..
5334             return;
5335         }
5336         ce.removeClass('in'); // old...
5337         ce.setHeight(ce.getHeight());
5338         ce.removeClass('show');
5339         ce.addClass('collapsing');
5340         
5341         ce.on('transitionend', function() {
5342             ce.dom.style.height = '';
5343             ce.removeClass('collapsing');
5344             ce.addClass('collapse');
5345         }, this, { single: true} );
5346         ce.setHeight(0);
5347     }
5348     
5349     
5350     
5351 });
5352
5353
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * navbar
5361  * 
5362  */
5363
5364 /**
5365  * @class Roo.bootstrap.NavSimplebar
5366  * @extends Roo.bootstrap.Navbar
5367  * Bootstrap Sidebar class
5368  *
5369  * @cfg {Boolean} inverse is inverted color
5370  * 
5371  * @cfg {String} type (nav | pills | tabs)
5372  * @cfg {Boolean} arrangement stacked | justified
5373  * @cfg {String} align (left | right) alignment
5374  * 
5375  * @cfg {Boolean} main (true|false) main nav bar? default false
5376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377  * 
5378  * @cfg {String} tag (header|footer|nav|div) default is nav 
5379
5380  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381  * 
5382  * 
5383  * @constructor
5384  * Create a new Sidebar
5385  * @param {Object} config The config object
5386  */
5387
5388
5389 Roo.bootstrap.NavSimplebar = function(config){
5390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5391 };
5392
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5394     
5395     inverse: false,
5396     
5397     type: false,
5398     arrangement: '',
5399     align : false,
5400     
5401     weight : 'light',
5402     
5403     main : false,
5404     
5405     
5406     tag : false,
5407     
5408     
5409     getAutoCreate : function(){
5410         
5411         
5412         var cfg = {
5413             tag : this.tag || 'div',
5414             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415         };
5416         if (['light','white'].indexOf(this.weight) > -1) {
5417             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418         }
5419         cfg.cls += ' bg-' + this.weight;
5420         
5421         if (this.inverse) {
5422             cfg.cls += ' navbar-inverse';
5423             
5424         }
5425         
5426         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427         
5428         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5429             return cfg;
5430         }
5431         
5432         
5433     
5434         
5435         cfg.cn = [
5436             {
5437                 cls: 'nav nav-' + this.xtype,
5438                 tag : 'ul'
5439             }
5440         ];
5441         
5442          
5443         this.type = this.type || 'nav';
5444         if (['tabs','pills'].indexOf(this.type) != -1) {
5445             cfg.cn[0].cls += ' nav-' + this.type
5446         
5447         
5448         } else {
5449             if (this.type!=='nav') {
5450                 Roo.log('nav type must be nav/tabs/pills')
5451             }
5452             cfg.cn[0].cls += ' navbar-nav'
5453         }
5454         
5455         
5456         
5457         
5458         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459             cfg.cn[0].cls += ' nav-' + this.arrangement;
5460         }
5461         
5462         
5463         if (this.align === 'right') {
5464             cfg.cn[0].cls += ' navbar-right';
5465         }
5466         
5467         
5468         
5469         
5470         return cfg;
5471     
5472         
5473     }
5474     
5475     
5476     
5477 });
5478
5479
5480
5481  
5482
5483  
5484        /*
5485  * - LGPL
5486  *
5487  * navbar
5488  * navbar-fixed-top
5489  * navbar-expand-md  fixed-top 
5490  */
5491
5492 /**
5493  * @class Roo.bootstrap.NavHeaderbar
5494  * @extends Roo.bootstrap.NavSimplebar
5495  * Bootstrap Sidebar class
5496  *
5497  * @cfg {String} brand what is brand
5498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499  * @cfg {String} brand_href href of the brand
5500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5504  * 
5505  * @constructor
5506  * Create a new Sidebar
5507  * @param {Object} config The config object
5508  */
5509
5510
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513       
5514 };
5515
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5517     
5518     position: '',
5519     brand: '',
5520     brand_href: false,
5521     srButton : true,
5522     autohide : false,
5523     desktopCenter : false,
5524    
5525     
5526     getAutoCreate : function(){
5527         
5528         var   cfg = {
5529             tag: this.nav || 'nav',
5530             cls: 'navbar navbar-expand-md',
5531             role: 'navigation',
5532             cn: []
5533         };
5534         
5535         var cn = cfg.cn;
5536         if (this.desktopCenter) {
5537             cn.push({cls : 'container', cn : []});
5538             cn = cn[0].cn;
5539         }
5540         
5541         if(this.srButton){
5542             var btn = {
5543                 tag: 'button',
5544                 type: 'button',
5545                 cls: 'navbar-toggle navbar-toggler',
5546                 'data-toggle': 'collapse',
5547                 cn: [
5548                     {
5549                         tag: 'span',
5550                         cls: 'sr-only',
5551                         html: 'Toggle navigation'
5552                     },
5553                     {
5554                         tag: 'span',
5555                         cls: 'icon-bar navbar-toggler-icon'
5556                     },
5557                     {
5558                         tag: 'span',
5559                         cls: 'icon-bar'
5560                     },
5561                     {
5562                         tag: 'span',
5563                         cls: 'icon-bar'
5564                     }
5565                 ]
5566             };
5567             
5568             cn.push( Roo.bootstrap.version == 4 ? btn : {
5569                 tag: 'div',
5570                 cls: 'navbar-header',
5571                 cn: [
5572                     btn
5573                 ]
5574             });
5575         }
5576         
5577         cn.push({
5578             tag: 'div',
5579             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580             cn : []
5581         });
5582         
5583         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584         
5585         if (['light','white'].indexOf(this.weight) > -1) {
5586             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587         }
5588         cfg.cls += ' bg-' + this.weight;
5589         
5590         
5591         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593             
5594             // tag can override this..
5595             
5596             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5597         }
5598         
5599         if (this.brand !== '') {
5600             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602                 tag: 'a',
5603                 href: this.brand_href ? this.brand_href : '#',
5604                 cls: 'navbar-brand',
5605                 cn: [
5606                 this.brand
5607                 ]
5608             });
5609         }
5610         
5611         if(this.main){
5612             cfg.cls += ' main-nav';
5613         }
5614         
5615         
5616         return cfg;
5617
5618         
5619     },
5620     getHeaderChildContainer : function()
5621     {
5622         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623             return this.el.select('.navbar-header',true).first();
5624         }
5625         
5626         return this.getChildContainer();
5627     },
5628     
5629     getChildContainer : function()
5630     {
5631          
5632         return this.el.select('.roo-navbar-collapse',true).first();
5633          
5634         
5635     },
5636     
5637     initEvents : function()
5638     {
5639         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640         
5641         if (this.autohide) {
5642             
5643             var prevScroll = 0;
5644             var ft = this.el;
5645             
5646             Roo.get(document).on('scroll',function(e) {
5647                 var ns = Roo.get(document).getScroll().top;
5648                 var os = prevScroll;
5649                 prevScroll = ns;
5650                 
5651                 if(ns > os){
5652                     ft.removeClass('slideDown');
5653                     ft.addClass('slideUp');
5654                     return;
5655                 }
5656                 ft.removeClass('slideUp');
5657                 ft.addClass('slideDown');
5658                  
5659               
5660           },this);
5661         }
5662     }    
5663     
5664 });
5665
5666
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * navbar
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.NavSidebar
5679  * @extends Roo.bootstrap.Navbar
5680  * Bootstrap Sidebar class
5681  * 
5682  * @constructor
5683  * Create a new Sidebar
5684  * @param {Object} config The config object
5685  */
5686
5687
5688 Roo.bootstrap.NavSidebar = function(config){
5689     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5693     
5694     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695     
5696     getAutoCreate : function(){
5697         
5698         
5699         return  {
5700             tag: 'div',
5701             cls: 'sidebar sidebar-nav'
5702         };
5703     
5704         
5705     }
5706     
5707     
5708     
5709 });
5710
5711
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * nav group
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.NavGroup
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap NavGroup class
5726  * @cfg {String} align (left|right)
5727  * @cfg {Boolean} inverse
5728  * @cfg {String} type (nav|pills|tab) default nav
5729  * @cfg {String} navId - reference Id for navbar.
5730
5731  * 
5732  * @constructor
5733  * Create a new nav group
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.NavGroup = function(config){
5738     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5739     this.navItems = [];
5740    
5741     Roo.bootstrap.NavGroup.register(this);
5742      this.addEvents({
5743         /**
5744              * @event changed
5745              * Fires when the active item changes
5746              * @param {Roo.bootstrap.NavGroup} this
5747              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5749          */
5750         'changed': true
5751      });
5752     
5753 };
5754
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5756     
5757     align: '',
5758     inverse: false,
5759     form: false,
5760     type: 'nav',
5761     navId : '',
5762     // private
5763     
5764     navItems : false, 
5765     
5766     getAutoCreate : function()
5767     {
5768         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag : 'ul',
5772             cls: 'nav' 
5773         };
5774         if (Roo.bootstrap.version == 4) {
5775             if (['tabs','pills'].indexOf(this.type) != -1) {
5776                 cfg.cls += ' nav-' + this.type; 
5777             } else {
5778                 // trying to remove so header bar can right align top?
5779                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780                     // do not use on header bar... 
5781                     cfg.cls += ' navbar-nav';
5782                 }
5783             }
5784             
5785         } else {
5786             if (['tabs','pills'].indexOf(this.type) != -1) {
5787                 cfg.cls += ' nav-' + this.type
5788             } else {
5789                 if (this.type !== 'nav') {
5790                     Roo.log('nav type must be nav/tabs/pills')
5791                 }
5792                 cfg.cls += ' navbar-nav'
5793             }
5794         }
5795         
5796         if (this.parent() && this.parent().sidebar) {
5797             cfg = {
5798                 tag: 'ul',
5799                 cls: 'dashboard-menu sidebar-menu'
5800             };
5801             
5802             return cfg;
5803         }
5804         
5805         if (this.form === true) {
5806             cfg = {
5807                 tag: 'form',
5808                 cls: 'navbar-form form-inline'
5809             };
5810             //nav navbar-right ml-md-auto
5811             if (this.align === 'right') {
5812                 cfg.cls += ' navbar-right ml-md-auto';
5813             } else {
5814                 cfg.cls += ' navbar-left';
5815             }
5816         }
5817         
5818         if (this.align === 'right') {
5819             cfg.cls += ' navbar-right ml-md-auto';
5820         } else {
5821             cfg.cls += ' mr-auto';
5822         }
5823         
5824         if (this.inverse) {
5825             cfg.cls += ' navbar-inverse';
5826             
5827         }
5828         
5829         
5830         return cfg;
5831     },
5832     /**
5833     * sets the active Navigation item
5834     * @param {Roo.bootstrap.NavItem} the new current navitem
5835     */
5836     setActiveItem : function(item)
5837     {
5838         var prev = false;
5839         Roo.each(this.navItems, function(v){
5840             if (v == item) {
5841                 return ;
5842             }
5843             if (v.isActive()) {
5844                 v.setActive(false, true);
5845                 prev = v;
5846                 
5847             }
5848             
5849         });
5850
5851         item.setActive(true, true);
5852         this.fireEvent('changed', this, item, prev);
5853         
5854         
5855     },
5856     /**
5857     * gets the active Navigation item
5858     * @return {Roo.bootstrap.NavItem} the current navitem
5859     */
5860     getActive : function()
5861     {
5862         
5863         var prev = false;
5864         Roo.each(this.navItems, function(v){
5865             
5866             if (v.isActive()) {
5867                 prev = v;
5868                 
5869             }
5870             
5871         });
5872         return prev;
5873     },
5874     
5875     indexOfNav : function()
5876     {
5877         
5878         var prev = false;
5879         Roo.each(this.navItems, function(v,i){
5880             
5881             if (v.isActive()) {
5882                 prev = i;
5883                 
5884             }
5885             
5886         });
5887         return prev;
5888     },
5889     /**
5890     * adds a Navigation item
5891     * @param {Roo.bootstrap.NavItem} the navitem to add
5892     */
5893     addItem : function(cfg)
5894     {
5895         if (this.form && Roo.bootstrap.version == 4) {
5896             cfg.tag = 'div';
5897         }
5898         var cn = new Roo.bootstrap.NavItem(cfg);
5899         this.register(cn);
5900         cn.parentId = this.id;
5901         cn.onRender(this.el, null);
5902         return cn;
5903     },
5904     /**
5905     * register a Navigation item
5906     * @param {Roo.bootstrap.NavItem} the navitem to add
5907     */
5908     register : function(item)
5909     {
5910         this.navItems.push( item);
5911         item.navId = this.navId;
5912     
5913     },
5914     
5915     /**
5916     * clear all the Navigation item
5917     */
5918    
5919     clearAll : function()
5920     {
5921         this.navItems = [];
5922         this.el.dom.innerHTML = '';
5923     },
5924     
5925     getNavItem: function(tabId)
5926     {
5927         var ret = false;
5928         Roo.each(this.navItems, function(e) {
5929             if (e.tabId == tabId) {
5930                ret =  e;
5931                return false;
5932             }
5933             return true;
5934             
5935         });
5936         return ret;
5937     },
5938     
5939     setActiveNext : function()
5940     {
5941         var i = this.indexOfNav(this.getActive());
5942         if (i > this.navItems.length) {
5943             return;
5944         }
5945         this.setActiveItem(this.navItems[i+1]);
5946     },
5947     setActivePrev : function()
5948     {
5949         var i = this.indexOfNav(this.getActive());
5950         if (i  < 1) {
5951             return;
5952         }
5953         this.setActiveItem(this.navItems[i-1]);
5954     },
5955     clearWasActive : function(except) {
5956         Roo.each(this.navItems, function(e) {
5957             if (e.tabId != except.tabId && e.was_active) {
5958                e.was_active = false;
5959                return false;
5960             }
5961             return true;
5962             
5963         });
5964     },
5965     getWasActive : function ()
5966     {
5967         var r = false;
5968         Roo.each(this.navItems, function(e) {
5969             if (e.was_active) {
5970                r = e;
5971                return false;
5972             }
5973             return true;
5974             
5975         });
5976         return r;
5977     }
5978     
5979     
5980 });
5981
5982  
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5984     
5985     groups: {},
5986      /**
5987     * register a Navigation Group
5988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989     */
5990     register : function(navgrp)
5991     {
5992         this.groups[navgrp.navId] = navgrp;
5993         
5994     },
5995     /**
5996     * fetch a Navigation Group based on the navigation ID
5997     * @param {string} the navgroup to add
5998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5999     */
6000     get: function(navId) {
6001         if (typeof(this.groups[navId]) == 'undefined') {
6002             return false;
6003             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004         }
6005         return this.groups[navId] ;
6006     }
6007     
6008     
6009     
6010 });
6011
6012  /*
6013  * - LGPL
6014  *
6015  * row
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.NavItem
6021  * @extends Roo.bootstrap.Component
6022  * Bootstrap Navbar.NavItem class
6023  * @cfg {String} href  link to
6024  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025
6026  * @cfg {String} html content of button
6027  * @cfg {String} badge text inside badge
6028  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029  * @cfg {String} glyphicon DEPRICATED - use fa
6030  * @cfg {String} icon DEPRICATED - use fa
6031  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032  * @cfg {Boolean} active Is item active
6033  * @cfg {Boolean} disabled Is item disabled
6034  
6035  * @cfg {Boolean} preventDefault (true | false) default false
6036  * @cfg {String} tabId the tab that this item activates.
6037  * @cfg {String} tagtype (a|span) render as a href or span?
6038  * @cfg {Boolean} animateRef (true|false) link to element default false  
6039   
6040  * @constructor
6041  * Create a new Navbar Item
6042  * @param {Object} config The config object
6043  */
6044 Roo.bootstrap.NavItem = function(config){
6045     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6046     this.addEvents({
6047         // raw events
6048         /**
6049          * @event click
6050          * The raw click event for the entire grid.
6051          * @param {Roo.EventObject} e
6052          */
6053         "click" : true,
6054          /**
6055             * @event changed
6056             * Fires when the active item active state changes
6057             * @param {Roo.bootstrap.NavItem} this
6058             * @param {boolean} state the new state
6059              
6060          */
6061         'changed': true,
6062         /**
6063             * @event scrollto
6064             * Fires when scroll to element
6065             * @param {Roo.bootstrap.NavItem} this
6066             * @param {Object} options
6067             * @param {Roo.EventObject} e
6068              
6069          */
6070         'scrollto': true
6071     });
6072    
6073 };
6074
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6076     
6077     href: false,
6078     html: '',
6079     badge: '',
6080     icon: false,
6081     fa : false,
6082     glyphicon: false,
6083     active: false,
6084     preventDefault : false,
6085     tabId : false,
6086     tagtype : 'a',
6087     tag: 'li',
6088     disabled : false,
6089     animateRef : false,
6090     was_active : false,
6091     button_weight : '',
6092     button_outline : false,
6093     
6094     navLink: false,
6095     
6096     getAutoCreate : function(){
6097          
6098         var cfg = {
6099             tag: this.tag,
6100             cls: 'nav-item'
6101         };
6102         
6103         if (this.active) {
6104             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105         }
6106         if (this.disabled) {
6107             cfg.cls += ' disabled';
6108         }
6109         
6110         // BS4 only?
6111         if (this.button_weight.length) {
6112             cfg.tag = this.href ? 'a' : 'button';
6113             cfg.html = this.html || '';
6114             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115             if (this.href) {
6116                 cfg.href = this.href;
6117             }
6118             if (this.fa) {
6119                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6120             }
6121             
6122             // menu .. should add dropdown-menu class - so no need for carat..
6123             
6124             if (this.badge !== '') {
6125                  
6126                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6127             }
6128             return cfg;
6129         }
6130         
6131         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132             cfg.cn = [
6133                 {
6134                     tag: this.tagtype,
6135                     href : this.href || "#",
6136                     html: this.html || ''
6137                 }
6138             ];
6139             if (this.tagtype == 'a') {
6140                 cfg.cn[0].cls = 'nav-link';
6141             }
6142             if (this.icon) {
6143                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if (this.fa) {
6146                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147             }
6148             if(this.glyphicon) {
6149                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6150             }
6151             
6152             if (this.menu) {
6153                 
6154                 cfg.cn[0].html += " <span class='caret'></span>";
6155              
6156             }
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162         }
6163         
6164         
6165         
6166         return cfg;
6167     },
6168     onRender : function(ct, position)
6169     {
6170        // Roo.log("Call onRender: " + this.xtype);
6171         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172             this.tag = 'div';
6173         }
6174         
6175         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176         this.navLink = this.el.select('.nav-link',true).first();
6177         return ret;
6178     },
6179       
6180     
6181     initEvents: function() 
6182     {
6183         if (typeof (this.menu) != 'undefined') {
6184             this.menu.parentType = this.xtype;
6185             this.menu.triggerEl = this.el;
6186             this.menu = this.addxtype(Roo.apply({}, this.menu));
6187         }
6188         
6189         this.el.select('a',true).on('click', this.onClick, this);
6190         
6191         if(this.tagtype == 'span'){
6192             this.el.select('span',true).on('click', this.onClick, this);
6193         }
6194        
6195         // at this point parent should be available..
6196         this.parent().register(this);
6197     },
6198     
6199     onClick : function(e)
6200     {
6201         if (e.getTarget('.dropdown-menu-item')) {
6202             // did you click on a menu itemm.... - then don't trigger onclick..
6203             return;
6204         }
6205         
6206         if(
6207                 this.preventDefault || 
6208                 this.href == '#' 
6209         ){
6210             Roo.log("NavItem - prevent Default?");
6211             e.preventDefault();
6212         }
6213         
6214         if (this.disabled) {
6215             return;
6216         }
6217         
6218         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219         if (tg && tg.transition) {
6220             Roo.log("waiting for the transitionend");
6221             return;
6222         }
6223         
6224         
6225         
6226         //Roo.log("fire event clicked");
6227         if(this.fireEvent('click', this, e) === false){
6228             return;
6229         };
6230         
6231         if(this.tagtype == 'span'){
6232             return;
6233         }
6234         
6235         //Roo.log(this.href);
6236         var ael = this.el.select('a',true).first();
6237         //Roo.log(ael);
6238         
6239         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242                 return; // ignore... - it's a 'hash' to another page.
6243             }
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246             this.scrollToElement(e);
6247         }
6248         
6249         
6250         var p =  this.parent();
6251    
6252         if (['tabs','pills'].indexOf(p.type)!==-1) {
6253             if (typeof(p.setActiveItem) !== 'undefined') {
6254                 p.setActiveItem(this);
6255             }
6256         }
6257         
6258         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260             // remove the collapsed menu expand...
6261             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6262         }
6263     },
6264     
6265     isActive: function () {
6266         return this.active
6267     },
6268     setActive : function(state, fire, is_was_active)
6269     {
6270         if (this.active && !state && this.navId) {
6271             this.was_active = true;
6272             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273             if (nv) {
6274                 nv.clearWasActive(this);
6275             }
6276             
6277         }
6278         this.active = state;
6279         
6280         if (!state ) {
6281             this.el.removeClass('active');
6282             this.navLink ? this.navLink.removeClass('active') : false;
6283         } else if (!this.el.hasClass('active')) {
6284             
6285             this.el.addClass('active');
6286             if (Roo.bootstrap.version == 4 && this.navLink ) {
6287                 this.navLink.addClass('active');
6288             }
6289             
6290         }
6291         if (fire) {
6292             this.fireEvent('changed', this, state);
6293         }
6294         
6295         // show a panel if it's registered and related..
6296         
6297         if (!this.navId || !this.tabId || !state || is_was_active) {
6298             return;
6299         }
6300         
6301         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302         if (!tg) {
6303             return;
6304         }
6305         var pan = tg.getPanelByName(this.tabId);
6306         if (!pan) {
6307             return;
6308         }
6309         // if we can not flip to new panel - go back to old nav highlight..
6310         if (false == tg.showPanel(pan)) {
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 var onav = nv.getWasActive();
6314                 if (onav) {
6315                     onav.setActive(true, false, true);
6316                 }
6317             }
6318             
6319         }
6320         
6321         
6322         
6323     },
6324      // this should not be here...
6325     setDisabled : function(state)
6326     {
6327         this.disabled = state;
6328         if (!state ) {
6329             this.el.removeClass('disabled');
6330         } else if (!this.el.hasClass('disabled')) {
6331             this.el.addClass('disabled');
6332         }
6333         
6334     },
6335     
6336     /**
6337      * Fetch the element to display the tooltip on.
6338      * @return {Roo.Element} defaults to this.el
6339      */
6340     tooltipEl : function()
6341     {
6342         return this.el.select('' + this.tagtype + '', true).first();
6343     },
6344     
6345     scrollToElement : function(e)
6346     {
6347         var c = document.body;
6348         
6349         /*
6350          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351          */
6352         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353             c = document.documentElement;
6354         }
6355         
6356         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6357         
6358         if(!target){
6359             return;
6360         }
6361
6362         var o = target.calcOffsetsTo(c);
6363         
6364         var options = {
6365             target : target,
6366             value : o[1]
6367         };
6368         
6369         this.fireEvent('scrollto', this, options, e);
6370         
6371         Roo.get(c).scrollTo('top', options.value, true);
6372         
6373         return;
6374     }
6375 });
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * sidebar item
6382  *
6383  *  li
6384  *    <span> icon </span>
6385  *    <span> text </span>
6386  *    <span>badge </span>
6387  */
6388
6389 /**
6390  * @class Roo.bootstrap.NavSidebarItem
6391  * @extends Roo.bootstrap.NavItem
6392  * Bootstrap Navbar.NavSidebarItem class
6393  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394  * {Boolean} open is the menu open
6395  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397  * {String} buttonSize (sm|md|lg)the extra classes for the button
6398  * {Boolean} showArrow show arrow next to the text (default true)
6399  * @constructor
6400  * Create a new Navbar Button
6401  * @param {Object} config The config object
6402  */
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6405     this.addEvents({
6406         // raw events
6407         /**
6408          * @event click
6409          * The raw click event for the entire grid.
6410          * @param {Roo.EventObject} e
6411          */
6412         "click" : true,
6413          /**
6414             * @event changed
6415             * Fires when the active item active state changes
6416             * @param {Roo.bootstrap.NavSidebarItem} this
6417             * @param {boolean} state the new state
6418              
6419          */
6420         'changed': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6426     
6427     badgeWeight : 'default',
6428     
6429     open: false,
6430     
6431     buttonView : false,
6432     
6433     buttonWeight : 'default',
6434     
6435     buttonSize : 'md',
6436     
6437     showArrow : true,
6438     
6439     getAutoCreate : function(){
6440         
6441         
6442         var a = {
6443                 tag: 'a',
6444                 href : this.href || '#',
6445                 cls: '',
6446                 html : '',
6447                 cn : []
6448         };
6449         
6450         if(this.buttonView){
6451             a = {
6452                 tag: 'button',
6453                 href : this.href || '#',
6454                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6455                 html : this.html,
6456                 cn : []
6457             };
6458         }
6459         
6460         var cfg = {
6461             tag: 'li',
6462             cls: '',
6463             cn: [ a ]
6464         };
6465         
6466         if (this.active) {
6467             cfg.cls += ' active';
6468         }
6469         
6470         if (this.disabled) {
6471             cfg.cls += ' disabled';
6472         }
6473         if (this.open) {
6474             cfg.cls += ' open x-open';
6475         }
6476         // left icon..
6477         if (this.glyphicon || this.icon) {
6478             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6479             a.cn.push({ tag : 'i', cls : c }) ;
6480         }
6481         
6482         if(!this.buttonView){
6483             var span = {
6484                 tag: 'span',
6485                 html : this.html || ''
6486             };
6487
6488             a.cn.push(span);
6489             
6490         }
6491         
6492         if (this.badge !== '') {
6493             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6494         }
6495         
6496         if (this.menu) {
6497             
6498             if(this.showArrow){
6499                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6500             }
6501             
6502             a.cls += ' dropdown-toggle treeview' ;
6503         }
6504         
6505         return cfg;
6506     },
6507     
6508     initEvents : function()
6509     { 
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         if(this.badge !== ''){
6519             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6520         }
6521         
6522     },
6523     
6524     onClick : function(e)
6525     {
6526         if(this.disabled){
6527             e.preventDefault();
6528             return;
6529         }
6530         
6531         if(this.preventDefault){
6532             e.preventDefault();
6533         }
6534         
6535         this.fireEvent('click', this, e);
6536     },
6537     
6538     disable : function()
6539     {
6540         this.setDisabled(true);
6541     },
6542     
6543     enable : function()
6544     {
6545         this.setDisabled(false);
6546     },
6547     
6548     setDisabled : function(state)
6549     {
6550         if(this.disabled == state){
6551             return;
6552         }
6553         
6554         this.disabled = state;
6555         
6556         if (state) {
6557             this.el.addClass('disabled');
6558             return;
6559         }
6560         
6561         this.el.removeClass('disabled');
6562         
6563         return;
6564     },
6565     
6566     setActive : function(state)
6567     {
6568         if(this.active == state){
6569             return;
6570         }
6571         
6572         this.active = state;
6573         
6574         if (state) {
6575             this.el.addClass('active');
6576             return;
6577         }
6578         
6579         this.el.removeClass('active');
6580         
6581         return;
6582     },
6583     
6584     isActive: function () 
6585     {
6586         return this.active;
6587     },
6588     
6589     setBadge : function(str)
6590     {
6591         if(!this.badgeEl){
6592             return;
6593         }
6594         
6595         this.badgeEl.dom.innerHTML = str;
6596     }
6597     
6598    
6599      
6600  
6601 });
6602  
6603
6604  /*
6605  * - LGPL
6606  *
6607  *  Breadcrumb Nav
6608  * 
6609  */
6610
6611
6612 /**
6613  * @class Roo.bootstrap.breadcrumb.Nav
6614  * @extends Roo.bootstrap.Component
6615  * Bootstrap Breadcrumb Nav Class
6616  *  
6617  * @children Roo.bootstrap.breadcrumb.Item
6618  * 
6619  * @constructor
6620  * Create a new breadcrumb.Nav
6621  * @param {Object} config The config object
6622  */
6623
6624 Roo.bootstrap.breadcrumb.Nav = function(config){
6625     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6626     
6627     
6628 };
6629
6630 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6631     
6632     getAutoCreate : function()
6633     {
6634
6635         var cfg = {
6636             tag: 'nav',
6637             cn : [
6638                 {
6639                     tag : 'ol',
6640                     cls : 'breadcrumb'
6641                 }
6642             ]
6643             
6644         };
6645           
6646         return cfg;
6647     },
6648     
6649     initEvents: function()
6650     {
6651         this.olEl = this.el.select('ol',true).first();    
6652     },
6653     getChildContainer : function()
6654     {
6655         return this.olEl;  
6656     }
6657     
6658 });
6659
6660  /*
6661  * - LGPL
6662  *
6663  *  Breadcrumb Item
6664  * 
6665  */
6666
6667
6668 /**
6669  * @class Roo.bootstrap.breadcrumb.Nav
6670  * @extends Roo.bootstrap.Component
6671  * Bootstrap Breadcrumb Nav Class
6672  *  
6673  * @children Roo.bootstrap.breadcrumb.Component
6674  * @cfg {String} html the content of the link.
6675  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6676  * @cfg {Boolean} active is it active
6677
6678  * 
6679  * @constructor
6680  * Create a new breadcrumb.Nav
6681  * @param {Object} config The config object
6682  */
6683
6684 Roo.bootstrap.breadcrumb.Item = function(config){
6685     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6686     this.addEvents({
6687         // img events
6688         /**
6689          * @event click
6690          * The img click event for the img.
6691          * @param {Roo.EventObject} e
6692          */
6693         "click" : true
6694     });
6695     
6696 };
6697
6698 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6699     
6700     href: false,
6701     html : '',
6702     
6703     getAutoCreate : function()
6704     {
6705
6706         var cfg = {
6707             tag: 'li',
6708             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6709         };
6710         if (this.href !== false) {
6711             cfg.cn = [{
6712                 tag : 'a',
6713                 href : this.href,
6714                 html : this.html
6715             }];
6716         } else {
6717             cfg.html = this.html;
6718         }
6719         
6720         return cfg;
6721     },
6722     
6723     initEvents: function()
6724     {
6725         if (this.href) {
6726             this.el.select('a', true).first().onClick(this.onClick, this)
6727         }
6728         
6729     },
6730     onClick : function(e)
6731     {
6732         e.preventDefault();
6733         this.fireEvent('click',this,  e);
6734     }
6735     
6736 });
6737
6738  /*
6739  * - LGPL
6740  *
6741  * row
6742  * 
6743  */
6744
6745 /**
6746  * @class Roo.bootstrap.Row
6747  * @extends Roo.bootstrap.Component
6748  * Bootstrap Row class (contains columns...)
6749  * 
6750  * @constructor
6751  * Create a new Row
6752  * @param {Object} config The config object
6753  */
6754
6755 Roo.bootstrap.Row = function(config){
6756     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6757 };
6758
6759 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6760     
6761     getAutoCreate : function(){
6762        return {
6763             cls: 'row clearfix'
6764        };
6765     }
6766     
6767     
6768 });
6769
6770  
6771
6772  /*
6773  * - LGPL
6774  *
6775  * pagination
6776  * 
6777  */
6778
6779 /**
6780  * @class Roo.bootstrap.Pagination
6781  * @extends Roo.bootstrap.Component
6782  * Bootstrap Pagination class
6783  * @cfg {String} size xs | sm | md | lg
6784  * @cfg {Boolean} inverse false | true
6785  * 
6786  * @constructor
6787  * Create a new Pagination
6788  * @param {Object} config The config object
6789  */
6790
6791 Roo.bootstrap.Pagination = function(config){
6792     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6793 };
6794
6795 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6796     
6797     cls: false,
6798     size: false,
6799     inverse: false,
6800     
6801     getAutoCreate : function(){
6802         var cfg = {
6803             tag: 'ul',
6804                 cls: 'pagination'
6805         };
6806         if (this.inverse) {
6807             cfg.cls += ' inverse';
6808         }
6809         if (this.html) {
6810             cfg.html=this.html;
6811         }
6812         if (this.cls) {
6813             cfg.cls += " " + this.cls;
6814         }
6815         return cfg;
6816     }
6817    
6818 });
6819
6820  
6821
6822  /*
6823  * - LGPL
6824  *
6825  * Pagination item
6826  * 
6827  */
6828
6829
6830 /**
6831  * @class Roo.bootstrap.PaginationItem
6832  * @extends Roo.bootstrap.Component
6833  * Bootstrap PaginationItem class
6834  * @cfg {String} html text
6835  * @cfg {String} href the link
6836  * @cfg {Boolean} preventDefault (true | false) default true
6837  * @cfg {Boolean} active (true | false) default false
6838  * @cfg {Boolean} disabled default false
6839  * 
6840  * 
6841  * @constructor
6842  * Create a new PaginationItem
6843  * @param {Object} config The config object
6844  */
6845
6846
6847 Roo.bootstrap.PaginationItem = function(config){
6848     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6849     this.addEvents({
6850         // raw events
6851         /**
6852          * @event click
6853          * The raw click event for the entire grid.
6854          * @param {Roo.EventObject} e
6855          */
6856         "click" : true
6857     });
6858 };
6859
6860 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6861     
6862     href : false,
6863     html : false,
6864     preventDefault: true,
6865     active : false,
6866     cls : false,
6867     disabled: false,
6868     
6869     getAutoCreate : function(){
6870         var cfg= {
6871             tag: 'li',
6872             cn: [
6873                 {
6874                     tag : 'a',
6875                     href : this.href ? this.href : '#',
6876                     html : this.html ? this.html : ''
6877                 }
6878             ]
6879         };
6880         
6881         if(this.cls){
6882             cfg.cls = this.cls;
6883         }
6884         
6885         if(this.disabled){
6886             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6887         }
6888         
6889         if(this.active){
6890             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6891         }
6892         
6893         return cfg;
6894     },
6895     
6896     initEvents: function() {
6897         
6898         this.el.on('click', this.onClick, this);
6899         
6900     },
6901     onClick : function(e)
6902     {
6903         Roo.log('PaginationItem on click ');
6904         if(this.preventDefault){
6905             e.preventDefault();
6906         }
6907         
6908         if(this.disabled){
6909             return;
6910         }
6911         
6912         this.fireEvent('click', this, e);
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * slider
6923  * 
6924  */
6925
6926
6927 /**
6928  * @class Roo.bootstrap.Slider
6929  * @extends Roo.bootstrap.Component
6930  * Bootstrap Slider class
6931  *    
6932  * @constructor
6933  * Create a new Slider
6934  * @param {Object} config The config object
6935  */
6936
6937 Roo.bootstrap.Slider = function(config){
6938     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6939 };
6940
6941 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6942     
6943     getAutoCreate : function(){
6944         
6945         var cfg = {
6946             tag: 'div',
6947             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6948             cn: [
6949                 {
6950                     tag: 'a',
6951                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6952                 }
6953             ]
6954         };
6955         
6956         return cfg;
6957     }
6958    
6959 });
6960
6961  /*
6962  * Based on:
6963  * Ext JS Library 1.1.1
6964  * Copyright(c) 2006-2007, Ext JS, LLC.
6965  *
6966  * Originally Released Under LGPL - original licence link has changed is not relivant.
6967  *
6968  * Fork - LGPL
6969  * <script type="text/javascript">
6970  */
6971  
6972
6973 /**
6974  * @class Roo.grid.ColumnModel
6975  * @extends Roo.util.Observable
6976  * This is the default implementation of a ColumnModel used by the Grid. It defines
6977  * the columns in the grid.
6978  * <br>Usage:<br>
6979  <pre><code>
6980  var colModel = new Roo.grid.ColumnModel([
6981         {header: "Ticker", width: 60, sortable: true, locked: true},
6982         {header: "Company Name", width: 150, sortable: true},
6983         {header: "Market Cap.", width: 100, sortable: true},
6984         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6985         {header: "Employees", width: 100, sortable: true, resizable: false}
6986  ]);
6987  </code></pre>
6988  * <p>
6989  
6990  * The config options listed for this class are options which may appear in each
6991  * individual column definition.
6992  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6993  * @constructor
6994  * @param {Object} config An Array of column config objects. See this class's
6995  * config objects for details.
6996 */
6997 Roo.grid.ColumnModel = function(config){
6998         /**
6999      * The config passed into the constructor
7000      */
7001     this.config = config;
7002     this.lookup = {};
7003
7004     // if no id, create one
7005     // if the column does not have a dataIndex mapping,
7006     // map it to the order it is in the config
7007     for(var i = 0, len = config.length; i < len; i++){
7008         var c = config[i];
7009         if(typeof c.dataIndex == "undefined"){
7010             c.dataIndex = i;
7011         }
7012         if(typeof c.renderer == "string"){
7013             c.renderer = Roo.util.Format[c.renderer];
7014         }
7015         if(typeof c.id == "undefined"){
7016             c.id = Roo.id();
7017         }
7018         if(c.editor && c.editor.xtype){
7019             c.editor  = Roo.factory(c.editor, Roo.grid);
7020         }
7021         if(c.editor && c.editor.isFormField){
7022             c.editor = new Roo.grid.GridEditor(c.editor);
7023         }
7024         this.lookup[c.id] = c;
7025     }
7026
7027     /**
7028      * The width of columns which have no width specified (defaults to 100)
7029      * @type Number
7030      */
7031     this.defaultWidth = 100;
7032
7033     /**
7034      * Default sortable of columns which have no sortable specified (defaults to false)
7035      * @type Boolean
7036      */
7037     this.defaultSortable = false;
7038
7039     this.addEvents({
7040         /**
7041              * @event widthchange
7042              * Fires when the width of a column changes.
7043              * @param {ColumnModel} this
7044              * @param {Number} columnIndex The column index
7045              * @param {Number} newWidth The new width
7046              */
7047             "widthchange": true,
7048         /**
7049              * @event headerchange
7050              * Fires when the text of a header changes.
7051              * @param {ColumnModel} this
7052              * @param {Number} columnIndex The column index
7053              * @param {Number} newText The new header text
7054              */
7055             "headerchange": true,
7056         /**
7057              * @event hiddenchange
7058              * Fires when a column is hidden or "unhidden".
7059              * @param {ColumnModel} this
7060              * @param {Number} columnIndex The column index
7061              * @param {Boolean} hidden true if hidden, false otherwise
7062              */
7063             "hiddenchange": true,
7064             /**
7065          * @event columnmoved
7066          * Fires when a column is moved.
7067          * @param {ColumnModel} this
7068          * @param {Number} oldIndex
7069          * @param {Number} newIndex
7070          */
7071         "columnmoved" : true,
7072         /**
7073          * @event columlockchange
7074          * Fires when a column's locked state is changed
7075          * @param {ColumnModel} this
7076          * @param {Number} colIndex
7077          * @param {Boolean} locked true if locked
7078          */
7079         "columnlockchange" : true
7080     });
7081     Roo.grid.ColumnModel.superclass.constructor.call(this);
7082 };
7083 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7084     /**
7085      * @cfg {String} header The header text to display in the Grid view.
7086      */
7087     /**
7088      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7089      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7090      * specified, the column's index is used as an index into the Record's data Array.
7091      */
7092     /**
7093      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7094      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7095      */
7096     /**
7097      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7098      * Defaults to the value of the {@link #defaultSortable} property.
7099      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7100      */
7101     /**
7102      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7103      */
7104     /**
7105      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7106      */
7107     /**
7108      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7109      */
7110     /**
7111      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7112      */
7113     /**
7114      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7115      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7116      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7117      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7118      */
7119        /**
7120      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7121      */
7122     /**
7123      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7124      */
7125     /**
7126      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7127      */
7128     /**
7129      * @cfg {String} cursor (Optional)
7130      */
7131     /**
7132      * @cfg {String} tooltip (Optional)
7133      */
7134     /**
7135      * @cfg {Number} xs (Optional)
7136      */
7137     /**
7138      * @cfg {Number} sm (Optional)
7139      */
7140     /**
7141      * @cfg {Number} md (Optional)
7142      */
7143     /**
7144      * @cfg {Number} lg (Optional)
7145      */
7146     /**
7147      * Returns the id of the column at the specified index.
7148      * @param {Number} index The column index
7149      * @return {String} the id
7150      */
7151     getColumnId : function(index){
7152         return this.config[index].id;
7153     },
7154
7155     /**
7156      * Returns the column for a specified id.
7157      * @param {String} id The column id
7158      * @return {Object} the column
7159      */
7160     getColumnById : function(id){
7161         return this.lookup[id];
7162     },
7163
7164     
7165     /**
7166      * Returns the column for a specified dataIndex.
7167      * @param {String} dataIndex The column dataIndex
7168      * @return {Object|Boolean} the column or false if not found
7169      */
7170     getColumnByDataIndex: function(dataIndex){
7171         var index = this.findColumnIndex(dataIndex);
7172         return index > -1 ? this.config[index] : false;
7173     },
7174     
7175     /**
7176      * Returns the index for a specified column id.
7177      * @param {String} id The column id
7178      * @return {Number} the index, or -1 if not found
7179      */
7180     getIndexById : function(id){
7181         for(var i = 0, len = this.config.length; i < len; i++){
7182             if(this.config[i].id == id){
7183                 return i;
7184             }
7185         }
7186         return -1;
7187     },
7188     
7189     /**
7190      * Returns the index for a specified column dataIndex.
7191      * @param {String} dataIndex The column dataIndex
7192      * @return {Number} the index, or -1 if not found
7193      */
7194     
7195     findColumnIndex : function(dataIndex){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].dataIndex == dataIndex){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     
7205     moveColumn : function(oldIndex, newIndex){
7206         var c = this.config[oldIndex];
7207         this.config.splice(oldIndex, 1);
7208         this.config.splice(newIndex, 0, c);
7209         this.dataMap = null;
7210         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7211     },
7212
7213     isLocked : function(colIndex){
7214         return this.config[colIndex].locked === true;
7215     },
7216
7217     setLocked : function(colIndex, value, suppressEvent){
7218         if(this.isLocked(colIndex) == value){
7219             return;
7220         }
7221         this.config[colIndex].locked = value;
7222         if(!suppressEvent){
7223             this.fireEvent("columnlockchange", this, colIndex, value);
7224         }
7225     },
7226
7227     getTotalLockedWidth : function(){
7228         var totalWidth = 0;
7229         for(var i = 0; i < this.config.length; i++){
7230             if(this.isLocked(i) && !this.isHidden(i)){
7231                 this.totalWidth += this.getColumnWidth(i);
7232             }
7233         }
7234         return totalWidth;
7235     },
7236
7237     getLockedCount : function(){
7238         for(var i = 0, len = this.config.length; i < len; i++){
7239             if(!this.isLocked(i)){
7240                 return i;
7241             }
7242         }
7243         
7244         return this.config.length;
7245     },
7246
7247     /**
7248      * Returns the number of columns.
7249      * @return {Number}
7250      */
7251     getColumnCount : function(visibleOnly){
7252         if(visibleOnly === true){
7253             var c = 0;
7254             for(var i = 0, len = this.config.length; i < len; i++){
7255                 if(!this.isHidden(i)){
7256                     c++;
7257                 }
7258             }
7259             return c;
7260         }
7261         return this.config.length;
7262     },
7263
7264     /**
7265      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7266      * @param {Function} fn
7267      * @param {Object} scope (optional)
7268      * @return {Array} result
7269      */
7270     getColumnsBy : function(fn, scope){
7271         var r = [];
7272         for(var i = 0, len = this.config.length; i < len; i++){
7273             var c = this.config[i];
7274             if(fn.call(scope||this, c, i) === true){
7275                 r[r.length] = c;
7276             }
7277         }
7278         return r;
7279     },
7280
7281     /**
7282      * Returns true if the specified column is sortable.
7283      * @param {Number} col The column index
7284      * @return {Boolean}
7285      */
7286     isSortable : function(col){
7287         if(typeof this.config[col].sortable == "undefined"){
7288             return this.defaultSortable;
7289         }
7290         return this.config[col].sortable;
7291     },
7292
7293     /**
7294      * Returns the rendering (formatting) function defined for the column.
7295      * @param {Number} col The column index.
7296      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7297      */
7298     getRenderer : function(col){
7299         if(!this.config[col].renderer){
7300             return Roo.grid.ColumnModel.defaultRenderer;
7301         }
7302         return this.config[col].renderer;
7303     },
7304
7305     /**
7306      * Sets the rendering (formatting) function for a column.
7307      * @param {Number} col The column index
7308      * @param {Function} fn The function to use to process the cell's raw data
7309      * to return HTML markup for the grid view. The render function is called with
7310      * the following parameters:<ul>
7311      * <li>Data value.</li>
7312      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7313      * <li>css A CSS style string to apply to the table cell.</li>
7314      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7315      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7316      * <li>Row index</li>
7317      * <li>Column index</li>
7318      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7319      */
7320     setRenderer : function(col, fn){
7321         this.config[col].renderer = fn;
7322     },
7323
7324     /**
7325      * Returns the width for the specified column.
7326      * @param {Number} col The column index
7327      * @return {Number}
7328      */
7329     getColumnWidth : function(col){
7330         return this.config[col].width * 1 || this.defaultWidth;
7331     },
7332
7333     /**
7334      * Sets the width for a column.
7335      * @param {Number} col The column index
7336      * @param {Number} width The new width
7337      */
7338     setColumnWidth : function(col, width, suppressEvent){
7339         this.config[col].width = width;
7340         this.totalWidth = null;
7341         if(!suppressEvent){
7342              this.fireEvent("widthchange", this, col, width);
7343         }
7344     },
7345
7346     /**
7347      * Returns the total width of all columns.
7348      * @param {Boolean} includeHidden True to include hidden column widths
7349      * @return {Number}
7350      */
7351     getTotalWidth : function(includeHidden){
7352         if(!this.totalWidth){
7353             this.totalWidth = 0;
7354             for(var i = 0, len = this.config.length; i < len; i++){
7355                 if(includeHidden || !this.isHidden(i)){
7356                     this.totalWidth += this.getColumnWidth(i);
7357                 }
7358             }
7359         }
7360         return this.totalWidth;
7361     },
7362
7363     /**
7364      * Returns the header for the specified column.
7365      * @param {Number} col The column index
7366      * @return {String}
7367      */
7368     getColumnHeader : function(col){
7369         return this.config[col].header;
7370     },
7371
7372     /**
7373      * Sets the header for a column.
7374      * @param {Number} col The column index
7375      * @param {String} header The new header
7376      */
7377     setColumnHeader : function(col, header){
7378         this.config[col].header = header;
7379         this.fireEvent("headerchange", this, col, header);
7380     },
7381
7382     /**
7383      * Returns the tooltip for the specified column.
7384      * @param {Number} col The column index
7385      * @return {String}
7386      */
7387     getColumnTooltip : function(col){
7388             return this.config[col].tooltip;
7389     },
7390     /**
7391      * Sets the tooltip for a column.
7392      * @param {Number} col The column index
7393      * @param {String} tooltip The new tooltip
7394      */
7395     setColumnTooltip : function(col, tooltip){
7396             this.config[col].tooltip = tooltip;
7397     },
7398
7399     /**
7400      * Returns the dataIndex for the specified column.
7401      * @param {Number} col The column index
7402      * @return {Number}
7403      */
7404     getDataIndex : function(col){
7405         return this.config[col].dataIndex;
7406     },
7407
7408     /**
7409      * Sets the dataIndex for a column.
7410      * @param {Number} col The column index
7411      * @param {Number} dataIndex The new dataIndex
7412      */
7413     setDataIndex : function(col, dataIndex){
7414         this.config[col].dataIndex = dataIndex;
7415     },
7416
7417     
7418     
7419     /**
7420      * Returns true if the cell is editable.
7421      * @param {Number} colIndex The column index
7422      * @param {Number} rowIndex The row index - this is nto actually used..?
7423      * @return {Boolean}
7424      */
7425     isCellEditable : function(colIndex, rowIndex){
7426         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7427     },
7428
7429     /**
7430      * Returns the editor defined for the cell/column.
7431      * return false or null to disable editing.
7432      * @param {Number} colIndex The column index
7433      * @param {Number} rowIndex The row index
7434      * @return {Object}
7435      */
7436     getCellEditor : function(colIndex, rowIndex){
7437         return this.config[colIndex].editor;
7438     },
7439
7440     /**
7441      * Sets if a column is editable.
7442      * @param {Number} col The column index
7443      * @param {Boolean} editable True if the column is editable
7444      */
7445     setEditable : function(col, editable){
7446         this.config[col].editable = editable;
7447     },
7448
7449
7450     /**
7451      * Returns true if the column is hidden.
7452      * @param {Number} colIndex The column index
7453      * @return {Boolean}
7454      */
7455     isHidden : function(colIndex){
7456         return this.config[colIndex].hidden;
7457     },
7458
7459
7460     /**
7461      * Returns true if the column width cannot be changed
7462      */
7463     isFixed : function(colIndex){
7464         return this.config[colIndex].fixed;
7465     },
7466
7467     /**
7468      * Returns true if the column can be resized
7469      * @return {Boolean}
7470      */
7471     isResizable : function(colIndex){
7472         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7473     },
7474     /**
7475      * Sets if a column is hidden.
7476      * @param {Number} colIndex The column index
7477      * @param {Boolean} hidden True if the column is hidden
7478      */
7479     setHidden : function(colIndex, hidden){
7480         this.config[colIndex].hidden = hidden;
7481         this.totalWidth = null;
7482         this.fireEvent("hiddenchange", this, colIndex, hidden);
7483     },
7484
7485     /**
7486      * Sets the editor for a column.
7487      * @param {Number} col The column index
7488      * @param {Object} editor The editor object
7489      */
7490     setEditor : function(col, editor){
7491         this.config[col].editor = editor;
7492     }
7493 });
7494
7495 Roo.grid.ColumnModel.defaultRenderer = function(value)
7496 {
7497     if(typeof value == "object") {
7498         return value;
7499     }
7500         if(typeof value == "string" && value.length < 1){
7501             return "&#160;";
7502         }
7503     
7504         return String.format("{0}", value);
7505 };
7506
7507 // Alias for backwards compatibility
7508 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7509 /*
7510  * Based on:
7511  * Ext JS Library 1.1.1
7512  * Copyright(c) 2006-2007, Ext JS, LLC.
7513  *
7514  * Originally Released Under LGPL - original licence link has changed is not relivant.
7515  *
7516  * Fork - LGPL
7517  * <script type="text/javascript">
7518  */
7519  
7520 /**
7521  * @class Roo.LoadMask
7522  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7523  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7524  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7525  * element's UpdateManager load indicator and will be destroyed after the initial load.
7526  * @constructor
7527  * Create a new LoadMask
7528  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7529  * @param {Object} config The config object
7530  */
7531 Roo.LoadMask = function(el, config){
7532     this.el = Roo.get(el);
7533     Roo.apply(this, config);
7534     if(this.store){
7535         this.store.on('beforeload', this.onBeforeLoad, this);
7536         this.store.on('load', this.onLoad, this);
7537         this.store.on('loadexception', this.onLoadException, this);
7538         this.removeMask = false;
7539     }else{
7540         var um = this.el.getUpdateManager();
7541         um.showLoadIndicator = false; // disable the default indicator
7542         um.on('beforeupdate', this.onBeforeLoad, this);
7543         um.on('update', this.onLoad, this);
7544         um.on('failure', this.onLoad, this);
7545         this.removeMask = true;
7546     }
7547 };
7548
7549 Roo.LoadMask.prototype = {
7550     /**
7551      * @cfg {Boolean} removeMask
7552      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7553      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7554      */
7555     /**
7556      * @cfg {String} msg
7557      * The text to display in a centered loading message box (defaults to 'Loading...')
7558      */
7559     msg : 'Loading...',
7560     /**
7561      * @cfg {String} msgCls
7562      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7563      */
7564     msgCls : 'x-mask-loading',
7565
7566     /**
7567      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7568      * @type Boolean
7569      */
7570     disabled: false,
7571
7572     /**
7573      * Disables the mask to prevent it from being displayed
7574      */
7575     disable : function(){
7576        this.disabled = true;
7577     },
7578
7579     /**
7580      * Enables the mask so that it can be displayed
7581      */
7582     enable : function(){
7583         this.disabled = false;
7584     },
7585     
7586     onLoadException : function()
7587     {
7588         Roo.log(arguments);
7589         
7590         if (typeof(arguments[3]) != 'undefined') {
7591             Roo.MessageBox.alert("Error loading",arguments[3]);
7592         } 
7593         /*
7594         try {
7595             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7596                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7597             }   
7598         } catch(e) {
7599             
7600         }
7601         */
7602     
7603         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7604     },
7605     // private
7606     onLoad : function()
7607     {
7608         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7609     },
7610
7611     // private
7612     onBeforeLoad : function(){
7613         if(!this.disabled){
7614             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7615         }
7616     },
7617
7618     // private
7619     destroy : function(){
7620         if(this.store){
7621             this.store.un('beforeload', this.onBeforeLoad, this);
7622             this.store.un('load', this.onLoad, this);
7623             this.store.un('loadexception', this.onLoadException, this);
7624         }else{
7625             var um = this.el.getUpdateManager();
7626             um.un('beforeupdate', this.onBeforeLoad, this);
7627             um.un('update', this.onLoad, this);
7628             um.un('failure', this.onLoad, this);
7629         }
7630     }
7631 };/*
7632  * - LGPL
7633  *
7634  * table
7635  * 
7636  */
7637
7638 /**
7639  * @class Roo.bootstrap.Table
7640  * @extends Roo.bootstrap.Component
7641  * Bootstrap Table class
7642  * @cfg {String} cls table class
7643  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7644  * @cfg {String} bgcolor Specifies the background color for a table
7645  * @cfg {Number} border Specifies whether the table cells should have borders or not
7646  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7647  * @cfg {Number} cellspacing Specifies the space between cells
7648  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7649  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7650  * @cfg {String} sortable Specifies that the table should be sortable
7651  * @cfg {String} summary Specifies a summary of the content of a table
7652  * @cfg {Number} width Specifies the width of a table
7653  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7654  * 
7655  * @cfg {boolean} striped Should the rows be alternative striped
7656  * @cfg {boolean} bordered Add borders to the table
7657  * @cfg {boolean} hover Add hover highlighting
7658  * @cfg {boolean} condensed Format condensed
7659  * @cfg {boolean} responsive Format condensed
7660  * @cfg {Boolean} loadMask (true|false) default false
7661  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7662  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7663  * @cfg {Boolean} rowSelection (true|false) default false
7664  * @cfg {Boolean} cellSelection (true|false) default false
7665  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7666  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7667  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7668  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7669  
7670  * 
7671  * @constructor
7672  * Create a new Table
7673  * @param {Object} config The config object
7674  */
7675
7676 Roo.bootstrap.Table = function(config){
7677     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7678     
7679   
7680     
7681     // BC...
7682     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7683     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7684     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7685     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7686     
7687     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7688     if (this.sm) {
7689         this.sm.grid = this;
7690         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7691         this.sm = this.selModel;
7692         this.sm.xmodule = this.xmodule || false;
7693     }
7694     
7695     if (this.cm && typeof(this.cm.config) == 'undefined') {
7696         this.colModel = new Roo.grid.ColumnModel(this.cm);
7697         this.cm = this.colModel;
7698         this.cm.xmodule = this.xmodule || false;
7699     }
7700     if (this.store) {
7701         this.store= Roo.factory(this.store, Roo.data);
7702         this.ds = this.store;
7703         this.ds.xmodule = this.xmodule || false;
7704          
7705     }
7706     if (this.footer && this.store) {
7707         this.footer.dataSource = this.ds;
7708         this.footer = Roo.factory(this.footer);
7709     }
7710     
7711     /** @private */
7712     this.addEvents({
7713         /**
7714          * @event cellclick
7715          * Fires when a cell is clicked
7716          * @param {Roo.bootstrap.Table} this
7717          * @param {Roo.Element} el
7718          * @param {Number} rowIndex
7719          * @param {Number} columnIndex
7720          * @param {Roo.EventObject} e
7721          */
7722         "cellclick" : true,
7723         /**
7724          * @event celldblclick
7725          * Fires when a cell is double clicked
7726          * @param {Roo.bootstrap.Table} this
7727          * @param {Roo.Element} el
7728          * @param {Number} rowIndex
7729          * @param {Number} columnIndex
7730          * @param {Roo.EventObject} e
7731          */
7732         "celldblclick" : true,
7733         /**
7734          * @event rowclick
7735          * Fires when a row is clicked
7736          * @param {Roo.bootstrap.Table} this
7737          * @param {Roo.Element} el
7738          * @param {Number} rowIndex
7739          * @param {Roo.EventObject} e
7740          */
7741         "rowclick" : true,
7742         /**
7743          * @event rowdblclick
7744          * Fires when a row is double clicked
7745          * @param {Roo.bootstrap.Table} this
7746          * @param {Roo.Element} el
7747          * @param {Number} rowIndex
7748          * @param {Roo.EventObject} e
7749          */
7750         "rowdblclick" : true,
7751         /**
7752          * @event mouseover
7753          * Fires when a mouseover occur
7754          * @param {Roo.bootstrap.Table} this
7755          * @param {Roo.Element} el
7756          * @param {Number} rowIndex
7757          * @param {Number} columnIndex
7758          * @param {Roo.EventObject} e
7759          */
7760         "mouseover" : true,
7761         /**
7762          * @event mouseout
7763          * Fires when a mouseout occur
7764          * @param {Roo.bootstrap.Table} this
7765          * @param {Roo.Element} el
7766          * @param {Number} rowIndex
7767          * @param {Number} columnIndex
7768          * @param {Roo.EventObject} e
7769          */
7770         "mouseout" : true,
7771         /**
7772          * @event rowclass
7773          * Fires when a row is rendered, so you can change add a style to it.
7774          * @param {Roo.bootstrap.Table} this
7775          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7776          */
7777         'rowclass' : true,
7778           /**
7779          * @event rowsrendered
7780          * Fires when all the  rows have been rendered
7781          * @param {Roo.bootstrap.Table} this
7782          */
7783         'rowsrendered' : true,
7784         /**
7785          * @event contextmenu
7786          * The raw contextmenu event for the entire grid.
7787          * @param {Roo.EventObject} e
7788          */
7789         "contextmenu" : true,
7790         /**
7791          * @event rowcontextmenu
7792          * Fires when a row is right clicked
7793          * @param {Roo.bootstrap.Table} this
7794          * @param {Number} rowIndex
7795          * @param {Roo.EventObject} e
7796          */
7797         "rowcontextmenu" : true,
7798         /**
7799          * @event cellcontextmenu
7800          * Fires when a cell is right clicked
7801          * @param {Roo.bootstrap.Table} this
7802          * @param {Number} rowIndex
7803          * @param {Number} cellIndex
7804          * @param {Roo.EventObject} e
7805          */
7806          "cellcontextmenu" : true,
7807          /**
7808          * @event headercontextmenu
7809          * Fires when a header is right clicked
7810          * @param {Roo.bootstrap.Table} this
7811          * @param {Number} columnIndex
7812          * @param {Roo.EventObject} e
7813          */
7814         "headercontextmenu" : true
7815     });
7816 };
7817
7818 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7819     
7820     cls: false,
7821     align: false,
7822     bgcolor: false,
7823     border: false,
7824     cellpadding: false,
7825     cellspacing: false,
7826     frame: false,
7827     rules: false,
7828     sortable: false,
7829     summary: false,
7830     width: false,
7831     striped : false,
7832     scrollBody : false,
7833     bordered: false,
7834     hover:  false,
7835     condensed : false,
7836     responsive : false,
7837     sm : false,
7838     cm : false,
7839     store : false,
7840     loadMask : false,
7841     footerShow : true,
7842     headerShow : true,
7843   
7844     rowSelection : false,
7845     cellSelection : false,
7846     layout : false,
7847     
7848     // Roo.Element - the tbody
7849     mainBody: false,
7850     // Roo.Element - thead element
7851     mainHead: false,
7852     
7853     container: false, // used by gridpanel...
7854     
7855     lazyLoad : false,
7856     
7857     CSS : Roo.util.CSS,
7858     
7859     auto_hide_footer : false,
7860     
7861     getAutoCreate : function()
7862     {
7863         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7864         
7865         cfg = {
7866             tag: 'table',
7867             cls : 'table',
7868             cn : []
7869         };
7870         if (this.scrollBody) {
7871             cfg.cls += ' table-body-fixed';
7872         }    
7873         if (this.striped) {
7874             cfg.cls += ' table-striped';
7875         }
7876         
7877         if (this.hover) {
7878             cfg.cls += ' table-hover';
7879         }
7880         if (this.bordered) {
7881             cfg.cls += ' table-bordered';
7882         }
7883         if (this.condensed) {
7884             cfg.cls += ' table-condensed';
7885         }
7886         if (this.responsive) {
7887             cfg.cls += ' table-responsive';
7888         }
7889         
7890         if (this.cls) {
7891             cfg.cls+=  ' ' +this.cls;
7892         }
7893         
7894         // this lot should be simplifed...
7895         var _t = this;
7896         var cp = [
7897             'align',
7898             'bgcolor',
7899             'border',
7900             'cellpadding',
7901             'cellspacing',
7902             'frame',
7903             'rules',
7904             'sortable',
7905             'summary',
7906             'width'
7907         ].forEach(function(k) {
7908             if (_t[k]) {
7909                 cfg[k] = _t[k];
7910             }
7911         });
7912         
7913         
7914         if (this.layout) {
7915             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7916         }
7917         
7918         if(this.store || this.cm){
7919             if(this.headerShow){
7920                 cfg.cn.push(this.renderHeader());
7921             }
7922             
7923             cfg.cn.push(this.renderBody());
7924             
7925             if(this.footerShow){
7926                 cfg.cn.push(this.renderFooter());
7927             }
7928             // where does this come from?
7929             //cfg.cls+=  ' TableGrid';
7930         }
7931         
7932         return { cn : [ cfg ] };
7933     },
7934     
7935     initEvents : function()
7936     {   
7937         if(!this.store || !this.cm){
7938             return;
7939         }
7940         if (this.selModel) {
7941             this.selModel.initEvents();
7942         }
7943         
7944         
7945         //Roo.log('initEvents with ds!!!!');
7946         
7947         this.mainBody = this.el.select('tbody', true).first();
7948         this.mainHead = this.el.select('thead', true).first();
7949         this.mainFoot = this.el.select('tfoot', true).first();
7950         
7951         
7952         
7953         var _this = this;
7954         
7955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7956             e.on('click', _this.sort, _this);
7957         });
7958         
7959         this.mainBody.on("click", this.onClick, this);
7960         this.mainBody.on("dblclick", this.onDblClick, this);
7961         
7962         // why is this done????? = it breaks dialogs??
7963         //this.parent().el.setStyle('position', 'relative');
7964         
7965         
7966         if (this.footer) {
7967             this.footer.parentId = this.id;
7968             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7969             
7970             if(this.lazyLoad){
7971                 this.el.select('tfoot tr td').first().addClass('hide');
7972             }
7973         } 
7974         
7975         if(this.loadMask) {
7976             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7977         }
7978         
7979         this.store.on('load', this.onLoad, this);
7980         this.store.on('beforeload', this.onBeforeLoad, this);
7981         this.store.on('update', this.onUpdate, this);
7982         this.store.on('add', this.onAdd, this);
7983         this.store.on("clear", this.clear, this);
7984         
7985         this.el.on("contextmenu", this.onContextMenu, this);
7986         
7987         this.mainBody.on('scroll', this.onBodyScroll, this);
7988         
7989         this.cm.on("headerchange", this.onHeaderChange, this);
7990         
7991         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7992         
7993     },
7994     
7995     onContextMenu : function(e, t)
7996     {
7997         this.processEvent("contextmenu", e);
7998     },
7999     
8000     processEvent : function(name, e)
8001     {
8002         if (name != 'touchstart' ) {
8003             this.fireEvent(name, e);    
8004         }
8005         
8006         var t = e.getTarget();
8007         
8008         var cell = Roo.get(t);
8009         
8010         if(!cell){
8011             return;
8012         }
8013         
8014         if(cell.findParent('tfoot', false, true)){
8015             return;
8016         }
8017         
8018         if(cell.findParent('thead', false, true)){
8019             
8020             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8021                 cell = Roo.get(t).findParent('th', false, true);
8022                 if (!cell) {
8023                     Roo.log("failed to find th in thead?");
8024                     Roo.log(e.getTarget());
8025                     return;
8026                 }
8027             }
8028             
8029             var cellIndex = cell.dom.cellIndex;
8030             
8031             var ename = name == 'touchstart' ? 'click' : name;
8032             this.fireEvent("header" + ename, this, cellIndex, e);
8033             
8034             return;
8035         }
8036         
8037         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8038             cell = Roo.get(t).findParent('td', false, true);
8039             if (!cell) {
8040                 Roo.log("failed to find th in tbody?");
8041                 Roo.log(e.getTarget());
8042                 return;
8043             }
8044         }
8045         
8046         var row = cell.findParent('tr', false, true);
8047         var cellIndex = cell.dom.cellIndex;
8048         var rowIndex = row.dom.rowIndex - 1;
8049         
8050         if(row !== false){
8051             
8052             this.fireEvent("row" + name, this, rowIndex, e);
8053             
8054             if(cell !== false){
8055             
8056                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8057             }
8058         }
8059         
8060     },
8061     
8062     onMouseover : function(e, el)
8063     {
8064         var cell = Roo.get(el);
8065         
8066         if(!cell){
8067             return;
8068         }
8069         
8070         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8071             cell = cell.findParent('td', false, true);
8072         }
8073         
8074         var row = cell.findParent('tr', false, true);
8075         var cellIndex = cell.dom.cellIndex;
8076         var rowIndex = row.dom.rowIndex - 1; // start from 0
8077         
8078         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8079         
8080     },
8081     
8082     onMouseout : function(e, el)
8083     {
8084         var cell = Roo.get(el);
8085         
8086         if(!cell){
8087             return;
8088         }
8089         
8090         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8091             cell = cell.findParent('td', false, true);
8092         }
8093         
8094         var row = cell.findParent('tr', false, true);
8095         var cellIndex = cell.dom.cellIndex;
8096         var rowIndex = row.dom.rowIndex - 1; // start from 0
8097         
8098         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8099         
8100     },
8101     
8102     onClick : function(e, el)
8103     {
8104         var cell = Roo.get(el);
8105         
8106         if(!cell || (!this.cellSelection && !this.rowSelection)){
8107             return;
8108         }
8109         
8110         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8111             cell = cell.findParent('td', false, true);
8112         }
8113         
8114         if(!cell || typeof(cell) == 'undefined'){
8115             return;
8116         }
8117         
8118         var row = cell.findParent('tr', false, true);
8119         
8120         if(!row || typeof(row) == 'undefined'){
8121             return;
8122         }
8123         
8124         var cellIndex = cell.dom.cellIndex;
8125         var rowIndex = this.getRowIndex(row);
8126         
8127         // why??? - should these not be based on SelectionModel?
8128         if(this.cellSelection){
8129             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8130         }
8131         
8132         if(this.rowSelection){
8133             this.fireEvent('rowclick', this, row, rowIndex, e);
8134         }
8135         
8136         
8137     },
8138         
8139     onDblClick : function(e,el)
8140     {
8141         var cell = Roo.get(el);
8142         
8143         if(!cell || (!this.cellSelection && !this.rowSelection)){
8144             return;
8145         }
8146         
8147         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8148             cell = cell.findParent('td', false, true);
8149         }
8150         
8151         if(!cell || typeof(cell) == 'undefined'){
8152             return;
8153         }
8154         
8155         var row = cell.findParent('tr', false, true);
8156         
8157         if(!row || typeof(row) == 'undefined'){
8158             return;
8159         }
8160         
8161         var cellIndex = cell.dom.cellIndex;
8162         var rowIndex = this.getRowIndex(row);
8163         
8164         if(this.cellSelection){
8165             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8166         }
8167         
8168         if(this.rowSelection){
8169             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8170         }
8171     },
8172     
8173     sort : function(e,el)
8174     {
8175         var col = Roo.get(el);
8176         
8177         if(!col.hasClass('sortable')){
8178             return;
8179         }
8180         
8181         var sort = col.attr('sort');
8182         var dir = 'ASC';
8183         
8184         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8185             dir = 'DESC';
8186         }
8187         
8188         this.store.sortInfo = {field : sort, direction : dir};
8189         
8190         if (this.footer) {
8191             Roo.log("calling footer first");
8192             this.footer.onClick('first');
8193         } else {
8194         
8195             this.store.load({ params : { start : 0 } });
8196         }
8197     },
8198     
8199     renderHeader : function()
8200     {
8201         var header = {
8202             tag: 'thead',
8203             cn : []
8204         };
8205         
8206         var cm = this.cm;
8207         this.totalWidth = 0;
8208         
8209         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8210             
8211             var config = cm.config[i];
8212             
8213             var c = {
8214                 tag: 'th',
8215                 cls : 'x-hcol-' + i,
8216                 style : '',
8217                 html: cm.getColumnHeader(i)
8218             };
8219             
8220             var hh = '';
8221             
8222             if(typeof(config.sortable) != 'undefined' && config.sortable){
8223                 c.cls = 'sortable';
8224                 c.html = '<i class="glyphicon"></i>' + c.html;
8225             }
8226             
8227             // could use BS4 hidden-..-down 
8228             
8229             if(typeof(config.lgHeader) != 'undefined'){
8230                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8231             }
8232             
8233             if(typeof(config.mdHeader) != 'undefined'){
8234                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8235             }
8236             
8237             if(typeof(config.smHeader) != 'undefined'){
8238                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8239             }
8240             
8241             if(typeof(config.xsHeader) != 'undefined'){
8242                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8243             }
8244             
8245             if(hh.length){
8246                 c.html = hh;
8247             }
8248             
8249             if(typeof(config.tooltip) != 'undefined'){
8250                 c.tooltip = config.tooltip;
8251             }
8252             
8253             if(typeof(config.colspan) != 'undefined'){
8254                 c.colspan = config.colspan;
8255             }
8256             
8257             if(typeof(config.hidden) != 'undefined' && config.hidden){
8258                 c.style += ' display:none;';
8259             }
8260             
8261             if(typeof(config.dataIndex) != 'undefined'){
8262                 c.sort = config.dataIndex;
8263             }
8264             
8265            
8266             
8267             if(typeof(config.align) != 'undefined' && config.align.length){
8268                 c.style += ' text-align:' + config.align + ';';
8269             }
8270             
8271             if(typeof(config.width) != 'undefined'){
8272                 c.style += ' width:' + config.width + 'px;';
8273                 this.totalWidth += config.width;
8274             } else {
8275                 this.totalWidth += 100; // assume minimum of 100 per column?
8276             }
8277             
8278             if(typeof(config.cls) != 'undefined'){
8279                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8280             }
8281             
8282             ['xs','sm','md','lg'].map(function(size){
8283                 
8284                 if(typeof(config[size]) == 'undefined'){
8285                     return;
8286                 }
8287                  
8288                 if (!config[size]) { // 0 = hidden
8289                     // BS 4 '0' is treated as hide that column and below.
8290                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8291                     return;
8292                 }
8293                 
8294                 c.cls += ' col-' + size + '-' + config[size] + (
8295                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8296                 );
8297                 
8298                 
8299             });
8300             
8301             header.cn.push(c)
8302         }
8303         
8304         return header;
8305     },
8306     
8307     renderBody : function()
8308     {
8309         var body = {
8310             tag: 'tbody',
8311             cn : [
8312                 {
8313                     tag: 'tr',
8314                     cn : [
8315                         {
8316                             tag : 'td',
8317                             colspan :  this.cm.getColumnCount()
8318                         }
8319                     ]
8320                 }
8321             ]
8322         };
8323         
8324         return body;
8325     },
8326     
8327     renderFooter : function()
8328     {
8329         var footer = {
8330             tag: 'tfoot',
8331             cn : [
8332                 {
8333                     tag: 'tr',
8334                     cn : [
8335                         {
8336                             tag : 'td',
8337                             colspan :  this.cm.getColumnCount()
8338                         }
8339                     ]
8340                 }
8341             ]
8342         };
8343         
8344         return footer;
8345     },
8346     
8347     
8348     
8349     onLoad : function()
8350     {
8351 //        Roo.log('ds onload');
8352         this.clear();
8353         
8354         var _this = this;
8355         var cm = this.cm;
8356         var ds = this.store;
8357         
8358         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8359             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8360             if (_this.store.sortInfo) {
8361                     
8362                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8363                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8364                 }
8365                 
8366                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8367                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8368                 }
8369             }
8370         });
8371         
8372         var tbody =  this.mainBody;
8373               
8374         if(ds.getCount() > 0){
8375             ds.data.each(function(d,rowIndex){
8376                 var row =  this.renderRow(cm, ds, rowIndex);
8377                 
8378                 tbody.createChild(row);
8379                 
8380                 var _this = this;
8381                 
8382                 if(row.cellObjects.length){
8383                     Roo.each(row.cellObjects, function(r){
8384                         _this.renderCellObject(r);
8385                     })
8386                 }
8387                 
8388             }, this);
8389         }
8390         
8391         var tfoot = this.el.select('tfoot', true).first();
8392         
8393         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8394             
8395             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8396             
8397             var total = this.ds.getTotalCount();
8398             
8399             if(this.footer.pageSize < total){
8400                 this.mainFoot.show();
8401             }
8402         }
8403         
8404         Roo.each(this.el.select('tbody td', true).elements, function(e){
8405             e.on('mouseover', _this.onMouseover, _this);
8406         });
8407         
8408         Roo.each(this.el.select('tbody td', true).elements, function(e){
8409             e.on('mouseout', _this.onMouseout, _this);
8410         });
8411         this.fireEvent('rowsrendered', this);
8412         
8413         this.autoSize();
8414     },
8415     
8416     
8417     onUpdate : function(ds,record)
8418     {
8419         this.refreshRow(record);
8420         this.autoSize();
8421     },
8422     
8423     onRemove : function(ds, record, index, isUpdate){
8424         if(isUpdate !== true){
8425             this.fireEvent("beforerowremoved", this, index, record);
8426         }
8427         var bt = this.mainBody.dom;
8428         
8429         var rows = this.el.select('tbody > tr', true).elements;
8430         
8431         if(typeof(rows[index]) != 'undefined'){
8432             bt.removeChild(rows[index].dom);
8433         }
8434         
8435 //        if(bt.rows[index]){
8436 //            bt.removeChild(bt.rows[index]);
8437 //        }
8438         
8439         if(isUpdate !== true){
8440             //this.stripeRows(index);
8441             //this.syncRowHeights(index, index);
8442             //this.layout();
8443             this.fireEvent("rowremoved", this, index, record);
8444         }
8445     },
8446     
8447     onAdd : function(ds, records, rowIndex)
8448     {
8449         //Roo.log('on Add called');
8450         // - note this does not handle multiple adding very well..
8451         var bt = this.mainBody.dom;
8452         for (var i =0 ; i < records.length;i++) {
8453             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8454             //Roo.log(records[i]);
8455             //Roo.log(this.store.getAt(rowIndex+i));
8456             this.insertRow(this.store, rowIndex + i, false);
8457             return;
8458         }
8459         
8460     },
8461     
8462     
8463     refreshRow : function(record){
8464         var ds = this.store, index;
8465         if(typeof record == 'number'){
8466             index = record;
8467             record = ds.getAt(index);
8468         }else{
8469             index = ds.indexOf(record);
8470             if (index < 0) {
8471                 return; // should not happen - but seems to 
8472             }
8473         }
8474         this.insertRow(ds, index, true);
8475         this.autoSize();
8476         this.onRemove(ds, record, index+1, true);
8477         this.autoSize();
8478         //this.syncRowHeights(index, index);
8479         //this.layout();
8480         this.fireEvent("rowupdated", this, index, record);
8481     },
8482     
8483     insertRow : function(dm, rowIndex, isUpdate){
8484         
8485         if(!isUpdate){
8486             this.fireEvent("beforerowsinserted", this, rowIndex);
8487         }
8488             //var s = this.getScrollState();
8489         var row = this.renderRow(this.cm, this.store, rowIndex);
8490         // insert before rowIndex..
8491         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8492         
8493         var _this = this;
8494                 
8495         if(row.cellObjects.length){
8496             Roo.each(row.cellObjects, function(r){
8497                 _this.renderCellObject(r);
8498             })
8499         }
8500             
8501         if(!isUpdate){
8502             this.fireEvent("rowsinserted", this, rowIndex);
8503             //this.syncRowHeights(firstRow, lastRow);
8504             //this.stripeRows(firstRow);
8505             //this.layout();
8506         }
8507         
8508     },
8509     
8510     
8511     getRowDom : function(rowIndex)
8512     {
8513         var rows = this.el.select('tbody > tr', true).elements;
8514         
8515         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8516         
8517     },
8518     // returns the object tree for a tr..
8519   
8520     
8521     renderRow : function(cm, ds, rowIndex) 
8522     {
8523         var d = ds.getAt(rowIndex);
8524         
8525         var row = {
8526             tag : 'tr',
8527             cls : 'x-row-' + rowIndex,
8528             cn : []
8529         };
8530             
8531         var cellObjects = [];
8532         
8533         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8534             var config = cm.config[i];
8535             
8536             var renderer = cm.getRenderer(i);
8537             var value = '';
8538             var id = false;
8539             
8540             if(typeof(renderer) !== 'undefined'){
8541                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8542             }
8543             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8544             // and are rendered into the cells after the row is rendered - using the id for the element.
8545             
8546             if(typeof(value) === 'object'){
8547                 id = Roo.id();
8548                 cellObjects.push({
8549                     container : id,
8550                     cfg : value 
8551                 })
8552             }
8553             
8554             var rowcfg = {
8555                 record: d,
8556                 rowIndex : rowIndex,
8557                 colIndex : i,
8558                 rowClass : ''
8559             };
8560
8561             this.fireEvent('rowclass', this, rowcfg);
8562             
8563             var td = {
8564                 tag: 'td',
8565                 cls : rowcfg.rowClass + ' x-col-' + i,
8566                 style: '',
8567                 html: (typeof(value) === 'object') ? '' : value
8568             };
8569             
8570             if (id) {
8571                 td.id = id;
8572             }
8573             
8574             if(typeof(config.colspan) != 'undefined'){
8575                 td.colspan = config.colspan;
8576             }
8577             
8578             if(typeof(config.hidden) != 'undefined' && config.hidden){
8579                 td.style += ' display:none;';
8580             }
8581             
8582             if(typeof(config.align) != 'undefined' && config.align.length){
8583                 td.style += ' text-align:' + config.align + ';';
8584             }
8585             if(typeof(config.valign) != 'undefined' && config.valign.length){
8586                 td.style += ' vertical-align:' + config.valign + ';';
8587             }
8588             
8589             if(typeof(config.width) != 'undefined'){
8590                 td.style += ' width:' +  config.width + 'px;';
8591             }
8592             
8593             if(typeof(config.cursor) != 'undefined'){
8594                 td.style += ' cursor:' +  config.cursor + ';';
8595             }
8596             
8597             if(typeof(config.cls) != 'undefined'){
8598                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8599             }
8600             
8601             ['xs','sm','md','lg'].map(function(size){
8602                 
8603                 if(typeof(config[size]) == 'undefined'){
8604                     return;
8605                 }
8606                 
8607                 
8608                   
8609                 if (!config[size]) { // 0 = hidden
8610                     // BS 4 '0' is treated as hide that column and below.
8611                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8612                     return;
8613                 }
8614                 
8615                 td.cls += ' col-' + size + '-' + config[size] + (
8616                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8617                 );
8618                  
8619
8620             });
8621             
8622             row.cn.push(td);
8623            
8624         }
8625         
8626         row.cellObjects = cellObjects;
8627         
8628         return row;
8629           
8630     },
8631     
8632     
8633     
8634     onBeforeLoad : function()
8635     {
8636         
8637     },
8638      /**
8639      * Remove all rows
8640      */
8641     clear : function()
8642     {
8643         this.el.select('tbody', true).first().dom.innerHTML = '';
8644     },
8645     /**
8646      * Show or hide a row.
8647      * @param {Number} rowIndex to show or hide
8648      * @param {Boolean} state hide
8649      */
8650     setRowVisibility : function(rowIndex, state)
8651     {
8652         var bt = this.mainBody.dom;
8653         
8654         var rows = this.el.select('tbody > tr', true).elements;
8655         
8656         if(typeof(rows[rowIndex]) == 'undefined'){
8657             return;
8658         }
8659         rows[rowIndex].dom.style.display = state ? '' : 'none';
8660     },
8661     
8662     
8663     getSelectionModel : function(){
8664         if(!this.selModel){
8665             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8666         }
8667         return this.selModel;
8668     },
8669     /*
8670      * Render the Roo.bootstrap object from renderder
8671      */
8672     renderCellObject : function(r)
8673     {
8674         var _this = this;
8675         
8676         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8677         
8678         var t = r.cfg.render(r.container);
8679         
8680         if(r.cfg.cn){
8681             Roo.each(r.cfg.cn, function(c){
8682                 var child = {
8683                     container: t.getChildContainer(),
8684                     cfg: c
8685                 };
8686                 _this.renderCellObject(child);
8687             })
8688         }
8689     },
8690     
8691     getRowIndex : function(row)
8692     {
8693         var rowIndex = -1;
8694         
8695         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8696             if(el != row){
8697                 return;
8698             }
8699             
8700             rowIndex = index;
8701         });
8702         
8703         return rowIndex;
8704     },
8705      /**
8706      * Returns the grid's underlying element = used by panel.Grid
8707      * @return {Element} The element
8708      */
8709     getGridEl : function(){
8710         return this.el;
8711     },
8712      /**
8713      * Forces a resize - used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     autoSize : function()
8717     {
8718         //var ctr = Roo.get(this.container.dom.parentElement);
8719         var ctr = Roo.get(this.el.dom);
8720         
8721         var thd = this.getGridEl().select('thead',true).first();
8722         var tbd = this.getGridEl().select('tbody', true).first();
8723         var tfd = this.getGridEl().select('tfoot', true).first();
8724         
8725         var cw = ctr.getWidth();
8726         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8727         
8728         if (tbd) {
8729             
8730             tbd.setWidth(ctr.getWidth());
8731             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8732             // this needs fixing for various usage - currently only hydra job advers I think..
8733             //tdb.setHeight(
8734             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8735             //); 
8736             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8737             cw -= barsize;
8738         }
8739         cw = Math.max(cw, this.totalWidth);
8740         this.getGridEl().select('tbody tr',true).setWidth(cw);
8741         
8742         // resize 'expandable coloumn?
8743         
8744         return; // we doe not have a view in this design..
8745         
8746     },
8747     onBodyScroll: function()
8748     {
8749         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8750         if(this.mainHead){
8751             this.mainHead.setStyle({
8752                 'position' : 'relative',
8753                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8754             });
8755         }
8756         
8757         if(this.lazyLoad){
8758             
8759             var scrollHeight = this.mainBody.dom.scrollHeight;
8760             
8761             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8762             
8763             var height = this.mainBody.getHeight();
8764             
8765             if(scrollHeight - height == scrollTop) {
8766                 
8767                 var total = this.ds.getTotalCount();
8768                 
8769                 if(this.footer.cursor + this.footer.pageSize < total){
8770                     
8771                     this.footer.ds.load({
8772                         params : {
8773                             start : this.footer.cursor + this.footer.pageSize,
8774                             limit : this.footer.pageSize
8775                         },
8776                         add : true
8777                     });
8778                 }
8779             }
8780             
8781         }
8782     },
8783     
8784     onHeaderChange : function()
8785     {
8786         var header = this.renderHeader();
8787         var table = this.el.select('table', true).first();
8788         
8789         this.mainHead.remove();
8790         this.mainHead = table.createChild(header, this.mainBody, false);
8791     },
8792     
8793     onHiddenChange : function(colModel, colIndex, hidden)
8794     {
8795         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8796         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8797         
8798         this.CSS.updateRule(thSelector, "display", "");
8799         this.CSS.updateRule(tdSelector, "display", "");
8800         
8801         if(hidden){
8802             this.CSS.updateRule(thSelector, "display", "none");
8803             this.CSS.updateRule(tdSelector, "display", "none");
8804         }
8805         
8806         this.onHeaderChange();
8807         this.onLoad();
8808     },
8809     
8810     setColumnWidth: function(col_index, width)
8811     {
8812         // width = "md-2 xs-2..."
8813         if(!this.colModel.config[col_index]) {
8814             return;
8815         }
8816         
8817         var w = width.split(" ");
8818         
8819         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8820         
8821         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8822         
8823         
8824         for(var j = 0; j < w.length; j++) {
8825             
8826             if(!w[j]) {
8827                 continue;
8828             }
8829             
8830             var size_cls = w[j].split("-");
8831             
8832             if(!Number.isInteger(size_cls[1] * 1)) {
8833                 continue;
8834             }
8835             
8836             if(!this.colModel.config[col_index][size_cls[0]]) {
8837                 continue;
8838             }
8839             
8840             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8841                 continue;
8842             }
8843             
8844             h_row[0].classList.replace(
8845                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8846                 "col-"+size_cls[0]+"-"+size_cls[1]
8847             );
8848             
8849             for(var i = 0; i < rows.length; i++) {
8850                 
8851                 var size_cls = w[j].split("-");
8852                 
8853                 if(!Number.isInteger(size_cls[1] * 1)) {
8854                     continue;
8855                 }
8856                 
8857                 if(!this.colModel.config[col_index][size_cls[0]]) {
8858                     continue;
8859                 }
8860                 
8861                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8862                     continue;
8863                 }
8864                 
8865                 rows[i].classList.replace(
8866                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8867                     "col-"+size_cls[0]+"-"+size_cls[1]
8868                 );
8869             }
8870             
8871             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8872         }
8873     }
8874 });
8875
8876  
8877
8878  /*
8879  * - LGPL
8880  *
8881  * table cell
8882  * 
8883  */
8884
8885 /**
8886  * @class Roo.bootstrap.TableCell
8887  * @extends Roo.bootstrap.Component
8888  * Bootstrap TableCell class
8889  * @cfg {String} html cell contain text
8890  * @cfg {String} cls cell class
8891  * @cfg {String} tag cell tag (td|th) default td
8892  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8893  * @cfg {String} align Aligns the content in a cell
8894  * @cfg {String} axis Categorizes cells
8895  * @cfg {String} bgcolor Specifies the background color of a cell
8896  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8897  * @cfg {Number} colspan Specifies the number of columns a cell should span
8898  * @cfg {String} headers Specifies one or more header cells a cell is related to
8899  * @cfg {Number} height Sets the height of a cell
8900  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8901  * @cfg {Number} rowspan Sets the number of rows a cell should span
8902  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8903  * @cfg {String} valign Vertical aligns the content in a cell
8904  * @cfg {Number} width Specifies the width of a cell
8905  * 
8906  * @constructor
8907  * Create a new TableCell
8908  * @param {Object} config The config object
8909  */
8910
8911 Roo.bootstrap.TableCell = function(config){
8912     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8913 };
8914
8915 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8916     
8917     html: false,
8918     cls: false,
8919     tag: false,
8920     abbr: false,
8921     align: false,
8922     axis: false,
8923     bgcolor: false,
8924     charoff: false,
8925     colspan: false,
8926     headers: false,
8927     height: false,
8928     nowrap: false,
8929     rowspan: false,
8930     scope: false,
8931     valign: false,
8932     width: false,
8933     
8934     
8935     getAutoCreate : function(){
8936         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8937         
8938         cfg = {
8939             tag: 'td'
8940         };
8941         
8942         if(this.tag){
8943             cfg.tag = this.tag;
8944         }
8945         
8946         if (this.html) {
8947             cfg.html=this.html
8948         }
8949         if (this.cls) {
8950             cfg.cls=this.cls
8951         }
8952         if (this.abbr) {
8953             cfg.abbr=this.abbr
8954         }
8955         if (this.align) {
8956             cfg.align=this.align
8957         }
8958         if (this.axis) {
8959             cfg.axis=this.axis
8960         }
8961         if (this.bgcolor) {
8962             cfg.bgcolor=this.bgcolor
8963         }
8964         if (this.charoff) {
8965             cfg.charoff=this.charoff
8966         }
8967         if (this.colspan) {
8968             cfg.colspan=this.colspan
8969         }
8970         if (this.headers) {
8971             cfg.headers=this.headers
8972         }
8973         if (this.height) {
8974             cfg.height=this.height
8975         }
8976         if (this.nowrap) {
8977             cfg.nowrap=this.nowrap
8978         }
8979         if (this.rowspan) {
8980             cfg.rowspan=this.rowspan
8981         }
8982         if (this.scope) {
8983             cfg.scope=this.scope
8984         }
8985         if (this.valign) {
8986             cfg.valign=this.valign
8987         }
8988         if (this.width) {
8989             cfg.width=this.width
8990         }
8991         
8992         
8993         return cfg;
8994     }
8995    
8996 });
8997
8998  
8999
9000  /*
9001  * - LGPL
9002  *
9003  * table row
9004  * 
9005  */
9006
9007 /**
9008  * @class Roo.bootstrap.TableRow
9009  * @extends Roo.bootstrap.Component
9010  * Bootstrap TableRow class
9011  * @cfg {String} cls row class
9012  * @cfg {String} align Aligns the content in a table row
9013  * @cfg {String} bgcolor Specifies a background color for a table row
9014  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9015  * @cfg {String} valign Vertical aligns the content in a table row
9016  * 
9017  * @constructor
9018  * Create a new TableRow
9019  * @param {Object} config The config object
9020  */
9021
9022 Roo.bootstrap.TableRow = function(config){
9023     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9024 };
9025
9026 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9027     
9028     cls: false,
9029     align: false,
9030     bgcolor: false,
9031     charoff: false,
9032     valign: false,
9033     
9034     getAutoCreate : function(){
9035         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9036         
9037         cfg = {
9038             tag: 'tr'
9039         };
9040             
9041         if(this.cls){
9042             cfg.cls = this.cls;
9043         }
9044         if(this.align){
9045             cfg.align = this.align;
9046         }
9047         if(this.bgcolor){
9048             cfg.bgcolor = this.bgcolor;
9049         }
9050         if(this.charoff){
9051             cfg.charoff = this.charoff;
9052         }
9053         if(this.valign){
9054             cfg.valign = this.valign;
9055         }
9056         
9057         return cfg;
9058     }
9059    
9060 });
9061
9062  
9063
9064  /*
9065  * - LGPL
9066  *
9067  * table body
9068  * 
9069  */
9070
9071 /**
9072  * @class Roo.bootstrap.TableBody
9073  * @extends Roo.bootstrap.Component
9074  * Bootstrap TableBody class
9075  * @cfg {String} cls element class
9076  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9077  * @cfg {String} align Aligns the content inside the element
9078  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9079  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9080  * 
9081  * @constructor
9082  * Create a new TableBody
9083  * @param {Object} config The config object
9084  */
9085
9086 Roo.bootstrap.TableBody = function(config){
9087     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9088 };
9089
9090 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9091     
9092     cls: false,
9093     tag: false,
9094     align: false,
9095     charoff: false,
9096     valign: false,
9097     
9098     getAutoCreate : function(){
9099         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9100         
9101         cfg = {
9102             tag: 'tbody'
9103         };
9104             
9105         if (this.cls) {
9106             cfg.cls=this.cls
9107         }
9108         if(this.tag){
9109             cfg.tag = this.tag;
9110         }
9111         
9112         if(this.align){
9113             cfg.align = this.align;
9114         }
9115         if(this.charoff){
9116             cfg.charoff = this.charoff;
9117         }
9118         if(this.valign){
9119             cfg.valign = this.valign;
9120         }
9121         
9122         return cfg;
9123     }
9124     
9125     
9126 //    initEvents : function()
9127 //    {
9128 //        
9129 //        if(!this.store){
9130 //            return;
9131 //        }
9132 //        
9133 //        this.store = Roo.factory(this.store, Roo.data);
9134 //        this.store.on('load', this.onLoad, this);
9135 //        
9136 //        this.store.load();
9137 //        
9138 //    },
9139 //    
9140 //    onLoad: function () 
9141 //    {   
9142 //        this.fireEvent('load', this);
9143 //    }
9144 //    
9145 //   
9146 });
9147
9148  
9149
9150  /*
9151  * Based on:
9152  * Ext JS Library 1.1.1
9153  * Copyright(c) 2006-2007, Ext JS, LLC.
9154  *
9155  * Originally Released Under LGPL - original licence link has changed is not relivant.
9156  *
9157  * Fork - LGPL
9158  * <script type="text/javascript">
9159  */
9160
9161 // as we use this in bootstrap.
9162 Roo.namespace('Roo.form');
9163  /**
9164  * @class Roo.form.Action
9165  * Internal Class used to handle form actions
9166  * @constructor
9167  * @param {Roo.form.BasicForm} el The form element or its id
9168  * @param {Object} config Configuration options
9169  */
9170
9171  
9172  
9173 // define the action interface
9174 Roo.form.Action = function(form, options){
9175     this.form = form;
9176     this.options = options || {};
9177 };
9178 /**
9179  * Client Validation Failed
9180  * @const 
9181  */
9182 Roo.form.Action.CLIENT_INVALID = 'client';
9183 /**
9184  * Server Validation Failed
9185  * @const 
9186  */
9187 Roo.form.Action.SERVER_INVALID = 'server';
9188  /**
9189  * Connect to Server Failed
9190  * @const 
9191  */
9192 Roo.form.Action.CONNECT_FAILURE = 'connect';
9193 /**
9194  * Reading Data from Server Failed
9195  * @const 
9196  */
9197 Roo.form.Action.LOAD_FAILURE = 'load';
9198
9199 Roo.form.Action.prototype = {
9200     type : 'default',
9201     failureType : undefined,
9202     response : undefined,
9203     result : undefined,
9204
9205     // interface method
9206     run : function(options){
9207
9208     },
9209
9210     // interface method
9211     success : function(response){
9212
9213     },
9214
9215     // interface method
9216     handleResponse : function(response){
9217
9218     },
9219
9220     // default connection failure
9221     failure : function(response){
9222         
9223         this.response = response;
9224         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9225         this.form.afterAction(this, false);
9226     },
9227
9228     processResponse : function(response){
9229         this.response = response;
9230         if(!response.responseText){
9231             return true;
9232         }
9233         this.result = this.handleResponse(response);
9234         return this.result;
9235     },
9236
9237     // utility functions used internally
9238     getUrl : function(appendParams){
9239         var url = this.options.url || this.form.url || this.form.el.dom.action;
9240         if(appendParams){
9241             var p = this.getParams();
9242             if(p){
9243                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9244             }
9245         }
9246         return url;
9247     },
9248
9249     getMethod : function(){
9250         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9251     },
9252
9253     getParams : function(){
9254         var bp = this.form.baseParams;
9255         var p = this.options.params;
9256         if(p){
9257             if(typeof p == "object"){
9258                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9259             }else if(typeof p == 'string' && bp){
9260                 p += '&' + Roo.urlEncode(bp);
9261             }
9262         }else if(bp){
9263             p = Roo.urlEncode(bp);
9264         }
9265         return p;
9266     },
9267
9268     createCallback : function(){
9269         return {
9270             success: this.success,
9271             failure: this.failure,
9272             scope: this,
9273             timeout: (this.form.timeout*1000),
9274             upload: this.form.fileUpload ? this.success : undefined
9275         };
9276     }
9277 };
9278
9279 Roo.form.Action.Submit = function(form, options){
9280     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9281 };
9282
9283 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9284     type : 'submit',
9285
9286     haveProgress : false,
9287     uploadComplete : false,
9288     
9289     // uploadProgress indicator.
9290     uploadProgress : function()
9291     {
9292         if (!this.form.progressUrl) {
9293             return;
9294         }
9295         
9296         if (!this.haveProgress) {
9297             Roo.MessageBox.progress("Uploading", "Uploading");
9298         }
9299         if (this.uploadComplete) {
9300            Roo.MessageBox.hide();
9301            return;
9302         }
9303         
9304         this.haveProgress = true;
9305    
9306         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9307         
9308         var c = new Roo.data.Connection();
9309         c.request({
9310             url : this.form.progressUrl,
9311             params: {
9312                 id : uid
9313             },
9314             method: 'GET',
9315             success : function(req){
9316                //console.log(data);
9317                 var rdata = false;
9318                 var edata;
9319                 try  {
9320                    rdata = Roo.decode(req.responseText)
9321                 } catch (e) {
9322                     Roo.log("Invalid data from server..");
9323                     Roo.log(edata);
9324                     return;
9325                 }
9326                 if (!rdata || !rdata.success) {
9327                     Roo.log(rdata);
9328                     Roo.MessageBox.alert(Roo.encode(rdata));
9329                     return;
9330                 }
9331                 var data = rdata.data;
9332                 
9333                 if (this.uploadComplete) {
9334                    Roo.MessageBox.hide();
9335                    return;
9336                 }
9337                    
9338                 if (data){
9339                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9340                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9341                     );
9342                 }
9343                 this.uploadProgress.defer(2000,this);
9344             },
9345        
9346             failure: function(data) {
9347                 Roo.log('progress url failed ');
9348                 Roo.log(data);
9349             },
9350             scope : this
9351         });
9352            
9353     },
9354     
9355     
9356     run : function()
9357     {
9358         // run get Values on the form, so it syncs any secondary forms.
9359         this.form.getValues();
9360         
9361         var o = this.options;
9362         var method = this.getMethod();
9363         var isPost = method == 'POST';
9364         if(o.clientValidation === false || this.form.isValid()){
9365             
9366             if (this.form.progressUrl) {
9367                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9368                     (new Date() * 1) + '' + Math.random());
9369                     
9370             } 
9371             
9372             
9373             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9374                 form:this.form.el.dom,
9375                 url:this.getUrl(!isPost),
9376                 method: method,
9377                 params:isPost ? this.getParams() : null,
9378                 isUpload: this.form.fileUpload,
9379                 formData : this.form.formData
9380             }));
9381             
9382             this.uploadProgress();
9383
9384         }else if (o.clientValidation !== false){ // client validation failed
9385             this.failureType = Roo.form.Action.CLIENT_INVALID;
9386             this.form.afterAction(this, false);
9387         }
9388     },
9389
9390     success : function(response)
9391     {
9392         this.uploadComplete= true;
9393         if (this.haveProgress) {
9394             Roo.MessageBox.hide();
9395         }
9396         
9397         
9398         var result = this.processResponse(response);
9399         if(result === true || result.success){
9400             this.form.afterAction(this, true);
9401             return;
9402         }
9403         if(result.errors){
9404             this.form.markInvalid(result.errors);
9405             this.failureType = Roo.form.Action.SERVER_INVALID;
9406         }
9407         this.form.afterAction(this, false);
9408     },
9409     failure : function(response)
9410     {
9411         this.uploadComplete= true;
9412         if (this.haveProgress) {
9413             Roo.MessageBox.hide();
9414         }
9415         
9416         this.response = response;
9417         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9418         this.form.afterAction(this, false);
9419     },
9420     
9421     handleResponse : function(response){
9422         if(this.form.errorReader){
9423             var rs = this.form.errorReader.read(response);
9424             var errors = [];
9425             if(rs.records){
9426                 for(var i = 0, len = rs.records.length; i < len; i++) {
9427                     var r = rs.records[i];
9428                     errors[i] = r.data;
9429                 }
9430             }
9431             if(errors.length < 1){
9432                 errors = null;
9433             }
9434             return {
9435                 success : rs.success,
9436                 errors : errors
9437             };
9438         }
9439         var ret = false;
9440         try {
9441             ret = Roo.decode(response.responseText);
9442         } catch (e) {
9443             ret = {
9444                 success: false,
9445                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9446                 errors : []
9447             };
9448         }
9449         return ret;
9450         
9451     }
9452 });
9453
9454
9455 Roo.form.Action.Load = function(form, options){
9456     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9457     this.reader = this.form.reader;
9458 };
9459
9460 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9461     type : 'load',
9462
9463     run : function(){
9464         
9465         Roo.Ajax.request(Roo.apply(
9466                 this.createCallback(), {
9467                     method:this.getMethod(),
9468                     url:this.getUrl(false),
9469                     params:this.getParams()
9470         }));
9471     },
9472
9473     success : function(response){
9474         
9475         var result = this.processResponse(response);
9476         if(result === true || !result.success || !result.data){
9477             this.failureType = Roo.form.Action.LOAD_FAILURE;
9478             this.form.afterAction(this, false);
9479             return;
9480         }
9481         this.form.clearInvalid();
9482         this.form.setValues(result.data);
9483         this.form.afterAction(this, true);
9484     },
9485
9486     handleResponse : function(response){
9487         if(this.form.reader){
9488             var rs = this.form.reader.read(response);
9489             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9490             return {
9491                 success : rs.success,
9492                 data : data
9493             };
9494         }
9495         return Roo.decode(response.responseText);
9496     }
9497 });
9498
9499 Roo.form.Action.ACTION_TYPES = {
9500     'load' : Roo.form.Action.Load,
9501     'submit' : Roo.form.Action.Submit
9502 };/*
9503  * - LGPL
9504  *
9505  * form
9506  *
9507  */
9508
9509 /**
9510  * @class Roo.bootstrap.Form
9511  * @extends Roo.bootstrap.Component
9512  * Bootstrap Form class
9513  * @cfg {String} method  GET | POST (default POST)
9514  * @cfg {String} labelAlign top | left (default top)
9515  * @cfg {String} align left  | right - for navbars
9516  * @cfg {Boolean} loadMask load mask when submit (default true)
9517
9518  *
9519  * @constructor
9520  * Create a new Form
9521  * @param {Object} config The config object
9522  */
9523
9524
9525 Roo.bootstrap.Form = function(config){
9526     
9527     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9528     
9529     Roo.bootstrap.Form.popover.apply();
9530     
9531     this.addEvents({
9532         /**
9533          * @event clientvalidation
9534          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9535          * @param {Form} this
9536          * @param {Boolean} valid true if the form has passed client-side validation
9537          */
9538         clientvalidation: true,
9539         /**
9540          * @event beforeaction
9541          * Fires before any action is performed. Return false to cancel the action.
9542          * @param {Form} this
9543          * @param {Action} action The action to be performed
9544          */
9545         beforeaction: true,
9546         /**
9547          * @event actionfailed
9548          * Fires when an action fails.
9549          * @param {Form} this
9550          * @param {Action} action The action that failed
9551          */
9552         actionfailed : true,
9553         /**
9554          * @event actioncomplete
9555          * Fires when an action is completed.
9556          * @param {Form} this
9557          * @param {Action} action The action that completed
9558          */
9559         actioncomplete : true
9560     });
9561 };
9562
9563 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9564
9565      /**
9566      * @cfg {String} method
9567      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9568      */
9569     method : 'POST',
9570     /**
9571      * @cfg {String} url
9572      * The URL to use for form actions if one isn't supplied in the action options.
9573      */
9574     /**
9575      * @cfg {Boolean} fileUpload
9576      * Set to true if this form is a file upload.
9577      */
9578
9579     /**
9580      * @cfg {Object} baseParams
9581      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9582      */
9583
9584     /**
9585      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9586      */
9587     timeout: 30,
9588     /**
9589      * @cfg {Sting} align (left|right) for navbar forms
9590      */
9591     align : 'left',
9592
9593     // private
9594     activeAction : null,
9595
9596     /**
9597      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9598      * element by passing it or its id or mask the form itself by passing in true.
9599      * @type Mixed
9600      */
9601     waitMsgTarget : false,
9602
9603     loadMask : true,
9604     
9605     /**
9606      * @cfg {Boolean} errorMask (true|false) default false
9607      */
9608     errorMask : false,
9609     
9610     /**
9611      * @cfg {Number} maskOffset Default 100
9612      */
9613     maskOffset : 100,
9614     
9615     /**
9616      * @cfg {Boolean} maskBody
9617      */
9618     maskBody : false,
9619
9620     getAutoCreate : function(){
9621
9622         var cfg = {
9623             tag: 'form',
9624             method : this.method || 'POST',
9625             id : this.id || Roo.id(),
9626             cls : ''
9627         };
9628         if (this.parent().xtype.match(/^Nav/)) {
9629             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9630
9631         }
9632
9633         if (this.labelAlign == 'left' ) {
9634             cfg.cls += ' form-horizontal';
9635         }
9636
9637
9638         return cfg;
9639     },
9640     initEvents : function()
9641     {
9642         this.el.on('submit', this.onSubmit, this);
9643         // this was added as random key presses on the form where triggering form submit.
9644         this.el.on('keypress', function(e) {
9645             if (e.getCharCode() != 13) {
9646                 return true;
9647             }
9648             // we might need to allow it for textareas.. and some other items.
9649             // check e.getTarget().
9650
9651             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9652                 return true;
9653             }
9654
9655             Roo.log("keypress blocked");
9656
9657             e.preventDefault();
9658             return false;
9659         });
9660         
9661     },
9662     // private
9663     onSubmit : function(e){
9664         e.stopEvent();
9665     },
9666
9667      /**
9668      * Returns true if client-side validation on the form is successful.
9669      * @return Boolean
9670      */
9671     isValid : function(){
9672         var items = this.getItems();
9673         var valid = true;
9674         var target = false;
9675         
9676         items.each(function(f){
9677             
9678             if(f.validate()){
9679                 return;
9680             }
9681             
9682             Roo.log('invalid field: ' + f.name);
9683             
9684             valid = false;
9685
9686             if(!target && f.el.isVisible(true)){
9687                 target = f;
9688             }
9689            
9690         });
9691         
9692         if(this.errorMask && !valid){
9693             Roo.bootstrap.Form.popover.mask(this, target);
9694         }
9695         
9696         return valid;
9697     },
9698     
9699     /**
9700      * Returns true if any fields in this form have changed since their original load.
9701      * @return Boolean
9702      */
9703     isDirty : function(){
9704         var dirty = false;
9705         var items = this.getItems();
9706         items.each(function(f){
9707            if(f.isDirty()){
9708                dirty = true;
9709                return false;
9710            }
9711            return true;
9712         });
9713         return dirty;
9714     },
9715      /**
9716      * Performs a predefined action (submit or load) or custom actions you define on this form.
9717      * @param {String} actionName The name of the action type
9718      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9719      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9720      * accept other config options):
9721      * <pre>
9722 Property          Type             Description
9723 ----------------  ---------------  ----------------------------------------------------------------------------------
9724 url               String           The url for the action (defaults to the form's url)
9725 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9726 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9727 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9728                                    validate the form on the client (defaults to false)
9729      * </pre>
9730      * @return {BasicForm} this
9731      */
9732     doAction : function(action, options){
9733         if(typeof action == 'string'){
9734             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9735         }
9736         if(this.fireEvent('beforeaction', this, action) !== false){
9737             this.beforeAction(action);
9738             action.run.defer(100, action);
9739         }
9740         return this;
9741     },
9742
9743     // private
9744     beforeAction : function(action){
9745         var o = action.options;
9746         
9747         if(this.loadMask){
9748             
9749             if(this.maskBody){
9750                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9751             } else {
9752                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9753             }
9754         }
9755         // not really supported yet.. ??
9756
9757         //if(this.waitMsgTarget === true){
9758         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9759         //}else if(this.waitMsgTarget){
9760         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9761         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9762         //}else {
9763         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9764        // }
9765
9766     },
9767
9768     // private
9769     afterAction : function(action, success){
9770         this.activeAction = null;
9771         var o = action.options;
9772
9773         if(this.loadMask){
9774             
9775             if(this.maskBody){
9776                 Roo.get(document.body).unmask();
9777             } else {
9778                 this.el.unmask();
9779             }
9780         }
9781         
9782         //if(this.waitMsgTarget === true){
9783 //            this.el.unmask();
9784         //}else if(this.waitMsgTarget){
9785         //    this.waitMsgTarget.unmask();
9786         //}else{
9787         //    Roo.MessageBox.updateProgress(1);
9788         //    Roo.MessageBox.hide();
9789        // }
9790         //
9791         if(success){
9792             if(o.reset){
9793                 this.reset();
9794             }
9795             Roo.callback(o.success, o.scope, [this, action]);
9796             this.fireEvent('actioncomplete', this, action);
9797
9798         }else{
9799
9800             // failure condition..
9801             // we have a scenario where updates need confirming.
9802             // eg. if a locking scenario exists..
9803             // we look for { errors : { needs_confirm : true }} in the response.
9804             if (
9805                 (typeof(action.result) != 'undefined')  &&
9806                 (typeof(action.result.errors) != 'undefined')  &&
9807                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9808            ){
9809                 var _t = this;
9810                 Roo.log("not supported yet");
9811                  /*
9812
9813                 Roo.MessageBox.confirm(
9814                     "Change requires confirmation",
9815                     action.result.errorMsg,
9816                     function(r) {
9817                         if (r != 'yes') {
9818                             return;
9819                         }
9820                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9821                     }
9822
9823                 );
9824                 */
9825
9826
9827                 return;
9828             }
9829
9830             Roo.callback(o.failure, o.scope, [this, action]);
9831             // show an error message if no failed handler is set..
9832             if (!this.hasListener('actionfailed')) {
9833                 Roo.log("need to add dialog support");
9834                 /*
9835                 Roo.MessageBox.alert("Error",
9836                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9837                         action.result.errorMsg :
9838                         "Saving Failed, please check your entries or try again"
9839                 );
9840                 */
9841             }
9842
9843             this.fireEvent('actionfailed', this, action);
9844         }
9845
9846     },
9847     /**
9848      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9849      * @param {String} id The value to search for
9850      * @return Field
9851      */
9852     findField : function(id){
9853         var items = this.getItems();
9854         var field = items.get(id);
9855         if(!field){
9856              items.each(function(f){
9857                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9858                     field = f;
9859                     return false;
9860                 }
9861                 return true;
9862             });
9863         }
9864         return field || null;
9865     },
9866      /**
9867      * Mark fields in this form invalid in bulk.
9868      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9869      * @return {BasicForm} this
9870      */
9871     markInvalid : function(errors){
9872         if(errors instanceof Array){
9873             for(var i = 0, len = errors.length; i < len; i++){
9874                 var fieldError = errors[i];
9875                 var f = this.findField(fieldError.id);
9876                 if(f){
9877                     f.markInvalid(fieldError.msg);
9878                 }
9879             }
9880         }else{
9881             var field, id;
9882             for(id in errors){
9883                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9884                     field.markInvalid(errors[id]);
9885                 }
9886             }
9887         }
9888         //Roo.each(this.childForms || [], function (f) {
9889         //    f.markInvalid(errors);
9890         //});
9891
9892         return this;
9893     },
9894
9895     /**
9896      * Set values for fields in this form in bulk.
9897      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9898      * @return {BasicForm} this
9899      */
9900     setValues : function(values){
9901         if(values instanceof Array){ // array of objects
9902             for(var i = 0, len = values.length; i < len; i++){
9903                 var v = values[i];
9904                 var f = this.findField(v.id);
9905                 if(f){
9906                     f.setValue(v.value);
9907                     if(this.trackResetOnLoad){
9908                         f.originalValue = f.getValue();
9909                     }
9910                 }
9911             }
9912         }else{ // object hash
9913             var field, id;
9914             for(id in values){
9915                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9916
9917                     if (field.setFromData &&
9918                         field.valueField &&
9919                         field.displayField &&
9920                         // combos' with local stores can
9921                         // be queried via setValue()
9922                         // to set their value..
9923                         (field.store && !field.store.isLocal)
9924                         ) {
9925                         // it's a combo
9926                         var sd = { };
9927                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9928                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9929                         field.setFromData(sd);
9930
9931                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9932                         
9933                         field.setFromData(values);
9934                         
9935                     } else {
9936                         field.setValue(values[id]);
9937                     }
9938
9939
9940                     if(this.trackResetOnLoad){
9941                         field.originalValue = field.getValue();
9942                     }
9943                 }
9944             }
9945         }
9946
9947         //Roo.each(this.childForms || [], function (f) {
9948         //    f.setValues(values);
9949         //});
9950
9951         return this;
9952     },
9953
9954     /**
9955      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9956      * they are returned as an array.
9957      * @param {Boolean} asString
9958      * @return {Object}
9959      */
9960     getValues : function(asString){
9961         //if (this.childForms) {
9962             // copy values from the child forms
9963         //    Roo.each(this.childForms, function (f) {
9964         //        this.setValues(f.getValues());
9965         //    }, this);
9966         //}
9967
9968
9969
9970         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9971         if(asString === true){
9972             return fs;
9973         }
9974         return Roo.urlDecode(fs);
9975     },
9976
9977     /**
9978      * Returns the fields in this form as an object with key/value pairs.
9979      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9980      * @return {Object}
9981      */
9982     getFieldValues : function(with_hidden)
9983     {
9984         var items = this.getItems();
9985         var ret = {};
9986         items.each(function(f){
9987             
9988             if (!f.getName()) {
9989                 return;
9990             }
9991             
9992             var v = f.getValue();
9993             
9994             if (f.inputType =='radio') {
9995                 if (typeof(ret[f.getName()]) == 'undefined') {
9996                     ret[f.getName()] = ''; // empty..
9997                 }
9998
9999                 if (!f.el.dom.checked) {
10000                     return;
10001
10002                 }
10003                 v = f.el.dom.value;
10004
10005             }
10006             
10007             if(f.xtype == 'MoneyField'){
10008                 ret[f.currencyName] = f.getCurrency();
10009             }
10010
10011             // not sure if this supported any more..
10012             if ((typeof(v) == 'object') && f.getRawValue) {
10013                 v = f.getRawValue() ; // dates..
10014             }
10015             // combo boxes where name != hiddenName...
10016             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10017                 ret[f.name] = f.getRawValue();
10018             }
10019             ret[f.getName()] = v;
10020         });
10021
10022         return ret;
10023     },
10024
10025     /**
10026      * Clears all invalid messages in this form.
10027      * @return {BasicForm} this
10028      */
10029     clearInvalid : function(){
10030         var items = this.getItems();
10031
10032         items.each(function(f){
10033            f.clearInvalid();
10034         });
10035
10036         return this;
10037     },
10038
10039     /**
10040      * Resets this form.
10041      * @return {BasicForm} this
10042      */
10043     reset : function(){
10044         var items = this.getItems();
10045         items.each(function(f){
10046             f.reset();
10047         });
10048
10049         Roo.each(this.childForms || [], function (f) {
10050             f.reset();
10051         });
10052
10053
10054         return this;
10055     },
10056     
10057     getItems : function()
10058     {
10059         var r=new Roo.util.MixedCollection(false, function(o){
10060             return o.id || (o.id = Roo.id());
10061         });
10062         var iter = function(el) {
10063             if (el.inputEl) {
10064                 r.add(el);
10065             }
10066             if (!el.items) {
10067                 return;
10068             }
10069             Roo.each(el.items,function(e) {
10070                 iter(e);
10071             });
10072         };
10073
10074         iter(this);
10075         return r;
10076     },
10077     
10078     hideFields : function(items)
10079     {
10080         Roo.each(items, function(i){
10081             
10082             var f = this.findField(i);
10083             
10084             if(!f){
10085                 return;
10086             }
10087             
10088             f.hide();
10089             
10090         }, this);
10091     },
10092     
10093     showFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.show();
10104             
10105         }, this);
10106     }
10107
10108 });
10109
10110 Roo.apply(Roo.bootstrap.Form, {
10111     
10112     popover : {
10113         
10114         padding : 5,
10115         
10116         isApplied : false,
10117         
10118         isMasked : false,
10119         
10120         form : false,
10121         
10122         target : false,
10123         
10124         toolTip : false,
10125         
10126         intervalID : false,
10127         
10128         maskEl : false,
10129         
10130         apply : function()
10131         {
10132             if(this.isApplied){
10133                 return;
10134             }
10135             
10136             this.maskEl = {
10137                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10138                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10139                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10140                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10141             };
10142             
10143             this.maskEl.top.enableDisplayMode("block");
10144             this.maskEl.left.enableDisplayMode("block");
10145             this.maskEl.bottom.enableDisplayMode("block");
10146             this.maskEl.right.enableDisplayMode("block");
10147             
10148             this.toolTip = new Roo.bootstrap.Tooltip({
10149                 cls : 'roo-form-error-popover',
10150                 alignment : {
10151                     'left' : ['r-l', [-2,0], 'right'],
10152                     'right' : ['l-r', [2,0], 'left'],
10153                     'bottom' : ['tl-bl', [0,2], 'top'],
10154                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10155                 }
10156             });
10157             
10158             this.toolTip.render(Roo.get(document.body));
10159
10160             this.toolTip.el.enableDisplayMode("block");
10161             
10162             Roo.get(document.body).on('click', function(){
10163                 this.unmask();
10164             }, this);
10165             
10166             Roo.get(document.body).on('touchstart', function(){
10167                 this.unmask();
10168             }, this);
10169             
10170             this.isApplied = true
10171         },
10172         
10173         mask : function(form, target)
10174         {
10175             this.form = form;
10176             
10177             this.target = target;
10178             
10179             if(!this.form.errorMask || !target.el){
10180                 return;
10181             }
10182             
10183             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10184             
10185             Roo.log(scrollable);
10186             
10187             var ot = this.target.el.calcOffsetsTo(scrollable);
10188             
10189             var scrollTo = ot[1] - this.form.maskOffset;
10190             
10191             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10192             
10193             scrollable.scrollTo('top', scrollTo);
10194             
10195             var box = this.target.el.getBox();
10196             Roo.log(box);
10197             var zIndex = Roo.bootstrap.Modal.zIndex++;
10198
10199             
10200             this.maskEl.top.setStyle('position', 'absolute');
10201             this.maskEl.top.setStyle('z-index', zIndex);
10202             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10203             this.maskEl.top.setLeft(0);
10204             this.maskEl.top.setTop(0);
10205             this.maskEl.top.show();
10206             
10207             this.maskEl.left.setStyle('position', 'absolute');
10208             this.maskEl.left.setStyle('z-index', zIndex);
10209             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10210             this.maskEl.left.setLeft(0);
10211             this.maskEl.left.setTop(box.y - this.padding);
10212             this.maskEl.left.show();
10213
10214             this.maskEl.bottom.setStyle('position', 'absolute');
10215             this.maskEl.bottom.setStyle('z-index', zIndex);
10216             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10217             this.maskEl.bottom.setLeft(0);
10218             this.maskEl.bottom.setTop(box.bottom + this.padding);
10219             this.maskEl.bottom.show();
10220
10221             this.maskEl.right.setStyle('position', 'absolute');
10222             this.maskEl.right.setStyle('z-index', zIndex);
10223             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10224             this.maskEl.right.setLeft(box.right + this.padding);
10225             this.maskEl.right.setTop(box.y - this.padding);
10226             this.maskEl.right.show();
10227
10228             this.toolTip.bindEl = this.target.el;
10229
10230             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10231
10232             var tip = this.target.blankText;
10233
10234             if(this.target.getValue() !== '' ) {
10235                 
10236                 if (this.target.invalidText.length) {
10237                     tip = this.target.invalidText;
10238                 } else if (this.target.regexText.length){
10239                     tip = this.target.regexText;
10240                 }
10241             }
10242
10243             this.toolTip.show(tip);
10244
10245             this.intervalID = window.setInterval(function() {
10246                 Roo.bootstrap.Form.popover.unmask();
10247             }, 10000);
10248
10249             window.onwheel = function(){ return false;};
10250             
10251             (function(){ this.isMasked = true; }).defer(500, this);
10252             
10253         },
10254         
10255         unmask : function()
10256         {
10257             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10258                 return;
10259             }
10260             
10261             this.maskEl.top.setStyle('position', 'absolute');
10262             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10263             this.maskEl.top.hide();
10264
10265             this.maskEl.left.setStyle('position', 'absolute');
10266             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10267             this.maskEl.left.hide();
10268
10269             this.maskEl.bottom.setStyle('position', 'absolute');
10270             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10271             this.maskEl.bottom.hide();
10272
10273             this.maskEl.right.setStyle('position', 'absolute');
10274             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.right.hide();
10276             
10277             this.toolTip.hide();
10278             
10279             this.toolTip.el.hide();
10280             
10281             window.onwheel = function(){ return true;};
10282             
10283             if(this.intervalID){
10284                 window.clearInterval(this.intervalID);
10285                 this.intervalID = false;
10286             }
10287             
10288             this.isMasked = false;
10289             
10290         }
10291         
10292     }
10293     
10294 });
10295
10296 /*
10297  * Based on:
10298  * Ext JS Library 1.1.1
10299  * Copyright(c) 2006-2007, Ext JS, LLC.
10300  *
10301  * Originally Released Under LGPL - original licence link has changed is not relivant.
10302  *
10303  * Fork - LGPL
10304  * <script type="text/javascript">
10305  */
10306 /**
10307  * @class Roo.form.VTypes
10308  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10309  * @singleton
10310  */
10311 Roo.form.VTypes = function(){
10312     // closure these in so they are only created once.
10313     var alpha = /^[a-zA-Z_]+$/;
10314     var alphanum = /^[a-zA-Z0-9_]+$/;
10315     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10316     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10317
10318     // All these messages and functions are configurable
10319     return {
10320         /**
10321          * The function used to validate email addresses
10322          * @param {String} value The email address
10323          */
10324         'email' : function(v){
10325             return email.test(v);
10326         },
10327         /**
10328          * The error text to display when the email validation function returns false
10329          * @type String
10330          */
10331         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10332         /**
10333          * The keystroke filter mask to be applied on email input
10334          * @type RegExp
10335          */
10336         'emailMask' : /[a-z0-9_\.\-@]/i,
10337
10338         /**
10339          * The function used to validate URLs
10340          * @param {String} value The URL
10341          */
10342         'url' : function(v){
10343             return url.test(v);
10344         },
10345         /**
10346          * The error text to display when the url validation function returns false
10347          * @type String
10348          */
10349         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10350         
10351         /**
10352          * The function used to validate alpha values
10353          * @param {String} value The value
10354          */
10355         'alpha' : function(v){
10356             return alpha.test(v);
10357         },
10358         /**
10359          * The error text to display when the alpha validation function returns false
10360          * @type String
10361          */
10362         'alphaText' : 'This field should only contain letters and _',
10363         /**
10364          * The keystroke filter mask to be applied on alpha input
10365          * @type RegExp
10366          */
10367         'alphaMask' : /[a-z_]/i,
10368
10369         /**
10370          * The function used to validate alphanumeric values
10371          * @param {String} value The value
10372          */
10373         'alphanum' : function(v){
10374             return alphanum.test(v);
10375         },
10376         /**
10377          * The error text to display when the alphanumeric validation function returns false
10378          * @type String
10379          */
10380         'alphanumText' : 'This field should only contain letters, numbers and _',
10381         /**
10382          * The keystroke filter mask to be applied on alphanumeric input
10383          * @type RegExp
10384          */
10385         'alphanumMask' : /[a-z0-9_]/i
10386     };
10387 }();/*
10388  * - LGPL
10389  *
10390  * Input
10391  * 
10392  */
10393
10394 /**
10395  * @class Roo.bootstrap.Input
10396  * @extends Roo.bootstrap.Component
10397  * Bootstrap Input class
10398  * @cfg {Boolean} disabled is it disabled
10399  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10400  * @cfg {String} name name of the input
10401  * @cfg {string} fieldLabel - the label associated
10402  * @cfg {string} placeholder - placeholder to put in text.
10403  * @cfg {string}  before - input group add on before
10404  * @cfg {string} after - input group add on after
10405  * @cfg {string} size - (lg|sm) or leave empty..
10406  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10407  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10408  * @cfg {Number} md colspan out of 12 for computer-sized screens
10409  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10410  * @cfg {string} value default value of the input
10411  * @cfg {Number} labelWidth set the width of label 
10412  * @cfg {Number} labellg set the width of label (1-12)
10413  * @cfg {Number} labelmd set the width of label (1-12)
10414  * @cfg {Number} labelsm set the width of label (1-12)
10415  * @cfg {Number} labelxs set the width of label (1-12)
10416  * @cfg {String} labelAlign (top|left)
10417  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10418  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10419  * @cfg {String} indicatorpos (left|right) default left
10420  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10421  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10422  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10423
10424  * @cfg {String} align (left|center|right) Default left
10425  * @cfg {Boolean} forceFeedback (true|false) Default false
10426  * 
10427  * @constructor
10428  * Create a new Input
10429  * @param {Object} config The config object
10430  */
10431
10432 Roo.bootstrap.Input = function(config){
10433     
10434     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10435     
10436     this.addEvents({
10437         /**
10438          * @event focus
10439          * Fires when this field receives input focus.
10440          * @param {Roo.form.Field} this
10441          */
10442         focus : true,
10443         /**
10444          * @event blur
10445          * Fires when this field loses input focus.
10446          * @param {Roo.form.Field} this
10447          */
10448         blur : true,
10449         /**
10450          * @event specialkey
10451          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10452          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10453          * @param {Roo.form.Field} this
10454          * @param {Roo.EventObject} e The event object
10455          */
10456         specialkey : true,
10457         /**
10458          * @event change
10459          * Fires just before the field blurs if the field value has changed.
10460          * @param {Roo.form.Field} this
10461          * @param {Mixed} newValue The new value
10462          * @param {Mixed} oldValue The original value
10463          */
10464         change : true,
10465         /**
10466          * @event invalid
10467          * Fires after the field has been marked as invalid.
10468          * @param {Roo.form.Field} this
10469          * @param {String} msg The validation message
10470          */
10471         invalid : true,
10472         /**
10473          * @event valid
10474          * Fires after the field has been validated with no errors.
10475          * @param {Roo.form.Field} this
10476          */
10477         valid : true,
10478          /**
10479          * @event keyup
10480          * Fires after the key up
10481          * @param {Roo.form.Field} this
10482          * @param {Roo.EventObject}  e The event Object
10483          */
10484         keyup : true
10485     });
10486 };
10487
10488 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10489      /**
10490      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10491       automatic validation (defaults to "keyup").
10492      */
10493     validationEvent : "keyup",
10494      /**
10495      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10496      */
10497     validateOnBlur : true,
10498     /**
10499      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10500      */
10501     validationDelay : 250,
10502      /**
10503      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10504      */
10505     focusClass : "x-form-focus",  // not needed???
10506     
10507        
10508     /**
10509      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10510      */
10511     invalidClass : "has-warning",
10512     
10513     /**
10514      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10515      */
10516     validClass : "has-success",
10517     
10518     /**
10519      * @cfg {Boolean} hasFeedback (true|false) default true
10520      */
10521     hasFeedback : true,
10522     
10523     /**
10524      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10525      */
10526     invalidFeedbackClass : "glyphicon-warning-sign",
10527     
10528     /**
10529      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10530      */
10531     validFeedbackClass : "glyphicon-ok",
10532     
10533     /**
10534      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10535      */
10536     selectOnFocus : false,
10537     
10538      /**
10539      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10540      */
10541     maskRe : null,
10542        /**
10543      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10544      */
10545     vtype : null,
10546     
10547       /**
10548      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10549      */
10550     disableKeyFilter : false,
10551     
10552        /**
10553      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10554      */
10555     disabled : false,
10556      /**
10557      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10558      */
10559     allowBlank : true,
10560     /**
10561      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10562      */
10563     blankText : "Please complete this mandatory field",
10564     
10565      /**
10566      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10567      */
10568     minLength : 0,
10569     /**
10570      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10571      */
10572     maxLength : Number.MAX_VALUE,
10573     /**
10574      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10575      */
10576     minLengthText : "The minimum length for this field is {0}",
10577     /**
10578      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10579      */
10580     maxLengthText : "The maximum length for this field is {0}",
10581   
10582     
10583     /**
10584      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10585      * If available, this function will be called only after the basic validators all return true, and will be passed the
10586      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10587      */
10588     validator : null,
10589     /**
10590      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10591      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10592      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10593      */
10594     regex : null,
10595     /**
10596      * @cfg {String} regexText -- Depricated - use Invalid Text
10597      */
10598     regexText : "",
10599     
10600     /**
10601      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10602      */
10603     invalidText : "",
10604     
10605     
10606     
10607     autocomplete: false,
10608     
10609     
10610     fieldLabel : '',
10611     inputType : 'text',
10612     
10613     name : false,
10614     placeholder: false,
10615     before : false,
10616     after : false,
10617     size : false,
10618     hasFocus : false,
10619     preventMark: false,
10620     isFormField : true,
10621     value : '',
10622     labelWidth : 2,
10623     labelAlign : false,
10624     readOnly : false,
10625     align : false,
10626     formatedValue : false,
10627     forceFeedback : false,
10628     
10629     indicatorpos : 'left',
10630     
10631     labellg : 0,
10632     labelmd : 0,
10633     labelsm : 0,
10634     labelxs : 0,
10635     
10636     capture : '',
10637     accept : '',
10638     
10639     parentLabelAlign : function()
10640     {
10641         var parent = this;
10642         while (parent.parent()) {
10643             parent = parent.parent();
10644             if (typeof(parent.labelAlign) !='undefined') {
10645                 return parent.labelAlign;
10646             }
10647         }
10648         return 'left';
10649         
10650     },
10651     
10652     getAutoCreate : function()
10653     {
10654         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10655         
10656         var id = Roo.id();
10657         
10658         var cfg = {};
10659         
10660         if(this.inputType != 'hidden'){
10661             cfg.cls = 'form-group' //input-group
10662         }
10663         
10664         var input =  {
10665             tag: 'input',
10666             id : id,
10667             type : this.inputType,
10668             value : this.value,
10669             cls : 'form-control',
10670             placeholder : this.placeholder || '',
10671             autocomplete : this.autocomplete || 'new-password'
10672         };
10673         if (this.inputType == 'file') {
10674             input.style = 'overflow:hidden'; // why not in CSS?
10675         }
10676         
10677         if(this.capture.length){
10678             input.capture = this.capture;
10679         }
10680         
10681         if(this.accept.length){
10682             input.accept = this.accept + "/*";
10683         }
10684         
10685         if(this.align){
10686             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10687         }
10688         
10689         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10690             input.maxLength = this.maxLength;
10691         }
10692         
10693         if (this.disabled) {
10694             input.disabled=true;
10695         }
10696         
10697         if (this.readOnly) {
10698             input.readonly=true;
10699         }
10700         
10701         if (this.name) {
10702             input.name = this.name;
10703         }
10704         
10705         if (this.size) {
10706             input.cls += ' input-' + this.size;
10707         }
10708         
10709         var settings=this;
10710         ['xs','sm','md','lg'].map(function(size){
10711             if (settings[size]) {
10712                 cfg.cls += ' col-' + size + '-' + settings[size];
10713             }
10714         });
10715         
10716         var inputblock = input;
10717         
10718         var feedback = {
10719             tag: 'span',
10720             cls: 'glyphicon form-control-feedback'
10721         };
10722             
10723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10724             
10725             inputblock = {
10726                 cls : 'has-feedback',
10727                 cn :  [
10728                     input,
10729                     feedback
10730                 ] 
10731             };  
10732         }
10733         
10734         if (this.before || this.after) {
10735             
10736             inputblock = {
10737                 cls : 'input-group',
10738                 cn :  [] 
10739             };
10740             
10741             if (this.before && typeof(this.before) == 'string') {
10742                 
10743                 inputblock.cn.push({
10744                     tag :'span',
10745                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10746                     html : this.before
10747                 });
10748             }
10749             if (this.before && typeof(this.before) == 'object') {
10750                 this.before = Roo.factory(this.before);
10751                 
10752                 inputblock.cn.push({
10753                     tag :'span',
10754                     cls : 'roo-input-before input-group-prepend   input-group-' +
10755                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10756                 });
10757             }
10758             
10759             inputblock.cn.push(input);
10760             
10761             if (this.after && typeof(this.after) == 'string') {
10762                 inputblock.cn.push({
10763                     tag :'span',
10764                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10765                     html : this.after
10766                 });
10767             }
10768             if (this.after && typeof(this.after) == 'object') {
10769                 this.after = Roo.factory(this.after);
10770                 
10771                 inputblock.cn.push({
10772                     tag :'span',
10773                     cls : 'roo-input-after input-group-append  input-group-' +
10774                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10775                 });
10776             }
10777             
10778             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10779                 inputblock.cls += ' has-feedback';
10780                 inputblock.cn.push(feedback);
10781             }
10782         };
10783         var indicator = {
10784             tag : 'i',
10785             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10786             tooltip : 'This field is required'
10787         };
10788         if (this.allowBlank ) {
10789             indicator.style = this.allowBlank ? ' display:none' : '';
10790         }
10791         if (align ==='left' && this.fieldLabel.length) {
10792             
10793             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10794             
10795             cfg.cn = [
10796                 indicator,
10797                 {
10798                     tag: 'label',
10799                     'for' :  id,
10800                     cls : 'control-label col-form-label',
10801                     html : this.fieldLabel
10802
10803                 },
10804                 {
10805                     cls : "", 
10806                     cn: [
10807                         inputblock
10808                     ]
10809                 }
10810             ];
10811             
10812             var labelCfg = cfg.cn[1];
10813             var contentCfg = cfg.cn[2];
10814             
10815             if(this.indicatorpos == 'right'){
10816                 cfg.cn = [
10817                     {
10818                         tag: 'label',
10819                         'for' :  id,
10820                         cls : 'control-label col-form-label',
10821                         cn : [
10822                             {
10823                                 tag : 'span',
10824                                 html : this.fieldLabel
10825                             },
10826                             indicator
10827                         ]
10828                     },
10829                     {
10830                         cls : "",
10831                         cn: [
10832                             inputblock
10833                         ]
10834                     }
10835
10836                 ];
10837                 
10838                 labelCfg = cfg.cn[0];
10839                 contentCfg = cfg.cn[1];
10840             
10841             }
10842             
10843             if(this.labelWidth > 12){
10844                 labelCfg.style = "width: " + this.labelWidth + 'px';
10845             }
10846             
10847             if(this.labelWidth < 13 && this.labelmd == 0){
10848                 this.labelmd = this.labelWidth;
10849             }
10850             
10851             if(this.labellg > 0){
10852                 labelCfg.cls += ' col-lg-' + this.labellg;
10853                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10854             }
10855             
10856             if(this.labelmd > 0){
10857                 labelCfg.cls += ' col-md-' + this.labelmd;
10858                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10859             }
10860             
10861             if(this.labelsm > 0){
10862                 labelCfg.cls += ' col-sm-' + this.labelsm;
10863                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10864             }
10865             
10866             if(this.labelxs > 0){
10867                 labelCfg.cls += ' col-xs-' + this.labelxs;
10868                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10869             }
10870             
10871             
10872         } else if ( this.fieldLabel.length) {
10873                 
10874             
10875             
10876             cfg.cn = [
10877                 {
10878                     tag : 'i',
10879                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10880                     tooltip : 'This field is required',
10881                     style : this.allowBlank ? ' display:none' : '' 
10882                 },
10883                 {
10884                     tag: 'label',
10885                    //cls : 'input-group-addon',
10886                     html : this.fieldLabel
10887
10888                 },
10889
10890                inputblock
10891
10892            ];
10893            
10894            if(this.indicatorpos == 'right'){
10895        
10896                 cfg.cn = [
10897                     {
10898                         tag: 'label',
10899                        //cls : 'input-group-addon',
10900                         html : this.fieldLabel
10901
10902                     },
10903                     {
10904                         tag : 'i',
10905                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10906                         tooltip : 'This field is required',
10907                         style : this.allowBlank ? ' display:none' : '' 
10908                     },
10909
10910                    inputblock
10911
10912                ];
10913
10914             }
10915
10916         } else {
10917             
10918             cfg.cn = [
10919
10920                     inputblock
10921
10922             ];
10923                 
10924                 
10925         };
10926         
10927         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10928            cfg.cls += ' navbar-form';
10929         }
10930         
10931         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10932             // on BS4 we do this only if not form 
10933             cfg.cls += ' navbar-form';
10934             cfg.tag = 'li';
10935         }
10936         
10937         return cfg;
10938         
10939     },
10940     /**
10941      * return the real input element.
10942      */
10943     inputEl: function ()
10944     {
10945         return this.el.select('input.form-control',true).first();
10946     },
10947     
10948     tooltipEl : function()
10949     {
10950         return this.inputEl();
10951     },
10952     
10953     indicatorEl : function()
10954     {
10955         if (Roo.bootstrap.version == 4) {
10956             return false; // not enabled in v4 yet.
10957         }
10958         
10959         var indicator = this.el.select('i.roo-required-indicator',true).first();
10960         
10961         if(!indicator){
10962             return false;
10963         }
10964         
10965         return indicator;
10966         
10967     },
10968     
10969     setDisabled : function(v)
10970     {
10971         var i  = this.inputEl().dom;
10972         if (!v) {
10973             i.removeAttribute('disabled');
10974             return;
10975             
10976         }
10977         i.setAttribute('disabled','true');
10978     },
10979     initEvents : function()
10980     {
10981           
10982         this.inputEl().on("keydown" , this.fireKey,  this);
10983         this.inputEl().on("focus", this.onFocus,  this);
10984         this.inputEl().on("blur", this.onBlur,  this);
10985         
10986         this.inputEl().relayEvent('keyup', this);
10987         
10988         this.indicator = this.indicatorEl();
10989         
10990         if(this.indicator){
10991             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10992         }
10993  
10994         // reference to original value for reset
10995         this.originalValue = this.getValue();
10996         //Roo.form.TextField.superclass.initEvents.call(this);
10997         if(this.validationEvent == 'keyup'){
10998             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10999             this.inputEl().on('keyup', this.filterValidation, this);
11000         }
11001         else if(this.validationEvent !== false){
11002             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11003         }
11004         
11005         if(this.selectOnFocus){
11006             this.on("focus", this.preFocus, this);
11007             
11008         }
11009         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11010             this.inputEl().on("keypress", this.filterKeys, this);
11011         } else {
11012             this.inputEl().relayEvent('keypress', this);
11013         }
11014        /* if(this.grow){
11015             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11016             this.el.on("click", this.autoSize,  this);
11017         }
11018         */
11019         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11020             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11021         }
11022         
11023         if (typeof(this.before) == 'object') {
11024             this.before.render(this.el.select('.roo-input-before',true).first());
11025         }
11026         if (typeof(this.after) == 'object') {
11027             this.after.render(this.el.select('.roo-input-after',true).first());
11028         }
11029         
11030         this.inputEl().on('change', this.onChange, this);
11031         
11032     },
11033     filterValidation : function(e){
11034         if(!e.isNavKeyPress()){
11035             this.validationTask.delay(this.validationDelay);
11036         }
11037     },
11038      /**
11039      * Validates the field value
11040      * @return {Boolean} True if the value is valid, else false
11041      */
11042     validate : function(){
11043         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11044         if(this.disabled || this.validateValue(this.getRawValue())){
11045             this.markValid();
11046             return true;
11047         }
11048         
11049         this.markInvalid();
11050         return false;
11051     },
11052     
11053     
11054     /**
11055      * Validates a value according to the field's validation rules and marks the field as invalid
11056      * if the validation fails
11057      * @param {Mixed} value The value to validate
11058      * @return {Boolean} True if the value is valid, else false
11059      */
11060     validateValue : function(value)
11061     {
11062         if(this.getVisibilityEl().hasClass('hidden')){
11063             return true;
11064         }
11065         
11066         if(value.length < 1)  { // if it's blank
11067             if(this.allowBlank){
11068                 return true;
11069             }
11070             return false;
11071         }
11072         
11073         if(value.length < this.minLength){
11074             return false;
11075         }
11076         if(value.length > this.maxLength){
11077             return false;
11078         }
11079         if(this.vtype){
11080             var vt = Roo.form.VTypes;
11081             if(!vt[this.vtype](value, this)){
11082                 return false;
11083             }
11084         }
11085         if(typeof this.validator == "function"){
11086             var msg = this.validator(value);
11087             if(msg !== true){
11088                 return false;
11089             }
11090             if (typeof(msg) == 'string') {
11091                 this.invalidText = msg;
11092             }
11093         }
11094         
11095         if(this.regex && !this.regex.test(value)){
11096             return false;
11097         }
11098         
11099         return true;
11100     },
11101     
11102      // private
11103     fireKey : function(e){
11104         //Roo.log('field ' + e.getKey());
11105         if(e.isNavKeyPress()){
11106             this.fireEvent("specialkey", this, e);
11107         }
11108     },
11109     focus : function (selectText){
11110         if(this.rendered){
11111             this.inputEl().focus();
11112             if(selectText === true){
11113                 this.inputEl().dom.select();
11114             }
11115         }
11116         return this;
11117     } ,
11118     
11119     onFocus : function(){
11120         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11121            // this.el.addClass(this.focusClass);
11122         }
11123         if(!this.hasFocus){
11124             this.hasFocus = true;
11125             this.startValue = this.getValue();
11126             this.fireEvent("focus", this);
11127         }
11128     },
11129     
11130     beforeBlur : Roo.emptyFn,
11131
11132     
11133     // private
11134     onBlur : function(){
11135         this.beforeBlur();
11136         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11137             //this.el.removeClass(this.focusClass);
11138         }
11139         this.hasFocus = false;
11140         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11141             this.validate();
11142         }
11143         var v = this.getValue();
11144         if(String(v) !== String(this.startValue)){
11145             this.fireEvent('change', this, v, this.startValue);
11146         }
11147         this.fireEvent("blur", this);
11148     },
11149     
11150     onChange : function(e)
11151     {
11152         var v = this.getValue();
11153         if(String(v) !== String(this.startValue)){
11154             this.fireEvent('change', this, v, this.startValue);
11155         }
11156         
11157     },
11158     
11159     /**
11160      * Resets the current field value to the originally loaded value and clears any validation messages
11161      */
11162     reset : function(){
11163         this.setValue(this.originalValue);
11164         this.validate();
11165     },
11166      /**
11167      * Returns the name of the field
11168      * @return {Mixed} name The name field
11169      */
11170     getName: function(){
11171         return this.name;
11172     },
11173      /**
11174      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11175      * @return {Mixed} value The field value
11176      */
11177     getValue : function(){
11178         
11179         var v = this.inputEl().getValue();
11180         
11181         return v;
11182     },
11183     /**
11184      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11185      * @return {Mixed} value The field value
11186      */
11187     getRawValue : function(){
11188         var v = this.inputEl().getValue();
11189         
11190         return v;
11191     },
11192     
11193     /**
11194      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11195      * @param {Mixed} value The value to set
11196      */
11197     setRawValue : function(v){
11198         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11199     },
11200     
11201     selectText : function(start, end){
11202         var v = this.getRawValue();
11203         if(v.length > 0){
11204             start = start === undefined ? 0 : start;
11205             end = end === undefined ? v.length : end;
11206             var d = this.inputEl().dom;
11207             if(d.setSelectionRange){
11208                 d.setSelectionRange(start, end);
11209             }else if(d.createTextRange){
11210                 var range = d.createTextRange();
11211                 range.moveStart("character", start);
11212                 range.moveEnd("character", v.length-end);
11213                 range.select();
11214             }
11215         }
11216     },
11217     
11218     /**
11219      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11220      * @param {Mixed} value The value to set
11221      */
11222     setValue : function(v){
11223         this.value = v;
11224         if(this.rendered){
11225             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11226             this.validate();
11227         }
11228     },
11229     
11230     /*
11231     processValue : function(value){
11232         if(this.stripCharsRe){
11233             var newValue = value.replace(this.stripCharsRe, '');
11234             if(newValue !== value){
11235                 this.setRawValue(newValue);
11236                 return newValue;
11237             }
11238         }
11239         return value;
11240     },
11241   */
11242     preFocus : function(){
11243         
11244         if(this.selectOnFocus){
11245             this.inputEl().dom.select();
11246         }
11247     },
11248     filterKeys : function(e){
11249         var k = e.getKey();
11250         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11251             return;
11252         }
11253         var c = e.getCharCode(), cc = String.fromCharCode(c);
11254         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11255             return;
11256         }
11257         if(!this.maskRe.test(cc)){
11258             e.stopEvent();
11259         }
11260     },
11261      /**
11262      * Clear any invalid styles/messages for this field
11263      */
11264     clearInvalid : function(){
11265         
11266         if(!this.el || this.preventMark){ // not rendered
11267             return;
11268         }
11269         
11270         
11271         this.el.removeClass([this.invalidClass, 'is-invalid']);
11272         
11273         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11274             
11275             var feedback = this.el.select('.form-control-feedback', true).first();
11276             
11277             if(feedback){
11278                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11279             }
11280             
11281         }
11282         
11283         if(this.indicator){
11284             this.indicator.removeClass('visible');
11285             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11286         }
11287         
11288         this.fireEvent('valid', this);
11289     },
11290     
11291      /**
11292      * Mark this field as valid
11293      */
11294     markValid : function()
11295     {
11296         if(!this.el  || this.preventMark){ // not rendered...
11297             return;
11298         }
11299         
11300         this.el.removeClass([this.invalidClass, this.validClass]);
11301         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11302
11303         var feedback = this.el.select('.form-control-feedback', true).first();
11304             
11305         if(feedback){
11306             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11307         }
11308         
11309         if(this.indicator){
11310             this.indicator.removeClass('visible');
11311             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11312         }
11313         
11314         if(this.disabled){
11315             return;
11316         }
11317         
11318            
11319         if(this.allowBlank && !this.getRawValue().length){
11320             return;
11321         }
11322         if (Roo.bootstrap.version == 3) {
11323             this.el.addClass(this.validClass);
11324         } else {
11325             this.inputEl().addClass('is-valid');
11326         }
11327
11328         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11329             
11330             var feedback = this.el.select('.form-control-feedback', true).first();
11331             
11332             if(feedback){
11333                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11334                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11335             }
11336             
11337         }
11338         
11339         this.fireEvent('valid', this);
11340     },
11341     
11342      /**
11343      * Mark this field as invalid
11344      * @param {String} msg The validation message
11345      */
11346     markInvalid : function(msg)
11347     {
11348         if(!this.el  || this.preventMark){ // not rendered
11349             return;
11350         }
11351         
11352         this.el.removeClass([this.invalidClass, this.validClass]);
11353         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11354         
11355         var feedback = this.el.select('.form-control-feedback', true).first();
11356             
11357         if(feedback){
11358             this.el.select('.form-control-feedback', true).first().removeClass(
11359                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11360         }
11361
11362         if(this.disabled){
11363             return;
11364         }
11365         
11366         if(this.allowBlank && !this.getRawValue().length){
11367             return;
11368         }
11369         
11370         if(this.indicator){
11371             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11372             this.indicator.addClass('visible');
11373         }
11374         if (Roo.bootstrap.version == 3) {
11375             this.el.addClass(this.invalidClass);
11376         } else {
11377             this.inputEl().addClass('is-invalid');
11378         }
11379         
11380         
11381         
11382         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11383             
11384             var feedback = this.el.select('.form-control-feedback', true).first();
11385             
11386             if(feedback){
11387                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11388                 
11389                 if(this.getValue().length || this.forceFeedback){
11390                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11391                 }
11392                 
11393             }
11394             
11395         }
11396         
11397         this.fireEvent('invalid', this, msg);
11398     },
11399     // private
11400     SafariOnKeyDown : function(event)
11401     {
11402         // this is a workaround for a password hang bug on chrome/ webkit.
11403         if (this.inputEl().dom.type != 'password') {
11404             return;
11405         }
11406         
11407         var isSelectAll = false;
11408         
11409         if(this.inputEl().dom.selectionEnd > 0){
11410             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11411         }
11412         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11413             event.preventDefault();
11414             this.setValue('');
11415             return;
11416         }
11417         
11418         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11419             
11420             event.preventDefault();
11421             // this is very hacky as keydown always get's upper case.
11422             //
11423             var cc = String.fromCharCode(event.getCharCode());
11424             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11425             
11426         }
11427     },
11428     adjustWidth : function(tag, w){
11429         tag = tag.toLowerCase();
11430         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11431             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11432                 if(tag == 'input'){
11433                     return w + 2;
11434                 }
11435                 if(tag == 'textarea'){
11436                     return w-2;
11437                 }
11438             }else if(Roo.isOpera){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }
11446         }
11447         return w;
11448     },
11449     
11450     setFieldLabel : function(v)
11451     {
11452         if(!this.rendered){
11453             return;
11454         }
11455         
11456         if(this.indicatorEl()){
11457             var ar = this.el.select('label > span',true);
11458             
11459             if (ar.elements.length) {
11460                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11461                 this.fieldLabel = v;
11462                 return;
11463             }
11464             
11465             var br = this.el.select('label',true);
11466             
11467             if(br.elements.length) {
11468                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11469                 this.fieldLabel = v;
11470                 return;
11471             }
11472             
11473             Roo.log('Cannot Found any of label > span || label in input');
11474             return;
11475         }
11476         
11477         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11478         this.fieldLabel = v;
11479         
11480         
11481     }
11482 });
11483
11484  
11485 /*
11486  * - LGPL
11487  *
11488  * Input
11489  * 
11490  */
11491
11492 /**
11493  * @class Roo.bootstrap.TextArea
11494  * @extends Roo.bootstrap.Input
11495  * Bootstrap TextArea class
11496  * @cfg {Number} cols Specifies the visible width of a text area
11497  * @cfg {Number} rows Specifies the visible number of lines in a text area
11498  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11499  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11500  * @cfg {string} html text
11501  * 
11502  * @constructor
11503  * Create a new TextArea
11504  * @param {Object} config The config object
11505  */
11506
11507 Roo.bootstrap.TextArea = function(config){
11508     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11509    
11510 };
11511
11512 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11513      
11514     cols : false,
11515     rows : 5,
11516     readOnly : false,
11517     warp : 'soft',
11518     resize : false,
11519     value: false,
11520     html: false,
11521     
11522     getAutoCreate : function(){
11523         
11524         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11525         
11526         var id = Roo.id();
11527         
11528         var cfg = {};
11529         
11530         if(this.inputType != 'hidden'){
11531             cfg.cls = 'form-group' //input-group
11532         }
11533         
11534         var input =  {
11535             tag: 'textarea',
11536             id : id,
11537             warp : this.warp,
11538             rows : this.rows,
11539             value : this.value || '',
11540             html: this.html || '',
11541             cls : 'form-control',
11542             placeholder : this.placeholder || '' 
11543             
11544         };
11545         
11546         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11547             input.maxLength = this.maxLength;
11548         }
11549         
11550         if(this.resize){
11551             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11552         }
11553         
11554         if(this.cols){
11555             input.cols = this.cols;
11556         }
11557         
11558         if (this.readOnly) {
11559             input.readonly = true;
11560         }
11561         
11562         if (this.name) {
11563             input.name = this.name;
11564         }
11565         
11566         if (this.size) {
11567             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11568         }
11569         
11570         var settings=this;
11571         ['xs','sm','md','lg'].map(function(size){
11572             if (settings[size]) {
11573                 cfg.cls += ' col-' + size + '-' + settings[size];
11574             }
11575         });
11576         
11577         var inputblock = input;
11578         
11579         if(this.hasFeedback && !this.allowBlank){
11580             
11581             var feedback = {
11582                 tag: 'span',
11583                 cls: 'glyphicon form-control-feedback'
11584             };
11585
11586             inputblock = {
11587                 cls : 'has-feedback',
11588                 cn :  [
11589                     input,
11590                     feedback
11591                 ] 
11592             };  
11593         }
11594         
11595         
11596         if (this.before || this.after) {
11597             
11598             inputblock = {
11599                 cls : 'input-group',
11600                 cn :  [] 
11601             };
11602             if (this.before) {
11603                 inputblock.cn.push({
11604                     tag :'span',
11605                     cls : 'input-group-addon',
11606                     html : this.before
11607                 });
11608             }
11609             
11610             inputblock.cn.push(input);
11611             
11612             if(this.hasFeedback && !this.allowBlank){
11613                 inputblock.cls += ' has-feedback';
11614                 inputblock.cn.push(feedback);
11615             }
11616             
11617             if (this.after) {
11618                 inputblock.cn.push({
11619                     tag :'span',
11620                     cls : 'input-group-addon',
11621                     html : this.after
11622                 });
11623             }
11624             
11625         }
11626         
11627         if (align ==='left' && this.fieldLabel.length) {
11628             cfg.cn = [
11629                 {
11630                     tag: 'label',
11631                     'for' :  id,
11632                     cls : 'control-label',
11633                     html : this.fieldLabel
11634                 },
11635                 {
11636                     cls : "",
11637                     cn: [
11638                         inputblock
11639                     ]
11640                 }
11641
11642             ];
11643             
11644             if(this.labelWidth > 12){
11645                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11646             }
11647
11648             if(this.labelWidth < 13 && this.labelmd == 0){
11649                 this.labelmd = this.labelWidth;
11650             }
11651
11652             if(this.labellg > 0){
11653                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11654                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11655             }
11656
11657             if(this.labelmd > 0){
11658                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11659                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11660             }
11661
11662             if(this.labelsm > 0){
11663                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11664                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11665             }
11666
11667             if(this.labelxs > 0){
11668                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11669                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11670             }
11671             
11672         } else if ( this.fieldLabel.length) {
11673             cfg.cn = [
11674
11675                {
11676                    tag: 'label',
11677                    //cls : 'input-group-addon',
11678                    html : this.fieldLabel
11679
11680                },
11681
11682                inputblock
11683
11684            ];
11685
11686         } else {
11687
11688             cfg.cn = [
11689
11690                 inputblock
11691
11692             ];
11693                 
11694         }
11695         
11696         if (this.disabled) {
11697             input.disabled=true;
11698         }
11699         
11700         return cfg;
11701         
11702     },
11703     /**
11704      * return the real textarea element.
11705      */
11706     inputEl: function ()
11707     {
11708         return this.el.select('textarea.form-control',true).first();
11709     },
11710     
11711     /**
11712      * Clear any invalid styles/messages for this field
11713      */
11714     clearInvalid : function()
11715     {
11716         
11717         if(!this.el || this.preventMark){ // not rendered
11718             return;
11719         }
11720         
11721         var label = this.el.select('label', true).first();
11722         var icon = this.el.select('i.fa-star', true).first();
11723         
11724         if(label && icon){
11725             icon.remove();
11726         }
11727         this.el.removeClass( this.validClass);
11728         this.inputEl().removeClass('is-invalid');
11729          
11730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11731             
11732             var feedback = this.el.select('.form-control-feedback', true).first();
11733             
11734             if(feedback){
11735                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11736             }
11737             
11738         }
11739         
11740         this.fireEvent('valid', this);
11741     },
11742     
11743      /**
11744      * Mark this field as valid
11745      */
11746     markValid : function()
11747     {
11748         if(!this.el  || this.preventMark){ // not rendered
11749             return;
11750         }
11751         
11752         this.el.removeClass([this.invalidClass, this.validClass]);
11753         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11754         
11755         var feedback = this.el.select('.form-control-feedback', true).first();
11756             
11757         if(feedback){
11758             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11759         }
11760
11761         if(this.disabled || this.allowBlank){
11762             return;
11763         }
11764         
11765         var label = this.el.select('label', true).first();
11766         var icon = this.el.select('i.fa-star', true).first();
11767         
11768         if(label && icon){
11769             icon.remove();
11770         }
11771         if (Roo.bootstrap.version == 3) {
11772             this.el.addClass(this.validClass);
11773         } else {
11774             this.inputEl().addClass('is-valid');
11775         }
11776         
11777         
11778         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11779             
11780             var feedback = this.el.select('.form-control-feedback', true).first();
11781             
11782             if(feedback){
11783                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11784                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11785             }
11786             
11787         }
11788         
11789         this.fireEvent('valid', this);
11790     },
11791     
11792      /**
11793      * Mark this field as invalid
11794      * @param {String} msg The validation message
11795      */
11796     markInvalid : function(msg)
11797     {
11798         if(!this.el  || this.preventMark){ // not rendered
11799             return;
11800         }
11801         
11802         this.el.removeClass([this.invalidClass, this.validClass]);
11803         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11804         
11805         var feedback = this.el.select('.form-control-feedback', true).first();
11806             
11807         if(feedback){
11808             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11809         }
11810
11811         if(this.disabled || this.allowBlank){
11812             return;
11813         }
11814         
11815         var label = this.el.select('label', true).first();
11816         var icon = this.el.select('i.fa-star', true).first();
11817         
11818         if(!this.getValue().length && label && !icon){
11819             this.el.createChild({
11820                 tag : 'i',
11821                 cls : 'text-danger fa fa-lg fa-star',
11822                 tooltip : 'This field is required',
11823                 style : 'margin-right:5px;'
11824             }, label, true);
11825         }
11826         
11827         if (Roo.bootstrap.version == 3) {
11828             this.el.addClass(this.invalidClass);
11829         } else {
11830             this.inputEl().addClass('is-invalid');
11831         }
11832         
11833         // fixme ... this may be depricated need to test..
11834         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11835             
11836             var feedback = this.el.select('.form-control-feedback', true).first();
11837             
11838             if(feedback){
11839                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11840                 
11841                 if(this.getValue().length || this.forceFeedback){
11842                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11843                 }
11844                 
11845             }
11846             
11847         }
11848         
11849         this.fireEvent('invalid', this, msg);
11850     }
11851 });
11852
11853  
11854 /*
11855  * - LGPL
11856  *
11857  * trigger field - base class for combo..
11858  * 
11859  */
11860  
11861 /**
11862  * @class Roo.bootstrap.TriggerField
11863  * @extends Roo.bootstrap.Input
11864  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11865  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11866  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11867  * for which you can provide a custom implementation.  For example:
11868  * <pre><code>
11869 var trigger = new Roo.bootstrap.TriggerField();
11870 trigger.onTriggerClick = myTriggerFn;
11871 trigger.applyTo('my-field');
11872 </code></pre>
11873  *
11874  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11875  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11876  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11877  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11878  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11879
11880  * @constructor
11881  * Create a new TriggerField.
11882  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11883  * to the base TextField)
11884  */
11885 Roo.bootstrap.TriggerField = function(config){
11886     this.mimicing = false;
11887     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11888 };
11889
11890 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11891     /**
11892      * @cfg {String} triggerClass A CSS class to apply to the trigger
11893      */
11894      /**
11895      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11896      */
11897     hideTrigger:false,
11898
11899     /**
11900      * @cfg {Boolean} removable (true|false) special filter default false
11901      */
11902     removable : false,
11903     
11904     /** @cfg {Boolean} grow @hide */
11905     /** @cfg {Number} growMin @hide */
11906     /** @cfg {Number} growMax @hide */
11907
11908     /**
11909      * @hide 
11910      * @method
11911      */
11912     autoSize: Roo.emptyFn,
11913     // private
11914     monitorTab : true,
11915     // private
11916     deferHeight : true,
11917
11918     
11919     actionMode : 'wrap',
11920     
11921     caret : false,
11922     
11923     
11924     getAutoCreate : function(){
11925        
11926         var align = this.labelAlign || this.parentLabelAlign();
11927         
11928         var id = Roo.id();
11929         
11930         var cfg = {
11931             cls: 'form-group' //input-group
11932         };
11933         
11934         
11935         var input =  {
11936             tag: 'input',
11937             id : id,
11938             type : this.inputType,
11939             cls : 'form-control',
11940             autocomplete: 'new-password',
11941             placeholder : this.placeholder || '' 
11942             
11943         };
11944         if (this.name) {
11945             input.name = this.name;
11946         }
11947         if (this.size) {
11948             input.cls += ' input-' + this.size;
11949         }
11950         
11951         if (this.disabled) {
11952             input.disabled=true;
11953         }
11954         
11955         var inputblock = input;
11956         
11957         if(this.hasFeedback && !this.allowBlank){
11958             
11959             var feedback = {
11960                 tag: 'span',
11961                 cls: 'glyphicon form-control-feedback'
11962             };
11963             
11964             if(this.removable && !this.editable  ){
11965                 inputblock = {
11966                     cls : 'has-feedback',
11967                     cn :  [
11968                         inputblock,
11969                         {
11970                             tag: 'button',
11971                             html : 'x',
11972                             cls : 'roo-combo-removable-btn close'
11973                         },
11974                         feedback
11975                     ] 
11976                 };
11977             } else {
11978                 inputblock = {
11979                     cls : 'has-feedback',
11980                     cn :  [
11981                         inputblock,
11982                         feedback
11983                     ] 
11984                 };
11985             }
11986
11987         } else {
11988             if(this.removable && !this.editable ){
11989                 inputblock = {
11990                     cls : 'roo-removable',
11991                     cn :  [
11992                         inputblock,
11993                         {
11994                             tag: 'button',
11995                             html : 'x',
11996                             cls : 'roo-combo-removable-btn close'
11997                         }
11998                     ] 
11999                 };
12000             }
12001         }
12002         
12003         if (this.before || this.after) {
12004             
12005             inputblock = {
12006                 cls : 'input-group',
12007                 cn :  [] 
12008             };
12009             if (this.before) {
12010                 inputblock.cn.push({
12011                     tag :'span',
12012                     cls : 'input-group-addon input-group-prepend input-group-text',
12013                     html : this.before
12014                 });
12015             }
12016             
12017             inputblock.cn.push(input);
12018             
12019             if(this.hasFeedback && !this.allowBlank){
12020                 inputblock.cls += ' has-feedback';
12021                 inputblock.cn.push(feedback);
12022             }
12023             
12024             if (this.after) {
12025                 inputblock.cn.push({
12026                     tag :'span',
12027                     cls : 'input-group-addon input-group-append input-group-text',
12028                     html : this.after
12029                 });
12030             }
12031             
12032         };
12033         
12034       
12035         
12036         var ibwrap = inputblock;
12037         
12038         if(this.multiple){
12039             ibwrap = {
12040                 tag: 'ul',
12041                 cls: 'roo-select2-choices',
12042                 cn:[
12043                     {
12044                         tag: 'li',
12045                         cls: 'roo-select2-search-field',
12046                         cn: [
12047
12048                             inputblock
12049                         ]
12050                     }
12051                 ]
12052             };
12053                 
12054         }
12055         
12056         var combobox = {
12057             cls: 'roo-select2-container input-group',
12058             cn: [
12059                  {
12060                     tag: 'input',
12061                     type : 'hidden',
12062                     cls: 'form-hidden-field'
12063                 },
12064                 ibwrap
12065             ]
12066         };
12067         
12068         if(!this.multiple && this.showToggleBtn){
12069             
12070             var caret = {
12071                         tag: 'span',
12072                         cls: 'caret'
12073              };
12074             if (this.caret != false) {
12075                 caret = {
12076                      tag: 'i',
12077                      cls: 'fa fa-' + this.caret
12078                 };
12079                 
12080             }
12081             
12082             combobox.cn.push({
12083                 tag :'span',
12084                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12085                 cn : [
12086                     Roo.bootstrap.version == 3 ? caret : '',
12087                     {
12088                         tag: 'span',
12089                         cls: 'combobox-clear',
12090                         cn  : [
12091                             {
12092                                 tag : 'i',
12093                                 cls: 'icon-remove'
12094                             }
12095                         ]
12096                     }
12097                 ]
12098
12099             })
12100         }
12101         
12102         if(this.multiple){
12103             combobox.cls += ' roo-select2-container-multi';
12104         }
12105          var indicator = {
12106             tag : 'i',
12107             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12108             tooltip : 'This field is required'
12109         };
12110         if (Roo.bootstrap.version == 4) {
12111             indicator = {
12112                 tag : 'i',
12113                 style : 'display:none'
12114             };
12115         }
12116         
12117         
12118         if (align ==='left' && this.fieldLabel.length) {
12119             
12120             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12121
12122             cfg.cn = [
12123                 indicator,
12124                 {
12125                     tag: 'label',
12126                     'for' :  id,
12127                     cls : 'control-label',
12128                     html : this.fieldLabel
12129
12130                 },
12131                 {
12132                     cls : "", 
12133                     cn: [
12134                         combobox
12135                     ]
12136                 }
12137
12138             ];
12139             
12140             var labelCfg = cfg.cn[1];
12141             var contentCfg = cfg.cn[2];
12142             
12143             if(this.indicatorpos == 'right'){
12144                 cfg.cn = [
12145                     {
12146                         tag: 'label',
12147                         'for' :  id,
12148                         cls : 'control-label',
12149                         cn : [
12150                             {
12151                                 tag : 'span',
12152                                 html : this.fieldLabel
12153                             },
12154                             indicator
12155                         ]
12156                     },
12157                     {
12158                         cls : "", 
12159                         cn: [
12160                             combobox
12161                         ]
12162                     }
12163
12164                 ];
12165                 
12166                 labelCfg = cfg.cn[0];
12167                 contentCfg = cfg.cn[1];
12168             }
12169             
12170             if(this.labelWidth > 12){
12171                 labelCfg.style = "width: " + this.labelWidth + 'px';
12172             }
12173             
12174             if(this.labelWidth < 13 && this.labelmd == 0){
12175                 this.labelmd = this.labelWidth;
12176             }
12177             
12178             if(this.labellg > 0){
12179                 labelCfg.cls += ' col-lg-' + this.labellg;
12180                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12181             }
12182             
12183             if(this.labelmd > 0){
12184                 labelCfg.cls += ' col-md-' + this.labelmd;
12185                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12186             }
12187             
12188             if(this.labelsm > 0){
12189                 labelCfg.cls += ' col-sm-' + this.labelsm;
12190                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12191             }
12192             
12193             if(this.labelxs > 0){
12194                 labelCfg.cls += ' col-xs-' + this.labelxs;
12195                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12196             }
12197             
12198         } else if ( this.fieldLabel.length) {
12199 //                Roo.log(" label");
12200             cfg.cn = [
12201                 indicator,
12202                {
12203                    tag: 'label',
12204                    //cls : 'input-group-addon',
12205                    html : this.fieldLabel
12206
12207                },
12208
12209                combobox
12210
12211             ];
12212             
12213             if(this.indicatorpos == 'right'){
12214                 
12215                 cfg.cn = [
12216                     {
12217                        tag: 'label',
12218                        cn : [
12219                            {
12220                                tag : 'span',
12221                                html : this.fieldLabel
12222                            },
12223                            indicator
12224                        ]
12225
12226                     },
12227                     combobox
12228
12229                 ];
12230
12231             }
12232
12233         } else {
12234             
12235 //                Roo.log(" no label && no align");
12236                 cfg = combobox
12237                      
12238                 
12239         }
12240         
12241         var settings=this;
12242         ['xs','sm','md','lg'].map(function(size){
12243             if (settings[size]) {
12244                 cfg.cls += ' col-' + size + '-' + settings[size];
12245             }
12246         });
12247         
12248         return cfg;
12249         
12250     },
12251     
12252     
12253     
12254     // private
12255     onResize : function(w, h){
12256 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12257 //        if(typeof w == 'number'){
12258 //            var x = w - this.trigger.getWidth();
12259 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12260 //            this.trigger.setStyle('left', x+'px');
12261 //        }
12262     },
12263
12264     // private
12265     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12266
12267     // private
12268     getResizeEl : function(){
12269         return this.inputEl();
12270     },
12271
12272     // private
12273     getPositionEl : function(){
12274         return this.inputEl();
12275     },
12276
12277     // private
12278     alignErrorIcon : function(){
12279         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12280     },
12281
12282     // private
12283     initEvents : function(){
12284         
12285         this.createList();
12286         
12287         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12288         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12289         if(!this.multiple && this.showToggleBtn){
12290             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12291             if(this.hideTrigger){
12292                 this.trigger.setDisplayed(false);
12293             }
12294             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12295         }
12296         
12297         if(this.multiple){
12298             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12299         }
12300         
12301         if(this.removable && !this.editable && !this.tickable){
12302             var close = this.closeTriggerEl();
12303             
12304             if(close){
12305                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12306                 close.on('click', this.removeBtnClick, this, close);
12307             }
12308         }
12309         
12310         //this.trigger.addClassOnOver('x-form-trigger-over');
12311         //this.trigger.addClassOnClick('x-form-trigger-click');
12312         
12313         //if(!this.width){
12314         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12315         //}
12316     },
12317     
12318     closeTriggerEl : function()
12319     {
12320         var close = this.el.select('.roo-combo-removable-btn', true).first();
12321         return close ? close : false;
12322     },
12323     
12324     removeBtnClick : function(e, h, el)
12325     {
12326         e.preventDefault();
12327         
12328         if(this.fireEvent("remove", this) !== false){
12329             this.reset();
12330             this.fireEvent("afterremove", this)
12331         }
12332     },
12333     
12334     createList : function()
12335     {
12336         this.list = Roo.get(document.body).createChild({
12337             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12338             cls: 'typeahead typeahead-long dropdown-menu',
12339             style: 'display:none'
12340         });
12341         
12342         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12343         
12344     },
12345
12346     // private
12347     initTrigger : function(){
12348        
12349     },
12350
12351     // private
12352     onDestroy : function(){
12353         if(this.trigger){
12354             this.trigger.removeAllListeners();
12355           //  this.trigger.remove();
12356         }
12357         //if(this.wrap){
12358         //    this.wrap.remove();
12359         //}
12360         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12361     },
12362
12363     // private
12364     onFocus : function(){
12365         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12366         /*
12367         if(!this.mimicing){
12368             this.wrap.addClass('x-trigger-wrap-focus');
12369             this.mimicing = true;
12370             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12371             if(this.monitorTab){
12372                 this.el.on("keydown", this.checkTab, this);
12373             }
12374         }
12375         */
12376     },
12377
12378     // private
12379     checkTab : function(e){
12380         if(e.getKey() == e.TAB){
12381             this.triggerBlur();
12382         }
12383     },
12384
12385     // private
12386     onBlur : function(){
12387         // do nothing
12388     },
12389
12390     // private
12391     mimicBlur : function(e, t){
12392         /*
12393         if(!this.wrap.contains(t) && this.validateBlur()){
12394             this.triggerBlur();
12395         }
12396         */
12397     },
12398
12399     // private
12400     triggerBlur : function(){
12401         this.mimicing = false;
12402         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12403         if(this.monitorTab){
12404             this.el.un("keydown", this.checkTab, this);
12405         }
12406         //this.wrap.removeClass('x-trigger-wrap-focus');
12407         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12408     },
12409
12410     // private
12411     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12412     validateBlur : function(e, t){
12413         return true;
12414     },
12415
12416     // private
12417     onDisable : function(){
12418         this.inputEl().dom.disabled = true;
12419         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12420         //if(this.wrap){
12421         //    this.wrap.addClass('x-item-disabled');
12422         //}
12423     },
12424
12425     // private
12426     onEnable : function(){
12427         this.inputEl().dom.disabled = false;
12428         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12429         //if(this.wrap){
12430         //    this.el.removeClass('x-item-disabled');
12431         //}
12432     },
12433
12434     // private
12435     onShow : function(){
12436         var ae = this.getActionEl();
12437         
12438         if(ae){
12439             ae.dom.style.display = '';
12440             ae.dom.style.visibility = 'visible';
12441         }
12442     },
12443
12444     // private
12445     
12446     onHide : function(){
12447         var ae = this.getActionEl();
12448         ae.dom.style.display = 'none';
12449     },
12450
12451     /**
12452      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12453      * by an implementing function.
12454      * @method
12455      * @param {EventObject} e
12456      */
12457     onTriggerClick : Roo.emptyFn
12458 });
12459  
12460 /*
12461 * Licence: LGPL
12462 */
12463
12464 /**
12465  * @class Roo.bootstrap.CardUploader
12466  * @extends Roo.bootstrap.Button
12467  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12468  * @cfg {Number} errorTimeout default 3000
12469  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12470  * @cfg {Array}  html The button text.
12471
12472  *
12473  * @constructor
12474  * Create a new CardUploader
12475  * @param {Object} config The config object
12476  */
12477
12478 Roo.bootstrap.CardUploader = function(config){
12479     
12480  
12481     
12482     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12483     
12484     
12485     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12486         return r.data.id
12487         });
12488     
12489     
12490 };
12491
12492 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12493     
12494      
12495     errorTimeout : 3000,
12496      
12497     images : false,
12498    
12499     fileCollection : false,
12500     allowBlank : true,
12501     
12502     getAutoCreate : function()
12503     {
12504         
12505         var cfg =  {
12506             cls :'form-group' ,
12507             cn : [
12508                
12509                 {
12510                     tag: 'label',
12511                    //cls : 'input-group-addon',
12512                     html : this.fieldLabel
12513
12514                 },
12515
12516                 {
12517                     tag: 'input',
12518                     type : 'hidden',
12519                     value : this.value,
12520                     cls : 'd-none  form-control'
12521                 },
12522                 
12523                 {
12524                     tag: 'input',
12525                     multiple : 'multiple',
12526                     type : 'file',
12527                     cls : 'd-none  roo-card-upload-selector'
12528                 },
12529                 
12530                 {
12531                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12532                 },
12533                 {
12534                     cls : 'card-columns roo-card-uploader-container'
12535                 }
12536
12537             ]
12538         };
12539            
12540          
12541         return cfg;
12542     },
12543     
12544     getChildContainer : function() /// what children are added to.
12545     {
12546         return this.containerEl;
12547     },
12548    
12549     getButtonContainer : function() /// what children are added to.
12550     {
12551         return this.el.select(".roo-card-uploader-button-container").first();
12552     },
12553    
12554     initEvents : function()
12555     {
12556         
12557         Roo.bootstrap.Input.prototype.initEvents.call(this);
12558         
12559         var t = this;
12560         this.addxtype({
12561             xns: Roo.bootstrap,
12562
12563             xtype : 'Button',
12564             container_method : 'getButtonContainer' ,            
12565             html :  this.html, // fix changable?
12566             cls : 'w-100 ',
12567             listeners : {
12568                 'click' : function(btn, e) {
12569                     t.onClick(e);
12570                 }
12571             }
12572         });
12573         
12574         
12575         
12576         
12577         this.urlAPI = (window.createObjectURL && window) || 
12578                                 (window.URL && URL.revokeObjectURL && URL) || 
12579                                 (window.webkitURL && webkitURL);
12580                         
12581          
12582          
12583          
12584         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12585         
12586         this.selectorEl.on('change', this.onFileSelected, this);
12587         if (this.images) {
12588             var t = this;
12589             this.images.forEach(function(img) {
12590                 t.addCard(img)
12591             });
12592             this.images = false;
12593         }
12594         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12595          
12596        
12597     },
12598     
12599    
12600     onClick : function(e)
12601     {
12602         e.preventDefault();
12603          
12604         this.selectorEl.dom.click();
12605          
12606     },
12607     
12608     onFileSelected : function(e)
12609     {
12610         e.preventDefault();
12611         
12612         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12613             return;
12614         }
12615         
12616         Roo.each(this.selectorEl.dom.files, function(file){    
12617             this.addFile(file);
12618         }, this);
12619          
12620     },
12621     
12622       
12623     
12624       
12625     
12626     addFile : function(file)
12627     {
12628            
12629         if(typeof(file) === 'string'){
12630             throw "Add file by name?"; // should not happen
12631             return;
12632         }
12633         
12634         if(!file || !this.urlAPI){
12635             return;
12636         }
12637         
12638         // file;
12639         // file.type;
12640         
12641         var _this = this;
12642         
12643         
12644         var url = _this.urlAPI.createObjectURL( file);
12645            
12646         this.addCard({
12647             id : Roo.bootstrap.CardUploader.ID--,
12648             is_uploaded : false,
12649             src : url,
12650             title : file.name,
12651             mimetype : file.type,
12652             preview : false,
12653             is_deleted : 0
12654         })
12655         
12656     },
12657     
12658     addCard : function (data)
12659     {
12660         // hidden input element?
12661         // if the file is not an image...
12662         //then we need to use something other that and header_image
12663         var t = this;
12664         //   remove.....
12665         var footer = [
12666             {
12667                 xns : Roo.bootstrap,
12668                 xtype : 'CardFooter',
12669                 items: [
12670                     {
12671                         xns : Roo.bootstrap,
12672                         xtype : 'Element',
12673                         cls : 'd-flex',
12674                         items : [
12675                             
12676                             {
12677                                 xns : Roo.bootstrap,
12678                                 xtype : 'Button',
12679                                 html : String.format("<small>{0}</small>", data.title),
12680                                 cls : 'col-11 text-left',
12681                                 size: 'sm',
12682                                 weight: 'link',
12683                                 fa : 'download',
12684                                 listeners : {
12685                                     click : function() {
12686                                         this.downloadCard(data.id)
12687                                     }
12688                                 }
12689                             },
12690                           
12691                             {
12692                                 xns : Roo.bootstrap,
12693                                 xtype : 'Button',
12694                                 
12695                                 size : 'sm',
12696                                 weight: 'danger',
12697                                 cls : 'col-1',
12698                                 fa : 'times',
12699                                 listeners : {
12700                                     click : function() {
12701                                         t.removeCard(data.id)
12702                                     }
12703                                 }
12704                             }
12705                         ]
12706                     }
12707                     
12708                 ] 
12709             }
12710             
12711         ];
12712
12713         var cn = this.addxtype(
12714             {
12715                  
12716                 xns : Roo.bootstrap,
12717                 xtype : 'Card',
12718                 closeable : true,
12719                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12720                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12721                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12722                 data : data,
12723                 html : false,
12724                  
12725                 items : footer,
12726                 initEvents : function() {
12727                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12728                     this.imgEl = this.el.select('.card-img-top').first();
12729                     if (this.imgEl) {
12730                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12731                         this.imgEl.set({ 'pointer' : 'cursor' });
12732                                   
12733                     }
12734                     
12735                   
12736                 }
12737                 
12738             }
12739         );
12740         // dont' really need ot update items.
12741         // this.items.push(cn);
12742         this.fileCollection.add(cn);
12743         this.updateInput();
12744         
12745     },
12746     removeCard : function(id)
12747     {
12748         
12749         var card  = this.fileCollection.get(id);
12750         card.data.is_deleted = 1;
12751         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12752         this.fileCollection.remove(card);
12753         //this.items = this.items.filter(function(e) { return e != card });
12754         // dont' really need ot update items.
12755         card.el.dom.parentNode.removeChild(card.el.dom);
12756         
12757     },
12758     reset: function()
12759     {
12760         this.fileCollection.each(function(card) {
12761             card.el.dom.parentNode.removeChild(card.el.dom);    
12762         });
12763         this.fileCollection.clear();
12764         this.updateInput();
12765     },
12766     
12767     updateInput : function()
12768     {
12769         var data = [];
12770         this.fileCollection.each(function(e) {
12771             data.push(e.data);
12772         });
12773         
12774         this.inputEl().dom.value = JSON.stringify(data);
12775     }
12776     
12777     
12778 });
12779
12780
12781 Roo.bootstrap.CardUploader.ID = -1;/*
12782  * Based on:
12783  * Ext JS Library 1.1.1
12784  * Copyright(c) 2006-2007, Ext JS, LLC.
12785  *
12786  * Originally Released Under LGPL - original licence link has changed is not relivant.
12787  *
12788  * Fork - LGPL
12789  * <script type="text/javascript">
12790  */
12791
12792
12793 /**
12794  * @class Roo.data.SortTypes
12795  * @singleton
12796  * Defines the default sorting (casting?) comparison functions used when sorting data.
12797  */
12798 Roo.data.SortTypes = {
12799     /**
12800      * Default sort that does nothing
12801      * @param {Mixed} s The value being converted
12802      * @return {Mixed} The comparison value
12803      */
12804     none : function(s){
12805         return s;
12806     },
12807     
12808     /**
12809      * The regular expression used to strip tags
12810      * @type {RegExp}
12811      * @property
12812      */
12813     stripTagsRE : /<\/?[^>]+>/gi,
12814     
12815     /**
12816      * Strips all HTML tags to sort on text only
12817      * @param {Mixed} s The value being converted
12818      * @return {String} The comparison value
12819      */
12820     asText : function(s){
12821         return String(s).replace(this.stripTagsRE, "");
12822     },
12823     
12824     /**
12825      * Strips all HTML tags to sort on text only - Case insensitive
12826      * @param {Mixed} s The value being converted
12827      * @return {String} The comparison value
12828      */
12829     asUCText : function(s){
12830         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12831     },
12832     
12833     /**
12834      * Case insensitive string
12835      * @param {Mixed} s The value being converted
12836      * @return {String} The comparison value
12837      */
12838     asUCString : function(s) {
12839         return String(s).toUpperCase();
12840     },
12841     
12842     /**
12843      * Date sorting
12844      * @param {Mixed} s The value being converted
12845      * @return {Number} The comparison value
12846      */
12847     asDate : function(s) {
12848         if(!s){
12849             return 0;
12850         }
12851         if(s instanceof Date){
12852             return s.getTime();
12853         }
12854         return Date.parse(String(s));
12855     },
12856     
12857     /**
12858      * Float sorting
12859      * @param {Mixed} s The value being converted
12860      * @return {Float} The comparison value
12861      */
12862     asFloat : function(s) {
12863         var val = parseFloat(String(s).replace(/,/g, ""));
12864         if(isNaN(val)) {
12865             val = 0;
12866         }
12867         return val;
12868     },
12869     
12870     /**
12871      * Integer sorting
12872      * @param {Mixed} s The value being converted
12873      * @return {Number} The comparison value
12874      */
12875     asInt : function(s) {
12876         var val = parseInt(String(s).replace(/,/g, ""));
12877         if(isNaN(val)) {
12878             val = 0;
12879         }
12880         return val;
12881     }
12882 };/*
12883  * Based on:
12884  * Ext JS Library 1.1.1
12885  * Copyright(c) 2006-2007, Ext JS, LLC.
12886  *
12887  * Originally Released Under LGPL - original licence link has changed is not relivant.
12888  *
12889  * Fork - LGPL
12890  * <script type="text/javascript">
12891  */
12892
12893 /**
12894 * @class Roo.data.Record
12895  * Instances of this class encapsulate both record <em>definition</em> information, and record
12896  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12897  * to access Records cached in an {@link Roo.data.Store} object.<br>
12898  * <p>
12899  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12900  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12901  * objects.<br>
12902  * <p>
12903  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12904  * @constructor
12905  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12906  * {@link #create}. The parameters are the same.
12907  * @param {Array} data An associative Array of data values keyed by the field name.
12908  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12909  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12910  * not specified an integer id is generated.
12911  */
12912 Roo.data.Record = function(data, id){
12913     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12914     this.data = data;
12915 };
12916
12917 /**
12918  * Generate a constructor for a specific record layout.
12919  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12920  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12921  * Each field definition object may contain the following properties: <ul>
12922  * <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,
12923  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12924  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12925  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12926  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12927  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12928  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12929  * this may be omitted.</p></li>
12930  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12931  * <ul><li>auto (Default, implies no conversion)</li>
12932  * <li>string</li>
12933  * <li>int</li>
12934  * <li>float</li>
12935  * <li>boolean</li>
12936  * <li>date</li></ul></p></li>
12937  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12938  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12939  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12940  * by the Reader into an object that will be stored in the Record. It is passed the
12941  * following parameters:<ul>
12942  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12943  * </ul></p></li>
12944  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12945  * </ul>
12946  * <br>usage:<br><pre><code>
12947 var TopicRecord = Roo.data.Record.create(
12948     {name: 'title', mapping: 'topic_title'},
12949     {name: 'author', mapping: 'username'},
12950     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12951     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12952     {name: 'lastPoster', mapping: 'user2'},
12953     {name: 'excerpt', mapping: 'post_text'}
12954 );
12955
12956 var myNewRecord = new TopicRecord({
12957     title: 'Do my job please',
12958     author: 'noobie',
12959     totalPosts: 1,
12960     lastPost: new Date(),
12961     lastPoster: 'Animal',
12962     excerpt: 'No way dude!'
12963 });
12964 myStore.add(myNewRecord);
12965 </code></pre>
12966  * @method create
12967  * @static
12968  */
12969 Roo.data.Record.create = function(o){
12970     var f = function(){
12971         f.superclass.constructor.apply(this, arguments);
12972     };
12973     Roo.extend(f, Roo.data.Record);
12974     var p = f.prototype;
12975     p.fields = new Roo.util.MixedCollection(false, function(field){
12976         return field.name;
12977     });
12978     for(var i = 0, len = o.length; i < len; i++){
12979         p.fields.add(new Roo.data.Field(o[i]));
12980     }
12981     f.getField = function(name){
12982         return p.fields.get(name);  
12983     };
12984     return f;
12985 };
12986
12987 Roo.data.Record.AUTO_ID = 1000;
12988 Roo.data.Record.EDIT = 'edit';
12989 Roo.data.Record.REJECT = 'reject';
12990 Roo.data.Record.COMMIT = 'commit';
12991
12992 Roo.data.Record.prototype = {
12993     /**
12994      * Readonly flag - true if this record has been modified.
12995      * @type Boolean
12996      */
12997     dirty : false,
12998     editing : false,
12999     error: null,
13000     modified: null,
13001
13002     // private
13003     join : function(store){
13004         this.store = store;
13005     },
13006
13007     /**
13008      * Set the named field to the specified value.
13009      * @param {String} name The name of the field to set.
13010      * @param {Object} value The value to set the field to.
13011      */
13012     set : function(name, value){
13013         if(this.data[name] == value){
13014             return;
13015         }
13016         this.dirty = true;
13017         if(!this.modified){
13018             this.modified = {};
13019         }
13020         if(typeof this.modified[name] == 'undefined'){
13021             this.modified[name] = this.data[name];
13022         }
13023         this.data[name] = value;
13024         if(!this.editing && this.store){
13025             this.store.afterEdit(this);
13026         }       
13027     },
13028
13029     /**
13030      * Get the value of the named field.
13031      * @param {String} name The name of the field to get the value of.
13032      * @return {Object} The value of the field.
13033      */
13034     get : function(name){
13035         return this.data[name]; 
13036     },
13037
13038     // private
13039     beginEdit : function(){
13040         this.editing = true;
13041         this.modified = {}; 
13042     },
13043
13044     // private
13045     cancelEdit : function(){
13046         this.editing = false;
13047         delete this.modified;
13048     },
13049
13050     // private
13051     endEdit : function(){
13052         this.editing = false;
13053         if(this.dirty && this.store){
13054             this.store.afterEdit(this);
13055         }
13056     },
13057
13058     /**
13059      * Usually called by the {@link Roo.data.Store} which owns the Record.
13060      * Rejects all changes made to the Record since either creation, or the last commit operation.
13061      * Modified fields are reverted to their original values.
13062      * <p>
13063      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13064      * of reject operations.
13065      */
13066     reject : function(){
13067         var m = this.modified;
13068         for(var n in m){
13069             if(typeof m[n] != "function"){
13070                 this.data[n] = m[n];
13071             }
13072         }
13073         this.dirty = false;
13074         delete this.modified;
13075         this.editing = false;
13076         if(this.store){
13077             this.store.afterReject(this);
13078         }
13079     },
13080
13081     /**
13082      * Usually called by the {@link Roo.data.Store} which owns the Record.
13083      * Commits all changes made to the Record since either creation, or the last commit operation.
13084      * <p>
13085      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13086      * of commit operations.
13087      */
13088     commit : function(){
13089         this.dirty = false;
13090         delete this.modified;
13091         this.editing = false;
13092         if(this.store){
13093             this.store.afterCommit(this);
13094         }
13095     },
13096
13097     // private
13098     hasError : function(){
13099         return this.error != null;
13100     },
13101
13102     // private
13103     clearError : function(){
13104         this.error = null;
13105     },
13106
13107     /**
13108      * Creates a copy of this record.
13109      * @param {String} id (optional) A new record id if you don't want to use this record's id
13110      * @return {Record}
13111      */
13112     copy : function(newId) {
13113         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13114     }
13115 };/*
13116  * Based on:
13117  * Ext JS Library 1.1.1
13118  * Copyright(c) 2006-2007, Ext JS, LLC.
13119  *
13120  * Originally Released Under LGPL - original licence link has changed is not relivant.
13121  *
13122  * Fork - LGPL
13123  * <script type="text/javascript">
13124  */
13125
13126
13127
13128 /**
13129  * @class Roo.data.Store
13130  * @extends Roo.util.Observable
13131  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13132  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13133  * <p>
13134  * 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
13135  * has no knowledge of the format of the data returned by the Proxy.<br>
13136  * <p>
13137  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13138  * instances from the data object. These records are cached and made available through accessor functions.
13139  * @constructor
13140  * Creates a new Store.
13141  * @param {Object} config A config object containing the objects needed for the Store to access data,
13142  * and read the data into Records.
13143  */
13144 Roo.data.Store = function(config){
13145     this.data = new Roo.util.MixedCollection(false);
13146     this.data.getKey = function(o){
13147         return o.id;
13148     };
13149     this.baseParams = {};
13150     // private
13151     this.paramNames = {
13152         "start" : "start",
13153         "limit" : "limit",
13154         "sort" : "sort",
13155         "dir" : "dir",
13156         "multisort" : "_multisort"
13157     };
13158
13159     if(config && config.data){
13160         this.inlineData = config.data;
13161         delete config.data;
13162     }
13163
13164     Roo.apply(this, config);
13165     
13166     if(this.reader){ // reader passed
13167         this.reader = Roo.factory(this.reader, Roo.data);
13168         this.reader.xmodule = this.xmodule || false;
13169         if(!this.recordType){
13170             this.recordType = this.reader.recordType;
13171         }
13172         if(this.reader.onMetaChange){
13173             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13174         }
13175     }
13176
13177     if(this.recordType){
13178         this.fields = this.recordType.prototype.fields;
13179     }
13180     this.modified = [];
13181
13182     this.addEvents({
13183         /**
13184          * @event datachanged
13185          * Fires when the data cache has changed, and a widget which is using this Store
13186          * as a Record cache should refresh its view.
13187          * @param {Store} this
13188          */
13189         datachanged : true,
13190         /**
13191          * @event metachange
13192          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13193          * @param {Store} this
13194          * @param {Object} meta The JSON metadata
13195          */
13196         metachange : true,
13197         /**
13198          * @event add
13199          * Fires when Records have been added to the Store
13200          * @param {Store} this
13201          * @param {Roo.data.Record[]} records The array of Records added
13202          * @param {Number} index The index at which the record(s) were added
13203          */
13204         add : true,
13205         /**
13206          * @event remove
13207          * Fires when a Record has been removed from the Store
13208          * @param {Store} this
13209          * @param {Roo.data.Record} record The Record that was removed
13210          * @param {Number} index The index at which the record was removed
13211          */
13212         remove : true,
13213         /**
13214          * @event update
13215          * Fires when a Record has been updated
13216          * @param {Store} this
13217          * @param {Roo.data.Record} record The Record that was updated
13218          * @param {String} operation The update operation being performed.  Value may be one of:
13219          * <pre><code>
13220  Roo.data.Record.EDIT
13221  Roo.data.Record.REJECT
13222  Roo.data.Record.COMMIT
13223          * </code></pre>
13224          */
13225         update : true,
13226         /**
13227          * @event clear
13228          * Fires when the data cache has been cleared.
13229          * @param {Store} this
13230          */
13231         clear : true,
13232         /**
13233          * @event beforeload
13234          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13235          * the load action will be canceled.
13236          * @param {Store} this
13237          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13238          */
13239         beforeload : true,
13240         /**
13241          * @event beforeloadadd
13242          * Fires after a new set of Records has been loaded.
13243          * @param {Store} this
13244          * @param {Roo.data.Record[]} records The Records that were loaded
13245          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13246          */
13247         beforeloadadd : true,
13248         /**
13249          * @event load
13250          * Fires after a new set of Records has been loaded, before they are added to the store.
13251          * @param {Store} this
13252          * @param {Roo.data.Record[]} records The Records that were loaded
13253          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13254          * @params {Object} return from reader
13255          */
13256         load : true,
13257         /**
13258          * @event loadexception
13259          * Fires if an exception occurs in the Proxy during loading.
13260          * Called with the signature of the Proxy's "loadexception" event.
13261          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13262          * 
13263          * @param {Proxy} 
13264          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13265          * @param {Object} load options 
13266          * @param {Object} jsonData from your request (normally this contains the Exception)
13267          */
13268         loadexception : true
13269     });
13270     
13271     if(this.proxy){
13272         this.proxy = Roo.factory(this.proxy, Roo.data);
13273         this.proxy.xmodule = this.xmodule || false;
13274         this.relayEvents(this.proxy,  ["loadexception"]);
13275     }
13276     this.sortToggle = {};
13277     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13278
13279     Roo.data.Store.superclass.constructor.call(this);
13280
13281     if(this.inlineData){
13282         this.loadData(this.inlineData);
13283         delete this.inlineData;
13284     }
13285 };
13286
13287 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13288      /**
13289     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13290     * without a remote query - used by combo/forms at present.
13291     */
13292     
13293     /**
13294     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13295     */
13296     /**
13297     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13298     */
13299     /**
13300     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13301     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13302     */
13303     /**
13304     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13305     * on any HTTP request
13306     */
13307     /**
13308     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13309     */
13310     /**
13311     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13312     */
13313     multiSort: false,
13314     /**
13315     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13316     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13317     */
13318     remoteSort : false,
13319
13320     /**
13321     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13322      * loaded or when a record is removed. (defaults to false).
13323     */
13324     pruneModifiedRecords : false,
13325
13326     // private
13327     lastOptions : null,
13328
13329     /**
13330      * Add Records to the Store and fires the add event.
13331      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13332      */
13333     add : function(records){
13334         records = [].concat(records);
13335         for(var i = 0, len = records.length; i < len; i++){
13336             records[i].join(this);
13337         }
13338         var index = this.data.length;
13339         this.data.addAll(records);
13340         this.fireEvent("add", this, records, index);
13341     },
13342
13343     /**
13344      * Remove a Record from the Store and fires the remove event.
13345      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13346      */
13347     remove : function(record){
13348         var index = this.data.indexOf(record);
13349         this.data.removeAt(index);
13350  
13351         if(this.pruneModifiedRecords){
13352             this.modified.remove(record);
13353         }
13354         this.fireEvent("remove", this, record, index);
13355     },
13356
13357     /**
13358      * Remove all Records from the Store and fires the clear event.
13359      */
13360     removeAll : function(){
13361         this.data.clear();
13362         if(this.pruneModifiedRecords){
13363             this.modified = [];
13364         }
13365         this.fireEvent("clear", this);
13366     },
13367
13368     /**
13369      * Inserts Records to the Store at the given index and fires the add event.
13370      * @param {Number} index The start index at which to insert the passed Records.
13371      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13372      */
13373     insert : function(index, records){
13374         records = [].concat(records);
13375         for(var i = 0, len = records.length; i < len; i++){
13376             this.data.insert(index, records[i]);
13377             records[i].join(this);
13378         }
13379         this.fireEvent("add", this, records, index);
13380     },
13381
13382     /**
13383      * Get the index within the cache of the passed Record.
13384      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13385      * @return {Number} The index of the passed Record. Returns -1 if not found.
13386      */
13387     indexOf : function(record){
13388         return this.data.indexOf(record);
13389     },
13390
13391     /**
13392      * Get the index within the cache of the Record with the passed id.
13393      * @param {String} id The id of the Record to find.
13394      * @return {Number} The index of the Record. Returns -1 if not found.
13395      */
13396     indexOfId : function(id){
13397         return this.data.indexOfKey(id);
13398     },
13399
13400     /**
13401      * Get the Record with the specified id.
13402      * @param {String} id The id of the Record to find.
13403      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13404      */
13405     getById : function(id){
13406         return this.data.key(id);
13407     },
13408
13409     /**
13410      * Get the Record at the specified index.
13411      * @param {Number} index The index of the Record to find.
13412      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13413      */
13414     getAt : function(index){
13415         return this.data.itemAt(index);
13416     },
13417
13418     /**
13419      * Returns a range of Records between specified indices.
13420      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13421      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13422      * @return {Roo.data.Record[]} An array of Records
13423      */
13424     getRange : function(start, end){
13425         return this.data.getRange(start, end);
13426     },
13427
13428     // private
13429     storeOptions : function(o){
13430         o = Roo.apply({}, o);
13431         delete o.callback;
13432         delete o.scope;
13433         this.lastOptions = o;
13434     },
13435
13436     /**
13437      * Loads the Record cache from the configured Proxy using the configured Reader.
13438      * <p>
13439      * If using remote paging, then the first load call must specify the <em>start</em>
13440      * and <em>limit</em> properties in the options.params property to establish the initial
13441      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13442      * <p>
13443      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13444      * and this call will return before the new data has been loaded. Perform any post-processing
13445      * in a callback function, or in a "load" event handler.</strong>
13446      * <p>
13447      * @param {Object} options An object containing properties which control loading options:<ul>
13448      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13449      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13450      * passed the following arguments:<ul>
13451      * <li>r : Roo.data.Record[]</li>
13452      * <li>options: Options object from the load call</li>
13453      * <li>success: Boolean success indicator</li></ul></li>
13454      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13455      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13456      * </ul>
13457      */
13458     load : function(options){
13459         options = options || {};
13460         if(this.fireEvent("beforeload", this, options) !== false){
13461             this.storeOptions(options);
13462             var p = Roo.apply(options.params || {}, this.baseParams);
13463             // if meta was not loaded from remote source.. try requesting it.
13464             if (!this.reader.metaFromRemote) {
13465                 p._requestMeta = 1;
13466             }
13467             if(this.sortInfo && this.remoteSort){
13468                 var pn = this.paramNames;
13469                 p[pn["sort"]] = this.sortInfo.field;
13470                 p[pn["dir"]] = this.sortInfo.direction;
13471             }
13472             if (this.multiSort) {
13473                 var pn = this.paramNames;
13474                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13475             }
13476             
13477             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13478         }
13479     },
13480
13481     /**
13482      * Reloads the Record cache from the configured Proxy using the configured Reader and
13483      * the options from the last load operation performed.
13484      * @param {Object} options (optional) An object containing properties which may override the options
13485      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13486      * the most recently used options are reused).
13487      */
13488     reload : function(options){
13489         this.load(Roo.applyIf(options||{}, this.lastOptions));
13490     },
13491
13492     // private
13493     // Called as a callback by the Reader during a load operation.
13494     loadRecords : function(o, options, success){
13495         if(!o || success === false){
13496             if(success !== false){
13497                 this.fireEvent("load", this, [], options, o);
13498             }
13499             if(options.callback){
13500                 options.callback.call(options.scope || this, [], options, false);
13501             }
13502             return;
13503         }
13504         // if data returned failure - throw an exception.
13505         if (o.success === false) {
13506             // show a message if no listener is registered.
13507             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13508                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13509             }
13510             // loadmask wil be hooked into this..
13511             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13512             return;
13513         }
13514         var r = o.records, t = o.totalRecords || r.length;
13515         
13516         this.fireEvent("beforeloadadd", this, r, options, o);
13517         
13518         if(!options || options.add !== true){
13519             if(this.pruneModifiedRecords){
13520                 this.modified = [];
13521             }
13522             for(var i = 0, len = r.length; i < len; i++){
13523                 r[i].join(this);
13524             }
13525             if(this.snapshot){
13526                 this.data = this.snapshot;
13527                 delete this.snapshot;
13528             }
13529             this.data.clear();
13530             this.data.addAll(r);
13531             this.totalLength = t;
13532             this.applySort();
13533             this.fireEvent("datachanged", this);
13534         }else{
13535             this.totalLength = Math.max(t, this.data.length+r.length);
13536             this.add(r);
13537         }
13538         
13539         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13540                 
13541             var e = new Roo.data.Record({});
13542
13543             e.set(this.parent.displayField, this.parent.emptyTitle);
13544             e.set(this.parent.valueField, '');
13545
13546             this.insert(0, e);
13547         }
13548             
13549         this.fireEvent("load", this, r, options, o);
13550         if(options.callback){
13551             options.callback.call(options.scope || this, r, options, true);
13552         }
13553     },
13554
13555
13556     /**
13557      * Loads data from a passed data block. A Reader which understands the format of the data
13558      * must have been configured in the constructor.
13559      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13560      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13561      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13562      */
13563     loadData : function(o, append){
13564         var r = this.reader.readRecords(o);
13565         this.loadRecords(r, {add: append}, true);
13566     },
13567     
13568      /**
13569      * using 'cn' the nested child reader read the child array into it's child stores.
13570      * @param {Object} rec The record with a 'children array
13571      */
13572     loadDataFromChildren : function(rec)
13573     {
13574         this.loadData(this.reader.toLoadData(rec));
13575     },
13576     
13577
13578     /**
13579      * Gets the number of cached records.
13580      * <p>
13581      * <em>If using paging, this may not be the total size of the dataset. If the data object
13582      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13583      * the data set size</em>
13584      */
13585     getCount : function(){
13586         return this.data.length || 0;
13587     },
13588
13589     /**
13590      * Gets the total number of records in the dataset as returned by the server.
13591      * <p>
13592      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13593      * the dataset size</em>
13594      */
13595     getTotalCount : function(){
13596         return this.totalLength || 0;
13597     },
13598
13599     /**
13600      * Returns the sort state of the Store as an object with two properties:
13601      * <pre><code>
13602  field {String} The name of the field by which the Records are sorted
13603  direction {String} The sort order, "ASC" or "DESC"
13604      * </code></pre>
13605      */
13606     getSortState : function(){
13607         return this.sortInfo;
13608     },
13609
13610     // private
13611     applySort : function(){
13612         if(this.sortInfo && !this.remoteSort){
13613             var s = this.sortInfo, f = s.field;
13614             var st = this.fields.get(f).sortType;
13615             var fn = function(r1, r2){
13616                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13617                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13618             };
13619             this.data.sort(s.direction, fn);
13620             if(this.snapshot && this.snapshot != this.data){
13621                 this.snapshot.sort(s.direction, fn);
13622             }
13623         }
13624     },
13625
13626     /**
13627      * Sets the default sort column and order to be used by the next load operation.
13628      * @param {String} fieldName The name of the field to sort by.
13629      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13630      */
13631     setDefaultSort : function(field, dir){
13632         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13633     },
13634
13635     /**
13636      * Sort the Records.
13637      * If remote sorting is used, the sort is performed on the server, and the cache is
13638      * reloaded. If local sorting is used, the cache is sorted internally.
13639      * @param {String} fieldName The name of the field to sort by.
13640      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13641      */
13642     sort : function(fieldName, dir){
13643         var f = this.fields.get(fieldName);
13644         if(!dir){
13645             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13646             
13647             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13648                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13649             }else{
13650                 dir = f.sortDir;
13651             }
13652         }
13653         this.sortToggle[f.name] = dir;
13654         this.sortInfo = {field: f.name, direction: dir};
13655         if(!this.remoteSort){
13656             this.applySort();
13657             this.fireEvent("datachanged", this);
13658         }else{
13659             this.load(this.lastOptions);
13660         }
13661     },
13662
13663     /**
13664      * Calls the specified function for each of the Records in the cache.
13665      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13666      * Returning <em>false</em> aborts and exits the iteration.
13667      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13668      */
13669     each : function(fn, scope){
13670         this.data.each(fn, scope);
13671     },
13672
13673     /**
13674      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13675      * (e.g., during paging).
13676      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13677      */
13678     getModifiedRecords : function(){
13679         return this.modified;
13680     },
13681
13682     // private
13683     createFilterFn : function(property, value, anyMatch){
13684         if(!value.exec){ // not a regex
13685             value = String(value);
13686             if(value.length == 0){
13687                 return false;
13688             }
13689             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13690         }
13691         return function(r){
13692             return value.test(r.data[property]);
13693         };
13694     },
13695
13696     /**
13697      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13698      * @param {String} property A field on your records
13699      * @param {Number} start The record index to start at (defaults to 0)
13700      * @param {Number} end The last record index to include (defaults to length - 1)
13701      * @return {Number} The sum
13702      */
13703     sum : function(property, start, end){
13704         var rs = this.data.items, v = 0;
13705         start = start || 0;
13706         end = (end || end === 0) ? end : rs.length-1;
13707
13708         for(var i = start; i <= end; i++){
13709             v += (rs[i].data[property] || 0);
13710         }
13711         return v;
13712     },
13713
13714     /**
13715      * Filter the records by a specified property.
13716      * @param {String} field A field on your records
13717      * @param {String/RegExp} value Either a string that the field
13718      * should start with or a RegExp to test against the field
13719      * @param {Boolean} anyMatch True to match any part not just the beginning
13720      */
13721     filter : function(property, value, anyMatch){
13722         var fn = this.createFilterFn(property, value, anyMatch);
13723         return fn ? this.filterBy(fn) : this.clearFilter();
13724     },
13725
13726     /**
13727      * Filter by a function. The specified function will be called with each
13728      * record in this data source. If the function returns true the record is included,
13729      * otherwise it is filtered.
13730      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13731      * @param {Object} scope (optional) The scope of the function (defaults to this)
13732      */
13733     filterBy : function(fn, scope){
13734         this.snapshot = this.snapshot || this.data;
13735         this.data = this.queryBy(fn, scope||this);
13736         this.fireEvent("datachanged", this);
13737     },
13738
13739     /**
13740      * Query the records by a specified property.
13741      * @param {String} field A field on your records
13742      * @param {String/RegExp} value Either a string that the field
13743      * should start with or a RegExp to test against the field
13744      * @param {Boolean} anyMatch True to match any part not just the beginning
13745      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13746      */
13747     query : function(property, value, anyMatch){
13748         var fn = this.createFilterFn(property, value, anyMatch);
13749         return fn ? this.queryBy(fn) : this.data.clone();
13750     },
13751
13752     /**
13753      * Query by a function. The specified function will be called with each
13754      * record in this data source. If the function returns true the record is included
13755      * in the results.
13756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13757      * @param {Object} scope (optional) The scope of the function (defaults to this)
13758       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13759      **/
13760     queryBy : function(fn, scope){
13761         var data = this.snapshot || this.data;
13762         return data.filterBy(fn, scope||this);
13763     },
13764
13765     /**
13766      * Collects unique values for a particular dataIndex from this store.
13767      * @param {String} dataIndex The property to collect
13768      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13769      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13770      * @return {Array} An array of the unique values
13771      **/
13772     collect : function(dataIndex, allowNull, bypassFilter){
13773         var d = (bypassFilter === true && this.snapshot) ?
13774                 this.snapshot.items : this.data.items;
13775         var v, sv, r = [], l = {};
13776         for(var i = 0, len = d.length; i < len; i++){
13777             v = d[i].data[dataIndex];
13778             sv = String(v);
13779             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13780                 l[sv] = true;
13781                 r[r.length] = v;
13782             }
13783         }
13784         return r;
13785     },
13786
13787     /**
13788      * Revert to a view of the Record cache with no filtering applied.
13789      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13790      */
13791     clearFilter : function(suppressEvent){
13792         if(this.snapshot && this.snapshot != this.data){
13793             this.data = this.snapshot;
13794             delete this.snapshot;
13795             if(suppressEvent !== true){
13796                 this.fireEvent("datachanged", this);
13797             }
13798         }
13799     },
13800
13801     // private
13802     afterEdit : function(record){
13803         if(this.modified.indexOf(record) == -1){
13804             this.modified.push(record);
13805         }
13806         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13807     },
13808     
13809     // private
13810     afterReject : function(record){
13811         this.modified.remove(record);
13812         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13813     },
13814
13815     // private
13816     afterCommit : function(record){
13817         this.modified.remove(record);
13818         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13819     },
13820
13821     /**
13822      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13823      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13824      */
13825     commitChanges : function(){
13826         var m = this.modified.slice(0);
13827         this.modified = [];
13828         for(var i = 0, len = m.length; i < len; i++){
13829             m[i].commit();
13830         }
13831     },
13832
13833     /**
13834      * Cancel outstanding changes on all changed records.
13835      */
13836     rejectChanges : function(){
13837         var m = this.modified.slice(0);
13838         this.modified = [];
13839         for(var i = 0, len = m.length; i < len; i++){
13840             m[i].reject();
13841         }
13842     },
13843
13844     onMetaChange : function(meta, rtype, o){
13845         this.recordType = rtype;
13846         this.fields = rtype.prototype.fields;
13847         delete this.snapshot;
13848         this.sortInfo = meta.sortInfo || this.sortInfo;
13849         this.modified = [];
13850         this.fireEvent('metachange', this, this.reader.meta);
13851     },
13852     
13853     moveIndex : function(data, type)
13854     {
13855         var index = this.indexOf(data);
13856         
13857         var newIndex = index + type;
13858         
13859         this.remove(data);
13860         
13861         this.insert(newIndex, data);
13862         
13863     }
13864 });/*
13865  * Based on:
13866  * Ext JS Library 1.1.1
13867  * Copyright(c) 2006-2007, Ext JS, LLC.
13868  *
13869  * Originally Released Under LGPL - original licence link has changed is not relivant.
13870  *
13871  * Fork - LGPL
13872  * <script type="text/javascript">
13873  */
13874
13875 /**
13876  * @class Roo.data.SimpleStore
13877  * @extends Roo.data.Store
13878  * Small helper class to make creating Stores from Array data easier.
13879  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13880  * @cfg {Array} fields An array of field definition objects, or field name strings.
13881  * @cfg {Object} an existing reader (eg. copied from another store)
13882  * @cfg {Array} data The multi-dimensional array of data
13883  * @constructor
13884  * @param {Object} config
13885  */
13886 Roo.data.SimpleStore = function(config)
13887 {
13888     Roo.data.SimpleStore.superclass.constructor.call(this, {
13889         isLocal : true,
13890         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13891                 id: config.id
13892             },
13893             Roo.data.Record.create(config.fields)
13894         ),
13895         proxy : new Roo.data.MemoryProxy(config.data)
13896     });
13897     this.load();
13898 };
13899 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13900  * Based on:
13901  * Ext JS Library 1.1.1
13902  * Copyright(c) 2006-2007, Ext JS, LLC.
13903  *
13904  * Originally Released Under LGPL - original licence link has changed is not relivant.
13905  *
13906  * Fork - LGPL
13907  * <script type="text/javascript">
13908  */
13909
13910 /**
13911 /**
13912  * @extends Roo.data.Store
13913  * @class Roo.data.JsonStore
13914  * Small helper class to make creating Stores for JSON data easier. <br/>
13915 <pre><code>
13916 var store = new Roo.data.JsonStore({
13917     url: 'get-images.php',
13918     root: 'images',
13919     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13920 });
13921 </code></pre>
13922  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13923  * JsonReader and HttpProxy (unless inline data is provided).</b>
13924  * @cfg {Array} fields An array of field definition objects, or field name strings.
13925  * @constructor
13926  * @param {Object} config
13927  */
13928 Roo.data.JsonStore = function(c){
13929     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13930         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13931         reader: new Roo.data.JsonReader(c, c.fields)
13932     }));
13933 };
13934 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945  
13946 Roo.data.Field = function(config){
13947     if(typeof config == "string"){
13948         config = {name: config};
13949     }
13950     Roo.apply(this, config);
13951     
13952     if(!this.type){
13953         this.type = "auto";
13954     }
13955     
13956     var st = Roo.data.SortTypes;
13957     // named sortTypes are supported, here we look them up
13958     if(typeof this.sortType == "string"){
13959         this.sortType = st[this.sortType];
13960     }
13961     
13962     // set default sortType for strings and dates
13963     if(!this.sortType){
13964         switch(this.type){
13965             case "string":
13966                 this.sortType = st.asUCString;
13967                 break;
13968             case "date":
13969                 this.sortType = st.asDate;
13970                 break;
13971             default:
13972                 this.sortType = st.none;
13973         }
13974     }
13975
13976     // define once
13977     var stripRe = /[\$,%]/g;
13978
13979     // prebuilt conversion function for this field, instead of
13980     // switching every time we're reading a value
13981     if(!this.convert){
13982         var cv, dateFormat = this.dateFormat;
13983         switch(this.type){
13984             case "":
13985             case "auto":
13986             case undefined:
13987                 cv = function(v){ return v; };
13988                 break;
13989             case "string":
13990                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13991                 break;
13992             case "int":
13993                 cv = function(v){
13994                     return v !== undefined && v !== null && v !== '' ?
13995                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13996                     };
13997                 break;
13998             case "float":
13999                 cv = function(v){
14000                     return v !== undefined && v !== null && v !== '' ?
14001                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14002                     };
14003                 break;
14004             case "bool":
14005             case "boolean":
14006                 cv = function(v){ return v === true || v === "true" || v == 1; };
14007                 break;
14008             case "date":
14009                 cv = function(v){
14010                     if(!v){
14011                         return '';
14012                     }
14013                     if(v instanceof Date){
14014                         return v;
14015                     }
14016                     if(dateFormat){
14017                         if(dateFormat == "timestamp"){
14018                             return new Date(v*1000);
14019                         }
14020                         return Date.parseDate(v, dateFormat);
14021                     }
14022                     var parsed = Date.parse(v);
14023                     return parsed ? new Date(parsed) : null;
14024                 };
14025              break;
14026             
14027         }
14028         this.convert = cv;
14029     }
14030 };
14031
14032 Roo.data.Field.prototype = {
14033     dateFormat: null,
14034     defaultValue: "",
14035     mapping: null,
14036     sortType : null,
14037     sortDir : "ASC"
14038 };/*
14039  * Based on:
14040  * Ext JS Library 1.1.1
14041  * Copyright(c) 2006-2007, Ext JS, LLC.
14042  *
14043  * Originally Released Under LGPL - original licence link has changed is not relivant.
14044  *
14045  * Fork - LGPL
14046  * <script type="text/javascript">
14047  */
14048  
14049 // Base class for reading structured data from a data source.  This class is intended to be
14050 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14051
14052 /**
14053  * @class Roo.data.DataReader
14054  * Base class for reading structured data from a data source.  This class is intended to be
14055  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14056  */
14057
14058 Roo.data.DataReader = function(meta, recordType){
14059     
14060     this.meta = meta;
14061     
14062     this.recordType = recordType instanceof Array ? 
14063         Roo.data.Record.create(recordType) : recordType;
14064 };
14065
14066 Roo.data.DataReader.prototype = {
14067     
14068     
14069     readerType : 'Data',
14070      /**
14071      * Create an empty record
14072      * @param {Object} data (optional) - overlay some values
14073      * @return {Roo.data.Record} record created.
14074      */
14075     newRow :  function(d) {
14076         var da =  {};
14077         this.recordType.prototype.fields.each(function(c) {
14078             switch( c.type) {
14079                 case 'int' : da[c.name] = 0; break;
14080                 case 'date' : da[c.name] = new Date(); break;
14081                 case 'float' : da[c.name] = 0.0; break;
14082                 case 'boolean' : da[c.name] = false; break;
14083                 default : da[c.name] = ""; break;
14084             }
14085             
14086         });
14087         return new this.recordType(Roo.apply(da, d));
14088     }
14089     
14090     
14091 };/*
14092  * Based on:
14093  * Ext JS Library 1.1.1
14094  * Copyright(c) 2006-2007, Ext JS, LLC.
14095  *
14096  * Originally Released Under LGPL - original licence link has changed is not relivant.
14097  *
14098  * Fork - LGPL
14099  * <script type="text/javascript">
14100  */
14101
14102 /**
14103  * @class Roo.data.DataProxy
14104  * @extends Roo.data.Observable
14105  * This class is an abstract base class for implementations which provide retrieval of
14106  * unformatted data objects.<br>
14107  * <p>
14108  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14109  * (of the appropriate type which knows how to parse the data object) to provide a block of
14110  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14111  * <p>
14112  * Custom implementations must implement the load method as described in
14113  * {@link Roo.data.HttpProxy#load}.
14114  */
14115 Roo.data.DataProxy = function(){
14116     this.addEvents({
14117         /**
14118          * @event beforeload
14119          * Fires before a network request is made to retrieve a data object.
14120          * @param {Object} This DataProxy object.
14121          * @param {Object} params The params parameter to the load function.
14122          */
14123         beforeload : true,
14124         /**
14125          * @event load
14126          * Fires before the load method's callback is called.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} o The data object.
14129          * @param {Object} arg The callback argument object passed to the load function.
14130          */
14131         load : true,
14132         /**
14133          * @event loadexception
14134          * Fires if an Exception occurs during data retrieval.
14135          * @param {Object} This DataProxy object.
14136          * @param {Object} o The data object.
14137          * @param {Object} arg The callback argument object passed to the load function.
14138          * @param {Object} e The Exception.
14139          */
14140         loadexception : true
14141     });
14142     Roo.data.DataProxy.superclass.constructor.call(this);
14143 };
14144
14145 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14146
14147     /**
14148      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14149      */
14150 /*
14151  * Based on:
14152  * Ext JS Library 1.1.1
14153  * Copyright(c) 2006-2007, Ext JS, LLC.
14154  *
14155  * Originally Released Under LGPL - original licence link has changed is not relivant.
14156  *
14157  * Fork - LGPL
14158  * <script type="text/javascript">
14159  */
14160 /**
14161  * @class Roo.data.MemoryProxy
14162  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14163  * to the Reader when its load method is called.
14164  * @constructor
14165  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14166  */
14167 Roo.data.MemoryProxy = function(data){
14168     if (data.data) {
14169         data = data.data;
14170     }
14171     Roo.data.MemoryProxy.superclass.constructor.call(this);
14172     this.data = data;
14173 };
14174
14175 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14176     
14177     /**
14178      * Load data from the requested source (in this case an in-memory
14179      * data object passed to the constructor), read the data object into
14180      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14181      * process that block using the passed callback.
14182      * @param {Object} params This parameter is not used by the MemoryProxy class.
14183      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14184      * object into a block of Roo.data.Records.
14185      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14186      * The function must be passed <ul>
14187      * <li>The Record block object</li>
14188      * <li>The "arg" argument from the load function</li>
14189      * <li>A boolean success indicator</li>
14190      * </ul>
14191      * @param {Object} scope The scope in which to call the callback
14192      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14193      */
14194     load : function(params, reader, callback, scope, arg){
14195         params = params || {};
14196         var result;
14197         try {
14198             result = reader.readRecords(params.data ? params.data :this.data);
14199         }catch(e){
14200             this.fireEvent("loadexception", this, arg, null, e);
14201             callback.call(scope, null, arg, false);
14202             return;
14203         }
14204         callback.call(scope, result, arg, true);
14205     },
14206     
14207     // private
14208     update : function(params, records){
14209         
14210     }
14211 });/*
14212  * Based on:
14213  * Ext JS Library 1.1.1
14214  * Copyright(c) 2006-2007, Ext JS, LLC.
14215  *
14216  * Originally Released Under LGPL - original licence link has changed is not relivant.
14217  *
14218  * Fork - LGPL
14219  * <script type="text/javascript">
14220  */
14221 /**
14222  * @class Roo.data.HttpProxy
14223  * @extends Roo.data.DataProxy
14224  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14225  * configured to reference a certain URL.<br><br>
14226  * <p>
14227  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14228  * from which the running page was served.<br><br>
14229  * <p>
14230  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14231  * <p>
14232  * Be aware that to enable the browser to parse an XML document, the server must set
14233  * the Content-Type header in the HTTP response to "text/xml".
14234  * @constructor
14235  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14236  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14237  * will be used to make the request.
14238  */
14239 Roo.data.HttpProxy = function(conn){
14240     Roo.data.HttpProxy.superclass.constructor.call(this);
14241     // is conn a conn config or a real conn?
14242     this.conn = conn;
14243     this.useAjax = !conn || !conn.events;
14244   
14245 };
14246
14247 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14248     // thse are take from connection...
14249     
14250     /**
14251      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14252      */
14253     /**
14254      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14255      * extra parameters to each request made by this object. (defaults to undefined)
14256      */
14257     /**
14258      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14259      *  to each request made by this object. (defaults to undefined)
14260      */
14261     /**
14262      * @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)
14263      */
14264     /**
14265      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14266      */
14267      /**
14268      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14269      * @type Boolean
14270      */
14271   
14272
14273     /**
14274      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14275      * @type Boolean
14276      */
14277     /**
14278      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14279      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14280      * a finer-grained basis than the DataProxy events.
14281      */
14282     getConnection : function(){
14283         return this.useAjax ? Roo.Ajax : this.conn;
14284     },
14285
14286     /**
14287      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14288      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14289      * process that block using the passed callback.
14290      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14291      * for the request to the remote server.
14292      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14293      * object into a block of Roo.data.Records.
14294      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14295      * The function must be passed <ul>
14296      * <li>The Record block object</li>
14297      * <li>The "arg" argument from the load function</li>
14298      * <li>A boolean success indicator</li>
14299      * </ul>
14300      * @param {Object} scope The scope in which to call the callback
14301      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14302      */
14303     load : function(params, reader, callback, scope, arg){
14304         if(this.fireEvent("beforeload", this, params) !== false){
14305             var  o = {
14306                 params : params || {},
14307                 request: {
14308                     callback : callback,
14309                     scope : scope,
14310                     arg : arg
14311                 },
14312                 reader: reader,
14313                 callback : this.loadResponse,
14314                 scope: this
14315             };
14316             if(this.useAjax){
14317                 Roo.applyIf(o, this.conn);
14318                 if(this.activeRequest){
14319                     Roo.Ajax.abort(this.activeRequest);
14320                 }
14321                 this.activeRequest = Roo.Ajax.request(o);
14322             }else{
14323                 this.conn.request(o);
14324             }
14325         }else{
14326             callback.call(scope||this, null, arg, false);
14327         }
14328     },
14329
14330     // private
14331     loadResponse : function(o, success, response){
14332         delete this.activeRequest;
14333         if(!success){
14334             this.fireEvent("loadexception", this, o, response);
14335             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14336             return;
14337         }
14338         var result;
14339         try {
14340             result = o.reader.read(response);
14341         }catch(e){
14342             this.fireEvent("loadexception", this, o, response, e);
14343             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14344             return;
14345         }
14346         
14347         this.fireEvent("load", this, o, o.request.arg);
14348         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14349     },
14350
14351     // private
14352     update : function(dataSet){
14353
14354     },
14355
14356     // private
14357     updateResponse : function(dataSet){
14358
14359     }
14360 });/*
14361  * Based on:
14362  * Ext JS Library 1.1.1
14363  * Copyright(c) 2006-2007, Ext JS, LLC.
14364  *
14365  * Originally Released Under LGPL - original licence link has changed is not relivant.
14366  *
14367  * Fork - LGPL
14368  * <script type="text/javascript">
14369  */
14370
14371 /**
14372  * @class Roo.data.ScriptTagProxy
14373  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14374  * other than the originating domain of the running page.<br><br>
14375  * <p>
14376  * <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
14377  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14378  * <p>
14379  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14380  * source code that is used as the source inside a &lt;script> tag.<br><br>
14381  * <p>
14382  * In order for the browser to process the returned data, the server must wrap the data object
14383  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14384  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14385  * depending on whether the callback name was passed:
14386  * <p>
14387  * <pre><code>
14388 boolean scriptTag = false;
14389 String cb = request.getParameter("callback");
14390 if (cb != null) {
14391     scriptTag = true;
14392     response.setContentType("text/javascript");
14393 } else {
14394     response.setContentType("application/x-json");
14395 }
14396 Writer out = response.getWriter();
14397 if (scriptTag) {
14398     out.write(cb + "(");
14399 }
14400 out.print(dataBlock.toJsonString());
14401 if (scriptTag) {
14402     out.write(");");
14403 }
14404 </pre></code>
14405  *
14406  * @constructor
14407  * @param {Object} config A configuration object.
14408  */
14409 Roo.data.ScriptTagProxy = function(config){
14410     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14411     Roo.apply(this, config);
14412     this.head = document.getElementsByTagName("head")[0];
14413 };
14414
14415 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14416
14417 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14418     /**
14419      * @cfg {String} url The URL from which to request the data object.
14420      */
14421     /**
14422      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14423      */
14424     timeout : 30000,
14425     /**
14426      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14427      * the server the name of the callback function set up by the load call to process the returned data object.
14428      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14429      * javascript output which calls this named function passing the data object as its only parameter.
14430      */
14431     callbackParam : "callback",
14432     /**
14433      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14434      * name to the request.
14435      */
14436     nocache : true,
14437
14438     /**
14439      * Load data from the configured URL, read the data object into
14440      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14441      * process that block using the passed callback.
14442      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14443      * for the request to the remote server.
14444      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14445      * object into a block of Roo.data.Records.
14446      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14447      * The function must be passed <ul>
14448      * <li>The Record block object</li>
14449      * <li>The "arg" argument from the load function</li>
14450      * <li>A boolean success indicator</li>
14451      * </ul>
14452      * @param {Object} scope The scope in which to call the callback
14453      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14454      */
14455     load : function(params, reader, callback, scope, arg){
14456         if(this.fireEvent("beforeload", this, params) !== false){
14457
14458             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14459
14460             var url = this.url;
14461             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14462             if(this.nocache){
14463                 url += "&_dc=" + (new Date().getTime());
14464             }
14465             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14466             var trans = {
14467                 id : transId,
14468                 cb : "stcCallback"+transId,
14469                 scriptId : "stcScript"+transId,
14470                 params : params,
14471                 arg : arg,
14472                 url : url,
14473                 callback : callback,
14474                 scope : scope,
14475                 reader : reader
14476             };
14477             var conn = this;
14478
14479             window[trans.cb] = function(o){
14480                 conn.handleResponse(o, trans);
14481             };
14482
14483             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14484
14485             if(this.autoAbort !== false){
14486                 this.abort();
14487             }
14488
14489             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14490
14491             var script = document.createElement("script");
14492             script.setAttribute("src", url);
14493             script.setAttribute("type", "text/javascript");
14494             script.setAttribute("id", trans.scriptId);
14495             this.head.appendChild(script);
14496
14497             this.trans = trans;
14498         }else{
14499             callback.call(scope||this, null, arg, false);
14500         }
14501     },
14502
14503     // private
14504     isLoading : function(){
14505         return this.trans ? true : false;
14506     },
14507
14508     /**
14509      * Abort the current server request.
14510      */
14511     abort : function(){
14512         if(this.isLoading()){
14513             this.destroyTrans(this.trans);
14514         }
14515     },
14516
14517     // private
14518     destroyTrans : function(trans, isLoaded){
14519         this.head.removeChild(document.getElementById(trans.scriptId));
14520         clearTimeout(trans.timeoutId);
14521         if(isLoaded){
14522             window[trans.cb] = undefined;
14523             try{
14524                 delete window[trans.cb];
14525             }catch(e){}
14526         }else{
14527             // if hasn't been loaded, wait for load to remove it to prevent script error
14528             window[trans.cb] = function(){
14529                 window[trans.cb] = undefined;
14530                 try{
14531                     delete window[trans.cb];
14532                 }catch(e){}
14533             };
14534         }
14535     },
14536
14537     // private
14538     handleResponse : function(o, trans){
14539         this.trans = false;
14540         this.destroyTrans(trans, true);
14541         var result;
14542         try {
14543             result = trans.reader.readRecords(o);
14544         }catch(e){
14545             this.fireEvent("loadexception", this, o, trans.arg, e);
14546             trans.callback.call(trans.scope||window, null, trans.arg, false);
14547             return;
14548         }
14549         this.fireEvent("load", this, o, trans.arg);
14550         trans.callback.call(trans.scope||window, result, trans.arg, true);
14551     },
14552
14553     // private
14554     handleFailure : function(trans){
14555         this.trans = false;
14556         this.destroyTrans(trans, false);
14557         this.fireEvent("loadexception", this, null, trans.arg);
14558         trans.callback.call(trans.scope||window, null, trans.arg, false);
14559     }
14560 });/*
14561  * Based on:
14562  * Ext JS Library 1.1.1
14563  * Copyright(c) 2006-2007, Ext JS, LLC.
14564  *
14565  * Originally Released Under LGPL - original licence link has changed is not relivant.
14566  *
14567  * Fork - LGPL
14568  * <script type="text/javascript">
14569  */
14570
14571 /**
14572  * @class Roo.data.JsonReader
14573  * @extends Roo.data.DataReader
14574  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14575  * based on mappings in a provided Roo.data.Record constructor.
14576  * 
14577  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14578  * in the reply previously. 
14579  * 
14580  * <p>
14581  * Example code:
14582  * <pre><code>
14583 var RecordDef = Roo.data.Record.create([
14584     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14585     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14586 ]);
14587 var myReader = new Roo.data.JsonReader({
14588     totalProperty: "results",    // The property which contains the total dataset size (optional)
14589     root: "rows",                // The property which contains an Array of row objects
14590     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14591 }, RecordDef);
14592 </code></pre>
14593  * <p>
14594  * This would consume a JSON file like this:
14595  * <pre><code>
14596 { 'results': 2, 'rows': [
14597     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14598     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14599 }
14600 </code></pre>
14601  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14602  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14603  * paged from the remote server.
14604  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14605  * @cfg {String} root name of the property which contains the Array of row objects.
14606  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14607  * @cfg {Array} fields Array of field definition objects
14608  * @constructor
14609  * Create a new JsonReader
14610  * @param {Object} meta Metadata configuration options
14611  * @param {Object} recordType Either an Array of field definition objects,
14612  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14613  */
14614 Roo.data.JsonReader = function(meta, recordType){
14615     
14616     meta = meta || {};
14617     // set some defaults:
14618     Roo.applyIf(meta, {
14619         totalProperty: 'total',
14620         successProperty : 'success',
14621         root : 'data',
14622         id : 'id'
14623     });
14624     
14625     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14626 };
14627 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14628     
14629     readerType : 'Json',
14630     
14631     /**
14632      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14633      * Used by Store query builder to append _requestMeta to params.
14634      * 
14635      */
14636     metaFromRemote : false,
14637     /**
14638      * This method is only used by a DataProxy which has retrieved data from a remote server.
14639      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14640      * @return {Object} data A data block which is used by an Roo.data.Store object as
14641      * a cache of Roo.data.Records.
14642      */
14643     read : function(response){
14644         var json = response.responseText;
14645        
14646         var o = /* eval:var:o */ eval("("+json+")");
14647         if(!o) {
14648             throw {message: "JsonReader.read: Json object not found"};
14649         }
14650         
14651         if(o.metaData){
14652             
14653             delete this.ef;
14654             this.metaFromRemote = true;
14655             this.meta = o.metaData;
14656             this.recordType = Roo.data.Record.create(o.metaData.fields);
14657             this.onMetaChange(this.meta, this.recordType, o);
14658         }
14659         return this.readRecords(o);
14660     },
14661
14662     // private function a store will implement
14663     onMetaChange : function(meta, recordType, o){
14664
14665     },
14666
14667     /**
14668          * @ignore
14669          */
14670     simpleAccess: function(obj, subsc) {
14671         return obj[subsc];
14672     },
14673
14674         /**
14675          * @ignore
14676          */
14677     getJsonAccessor: function(){
14678         var re = /[\[\.]/;
14679         return function(expr) {
14680             try {
14681                 return(re.test(expr))
14682                     ? new Function("obj", "return obj." + expr)
14683                     : function(obj){
14684                         return obj[expr];
14685                     };
14686             } catch(e){}
14687             return Roo.emptyFn;
14688         };
14689     }(),
14690
14691     /**
14692      * Create a data block containing Roo.data.Records from an XML document.
14693      * @param {Object} o An object which contains an Array of row objects in the property specified
14694      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14695      * which contains the total size of the dataset.
14696      * @return {Object} data A data block which is used by an Roo.data.Store object as
14697      * a cache of Roo.data.Records.
14698      */
14699     readRecords : function(o){
14700         /**
14701          * After any data loads, the raw JSON data is available for further custom processing.
14702          * @type Object
14703          */
14704         this.o = o;
14705         var s = this.meta, Record = this.recordType,
14706             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14707
14708 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14709         if (!this.ef) {
14710             if(s.totalProperty) {
14711                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14712                 }
14713                 if(s.successProperty) {
14714                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14715                 }
14716                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14717                 if (s.id) {
14718                         var g = this.getJsonAccessor(s.id);
14719                         this.getId = function(rec) {
14720                                 var r = g(rec);  
14721                                 return (r === undefined || r === "") ? null : r;
14722                         };
14723                 } else {
14724                         this.getId = function(){return null;};
14725                 }
14726             this.ef = [];
14727             for(var jj = 0; jj < fl; jj++){
14728                 f = fi[jj];
14729                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14730                 this.ef[jj] = this.getJsonAccessor(map);
14731             }
14732         }
14733
14734         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14735         if(s.totalProperty){
14736             var vt = parseInt(this.getTotal(o), 10);
14737             if(!isNaN(vt)){
14738                 totalRecords = vt;
14739             }
14740         }
14741         if(s.successProperty){
14742             var vs = this.getSuccess(o);
14743             if(vs === false || vs === 'false'){
14744                 success = false;
14745             }
14746         }
14747         var records = [];
14748         for(var i = 0; i < c; i++){
14749                 var n = root[i];
14750             var values = {};
14751             var id = this.getId(n);
14752             for(var j = 0; j < fl; j++){
14753                 f = fi[j];
14754             var v = this.ef[j](n);
14755             if (!f.convert) {
14756                 Roo.log('missing convert for ' + f.name);
14757                 Roo.log(f);
14758                 continue;
14759             }
14760             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14761             }
14762             var record = new Record(values, id);
14763             record.json = n;
14764             records[i] = record;
14765         }
14766         return {
14767             raw : o,
14768             success : success,
14769             records : records,
14770             totalRecords : totalRecords
14771         };
14772     },
14773     // used when loading children.. @see loadDataFromChildren
14774     toLoadData: function(rec)
14775     {
14776         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14777         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14778         return { data : data, total : data.length };
14779         
14780     }
14781 });/*
14782  * Based on:
14783  * Ext JS Library 1.1.1
14784  * Copyright(c) 2006-2007, Ext JS, LLC.
14785  *
14786  * Originally Released Under LGPL - original licence link has changed is not relivant.
14787  *
14788  * Fork - LGPL
14789  * <script type="text/javascript">
14790  */
14791
14792 /**
14793  * @class Roo.data.ArrayReader
14794  * @extends Roo.data.DataReader
14795  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14796  * Each element of that Array represents a row of data fields. The
14797  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14798  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14799  * <p>
14800  * Example code:.
14801  * <pre><code>
14802 var RecordDef = Roo.data.Record.create([
14803     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14804     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14805 ]);
14806 var myReader = new Roo.data.ArrayReader({
14807     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14808 }, RecordDef);
14809 </code></pre>
14810  * <p>
14811  * This would consume an Array like this:
14812  * <pre><code>
14813 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14814   </code></pre>
14815  
14816  * @constructor
14817  * Create a new JsonReader
14818  * @param {Object} meta Metadata configuration options.
14819  * @param {Object|Array} recordType Either an Array of field definition objects
14820  * 
14821  * @cfg {Array} fields Array of field definition objects
14822  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14823  * as specified to {@link Roo.data.Record#create},
14824  * or an {@link Roo.data.Record} object
14825  *
14826  * 
14827  * created using {@link Roo.data.Record#create}.
14828  */
14829 Roo.data.ArrayReader = function(meta, recordType)
14830 {    
14831     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14832 };
14833
14834 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14835     
14836       /**
14837      * Create a data block containing Roo.data.Records from an XML document.
14838      * @param {Object} o An Array of row objects which represents the dataset.
14839      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14840      * a cache of Roo.data.Records.
14841      */
14842     readRecords : function(o)
14843     {
14844         var sid = this.meta ? this.meta.id : null;
14845         var recordType = this.recordType, fields = recordType.prototype.fields;
14846         var records = [];
14847         var root = o;
14848         for(var i = 0; i < root.length; i++){
14849                 var n = root[i];
14850             var values = {};
14851             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14852             for(var j = 0, jlen = fields.length; j < jlen; j++){
14853                 var f = fields.items[j];
14854                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14855                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14856                 v = f.convert(v);
14857                 values[f.name] = v;
14858             }
14859             var record = new recordType(values, id);
14860             record.json = n;
14861             records[records.length] = record;
14862         }
14863         return {
14864             records : records,
14865             totalRecords : records.length
14866         };
14867     },
14868     // used when loading children.. @see loadDataFromChildren
14869     toLoadData: function(rec)
14870     {
14871         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14872         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14873         
14874     }
14875     
14876     
14877 });/*
14878  * - LGPL
14879  * * 
14880  */
14881
14882 /**
14883  * @class Roo.bootstrap.ComboBox
14884  * @extends Roo.bootstrap.TriggerField
14885  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14886  * @cfg {Boolean} append (true|false) default false
14887  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14888  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14889  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14890  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14891  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14892  * @cfg {Boolean} animate default true
14893  * @cfg {Boolean} emptyResultText only for touch device
14894  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14895  * @cfg {String} emptyTitle default ''
14896  * @cfg {Number} width fixed with? experimental
14897  * @constructor
14898  * Create a new ComboBox.
14899  * @param {Object} config Configuration options
14900  */
14901 Roo.bootstrap.ComboBox = function(config){
14902     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14903     this.addEvents({
14904         /**
14905          * @event expand
14906          * Fires when the dropdown list is expanded
14907         * @param {Roo.bootstrap.ComboBox} combo This combo box
14908         */
14909         'expand' : true,
14910         /**
14911          * @event collapse
14912          * Fires when the dropdown list is collapsed
14913         * @param {Roo.bootstrap.ComboBox} combo This combo box
14914         */
14915         'collapse' : true,
14916         /**
14917          * @event beforeselect
14918          * Fires before a list item is selected. Return false to cancel the selection.
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         * @param {Roo.data.Record} record The data record returned from the underlying store
14921         * @param {Number} index The index of the selected item in the dropdown list
14922         */
14923         'beforeselect' : true,
14924         /**
14925          * @event select
14926          * Fires when a list item is selected
14927         * @param {Roo.bootstrap.ComboBox} combo This combo box
14928         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14929         * @param {Number} index The index of the selected item in the dropdown list
14930         */
14931         'select' : true,
14932         /**
14933          * @event beforequery
14934          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14935          * The event object passed has these properties:
14936         * @param {Roo.bootstrap.ComboBox} combo This combo box
14937         * @param {String} query The query
14938         * @param {Boolean} forceAll true to force "all" query
14939         * @param {Boolean} cancel true to cancel the query
14940         * @param {Object} e The query event object
14941         */
14942         'beforequery': true,
14943          /**
14944          * @event add
14945          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14946         * @param {Roo.bootstrap.ComboBox} combo This combo box
14947         */
14948         'add' : true,
14949         /**
14950          * @event edit
14951          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14952         * @param {Roo.bootstrap.ComboBox} combo This combo box
14953         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14954         */
14955         'edit' : true,
14956         /**
14957          * @event remove
14958          * Fires when the remove value from the combobox array
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         */
14961         'remove' : true,
14962         /**
14963          * @event afterremove
14964          * Fires when the remove value from the combobox array
14965         * @param {Roo.bootstrap.ComboBox} combo This combo box
14966         */
14967         'afterremove' : true,
14968         /**
14969          * @event specialfilter
14970          * Fires when specialfilter
14971             * @param {Roo.bootstrap.ComboBox} combo This combo box
14972             */
14973         'specialfilter' : true,
14974         /**
14975          * @event tick
14976          * Fires when tick the element
14977             * @param {Roo.bootstrap.ComboBox} combo This combo box
14978             */
14979         'tick' : true,
14980         /**
14981          * @event touchviewdisplay
14982          * Fires when touch view require special display (default is using displayField)
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             * @param {Object} cfg set html .
14985             */
14986         'touchviewdisplay' : true
14987         
14988     });
14989     
14990     this.item = [];
14991     this.tickItems = [];
14992     
14993     this.selectedIndex = -1;
14994     if(this.mode == 'local'){
14995         if(config.queryDelay === undefined){
14996             this.queryDelay = 10;
14997         }
14998         if(config.minChars === undefined){
14999             this.minChars = 0;
15000         }
15001     }
15002 };
15003
15004 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15005      
15006     /**
15007      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15008      * rendering into an Roo.Editor, defaults to false)
15009      */
15010     /**
15011      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15012      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15013      */
15014     /**
15015      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15016      */
15017     /**
15018      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15019      * the dropdown list (defaults to undefined, with no header element)
15020      */
15021
15022      /**
15023      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15024      */
15025      
15026      /**
15027      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15028      */
15029     listWidth: undefined,
15030     /**
15031      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15032      * mode = 'remote' or 'text' if mode = 'local')
15033      */
15034     displayField: undefined,
15035     
15036     /**
15037      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15038      * mode = 'remote' or 'value' if mode = 'local'). 
15039      * Note: use of a valueField requires the user make a selection
15040      * in order for a value to be mapped.
15041      */
15042     valueField: undefined,
15043     /**
15044      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15045      */
15046     modalTitle : '',
15047     
15048     /**
15049      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15050      * field's data value (defaults to the underlying DOM element's name)
15051      */
15052     hiddenName: undefined,
15053     /**
15054      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15055      */
15056     listClass: '',
15057     /**
15058      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15059      */
15060     selectedClass: 'active',
15061     
15062     /**
15063      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15064      */
15065     shadow:'sides',
15066     /**
15067      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15068      * anchor positions (defaults to 'tl-bl')
15069      */
15070     listAlign: 'tl-bl?',
15071     /**
15072      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15073      */
15074     maxHeight: 300,
15075     /**
15076      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15077      * query specified by the allQuery config option (defaults to 'query')
15078      */
15079     triggerAction: 'query',
15080     /**
15081      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15082      * (defaults to 4, does not apply if editable = false)
15083      */
15084     minChars : 4,
15085     /**
15086      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15087      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15088      */
15089     typeAhead: false,
15090     /**
15091      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15092      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15093      */
15094     queryDelay: 500,
15095     /**
15096      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15097      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15098      */
15099     pageSize: 0,
15100     /**
15101      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15102      * when editable = true (defaults to false)
15103      */
15104     selectOnFocus:false,
15105     /**
15106      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15107      */
15108     queryParam: 'query',
15109     /**
15110      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15111      * when mode = 'remote' (defaults to 'Loading...')
15112      */
15113     loadingText: 'Loading...',
15114     /**
15115      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15116      */
15117     resizable: false,
15118     /**
15119      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15120      */
15121     handleHeight : 8,
15122     /**
15123      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15124      * traditional select (defaults to true)
15125      */
15126     editable: true,
15127     /**
15128      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15129      */
15130     allQuery: '',
15131     /**
15132      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15133      */
15134     mode: 'remote',
15135     /**
15136      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15137      * listWidth has a higher value)
15138      */
15139     minListWidth : 70,
15140     /**
15141      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15142      * allow the user to set arbitrary text into the field (defaults to false)
15143      */
15144     forceSelection:false,
15145     /**
15146      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15147      * if typeAhead = true (defaults to 250)
15148      */
15149     typeAheadDelay : 250,
15150     /**
15151      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15152      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15153      */
15154     valueNotFoundText : undefined,
15155     /**
15156      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15157      */
15158     blockFocus : false,
15159     
15160     /**
15161      * @cfg {Boolean} disableClear Disable showing of clear button.
15162      */
15163     disableClear : false,
15164     /**
15165      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15166      */
15167     alwaysQuery : false,
15168     
15169     /**
15170      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15171      */
15172     multiple : false,
15173     
15174     /**
15175      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15176      */
15177     invalidClass : "has-warning",
15178     
15179     /**
15180      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15181      */
15182     validClass : "has-success",
15183     
15184     /**
15185      * @cfg {Boolean} specialFilter (true|false) special filter default false
15186      */
15187     specialFilter : false,
15188     
15189     /**
15190      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15191      */
15192     mobileTouchView : true,
15193     
15194     /**
15195      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15196      */
15197     useNativeIOS : false,
15198     
15199     /**
15200      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15201      */
15202     mobile_restrict_height : false,
15203     
15204     ios_options : false,
15205     
15206     //private
15207     addicon : false,
15208     editicon: false,
15209     
15210     page: 0,
15211     hasQuery: false,
15212     append: false,
15213     loadNext: false,
15214     autoFocus : true,
15215     tickable : false,
15216     btnPosition : 'right',
15217     triggerList : true,
15218     showToggleBtn : true,
15219     animate : true,
15220     emptyResultText: 'Empty',
15221     triggerText : 'Select',
15222     emptyTitle : '',
15223     width : false,
15224     
15225     // element that contains real text value.. (when hidden is used..)
15226     
15227     getAutoCreate : function()
15228     {   
15229         var cfg = false;
15230         //render
15231         /*
15232          * Render classic select for iso
15233          */
15234         
15235         if(Roo.isIOS && this.useNativeIOS){
15236             cfg = this.getAutoCreateNativeIOS();
15237             return cfg;
15238         }
15239         
15240         /*
15241          * Touch Devices
15242          */
15243         
15244         if(Roo.isTouch && this.mobileTouchView){
15245             cfg = this.getAutoCreateTouchView();
15246             return cfg;;
15247         }
15248         
15249         /*
15250          *  Normal ComboBox
15251          */
15252         if(!this.tickable){
15253             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15254             return cfg;
15255         }
15256         
15257         /*
15258          *  ComboBox with tickable selections
15259          */
15260              
15261         var align = this.labelAlign || this.parentLabelAlign();
15262         
15263         cfg = {
15264             cls : 'form-group roo-combobox-tickable' //input-group
15265         };
15266         
15267         var btn_text_select = '';
15268         var btn_text_done = '';
15269         var btn_text_cancel = '';
15270         
15271         if (this.btn_text_show) {
15272             btn_text_select = 'Select';
15273             btn_text_done = 'Done';
15274             btn_text_cancel = 'Cancel'; 
15275         }
15276         
15277         var buttons = {
15278             tag : 'div',
15279             cls : 'tickable-buttons',
15280             cn : [
15281                 {
15282                     tag : 'button',
15283                     type : 'button',
15284                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15285                     //html : this.triggerText
15286                     html: btn_text_select
15287                 },
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     name : 'ok',
15292                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15293                     //html : 'Done'
15294                     html: btn_text_done
15295                 },
15296                 {
15297                     tag : 'button',
15298                     type : 'button',
15299                     name : 'cancel',
15300                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15301                     //html : 'Cancel'
15302                     html: btn_text_cancel
15303                 }
15304             ]
15305         };
15306         
15307         if(this.editable){
15308             buttons.cn.unshift({
15309                 tag: 'input',
15310                 cls: 'roo-select2-search-field-input'
15311             });
15312         }
15313         
15314         var _this = this;
15315         
15316         Roo.each(buttons.cn, function(c){
15317             if (_this.size) {
15318                 c.cls += ' btn-' + _this.size;
15319             }
15320
15321             if (_this.disabled) {
15322                 c.disabled = true;
15323             }
15324         });
15325         
15326         var box = {
15327             tag: 'div',
15328             style : 'display: contents',
15329             cn: [
15330                 {
15331                     tag: 'input',
15332                     type : 'hidden',
15333                     cls: 'form-hidden-field'
15334                 },
15335                 {
15336                     tag: 'ul',
15337                     cls: 'roo-select2-choices',
15338                     cn:[
15339                         {
15340                             tag: 'li',
15341                             cls: 'roo-select2-search-field',
15342                             cn: [
15343                                 buttons
15344                             ]
15345                         }
15346                     ]
15347                 }
15348             ]
15349         };
15350         
15351         var combobox = {
15352             cls: 'roo-select2-container input-group roo-select2-container-multi',
15353             cn: [
15354                 
15355                 box
15356 //                {
15357 //                    tag: 'ul',
15358 //                    cls: 'typeahead typeahead-long dropdown-menu',
15359 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15360 //                }
15361             ]
15362         };
15363         
15364         if(this.hasFeedback && !this.allowBlank){
15365             
15366             var feedback = {
15367                 tag: 'span',
15368                 cls: 'glyphicon form-control-feedback'
15369             };
15370
15371             combobox.cn.push(feedback);
15372         }
15373         
15374         
15375         
15376         var indicator = {
15377             tag : 'i',
15378             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15379             tooltip : 'This field is required'
15380         };
15381         if (Roo.bootstrap.version == 4) {
15382             indicator = {
15383                 tag : 'i',
15384                 style : 'display:none'
15385             };
15386         }
15387         if (align ==='left' && this.fieldLabel.length) {
15388             
15389             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15390             
15391             cfg.cn = [
15392                 indicator,
15393                 {
15394                     tag: 'label',
15395                     'for' :  id,
15396                     cls : 'control-label col-form-label',
15397                     html : this.fieldLabel
15398
15399                 },
15400                 {
15401                     cls : "", 
15402                     cn: [
15403                         combobox
15404                     ]
15405                 }
15406
15407             ];
15408             
15409             var labelCfg = cfg.cn[1];
15410             var contentCfg = cfg.cn[2];
15411             
15412
15413             if(this.indicatorpos == 'right'){
15414                 
15415                 cfg.cn = [
15416                     {
15417                         tag: 'label',
15418                         'for' :  id,
15419                         cls : 'control-label col-form-label',
15420                         cn : [
15421                             {
15422                                 tag : 'span',
15423                                 html : this.fieldLabel
15424                             },
15425                             indicator
15426                         ]
15427                     },
15428                     {
15429                         cls : "",
15430                         cn: [
15431                             combobox
15432                         ]
15433                     }
15434
15435                 ];
15436                 
15437                 
15438                 
15439                 labelCfg = cfg.cn[0];
15440                 contentCfg = cfg.cn[1];
15441             
15442             }
15443             
15444             if(this.labelWidth > 12){
15445                 labelCfg.style = "width: " + this.labelWidth + 'px';
15446             }
15447             if(this.width * 1 > 0){
15448                 contentCfg.style = "width: " + this.width + 'px';
15449             }
15450             if(this.labelWidth < 13 && this.labelmd == 0){
15451                 this.labelmd = this.labelWidth;
15452             }
15453             
15454             if(this.labellg > 0){
15455                 labelCfg.cls += ' col-lg-' + this.labellg;
15456                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15457             }
15458             
15459             if(this.labelmd > 0){
15460                 labelCfg.cls += ' col-md-' + this.labelmd;
15461                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15462             }
15463             
15464             if(this.labelsm > 0){
15465                 labelCfg.cls += ' col-sm-' + this.labelsm;
15466                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15467             }
15468             
15469             if(this.labelxs > 0){
15470                 labelCfg.cls += ' col-xs-' + this.labelxs;
15471                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15472             }
15473                 
15474                 
15475         } else if ( this.fieldLabel.length) {
15476 //                Roo.log(" label");
15477                  cfg.cn = [
15478                    indicator,
15479                     {
15480                         tag: 'label',
15481                         //cls : 'input-group-addon',
15482                         html : this.fieldLabel
15483                     },
15484                     combobox
15485                 ];
15486                 
15487                 if(this.indicatorpos == 'right'){
15488                     cfg.cn = [
15489                         {
15490                             tag: 'label',
15491                             //cls : 'input-group-addon',
15492                             html : this.fieldLabel
15493                         },
15494                         indicator,
15495                         combobox
15496                     ];
15497                     
15498                 }
15499
15500         } else {
15501             
15502 //                Roo.log(" no label && no align");
15503                 cfg = combobox
15504                      
15505                 
15506         }
15507          
15508         var settings=this;
15509         ['xs','sm','md','lg'].map(function(size){
15510             if (settings[size]) {
15511                 cfg.cls += ' col-' + size + '-' + settings[size];
15512             }
15513         });
15514         
15515         return cfg;
15516         
15517     },
15518     
15519     _initEventsCalled : false,
15520     
15521     // private
15522     initEvents: function()
15523     {   
15524         if (this._initEventsCalled) { // as we call render... prevent looping...
15525             return;
15526         }
15527         this._initEventsCalled = true;
15528         
15529         if (!this.store) {
15530             throw "can not find store for combo";
15531         }
15532         
15533         this.indicator = this.indicatorEl();
15534         
15535         this.store = Roo.factory(this.store, Roo.data);
15536         this.store.parent = this;
15537         
15538         // if we are building from html. then this element is so complex, that we can not really
15539         // use the rendered HTML.
15540         // so we have to trash and replace the previous code.
15541         if (Roo.XComponent.build_from_html) {
15542             // remove this element....
15543             var e = this.el.dom, k=0;
15544             while (e ) { e = e.previousSibling;  ++k;}
15545
15546             this.el.remove();
15547             
15548             this.el=false;
15549             this.rendered = false;
15550             
15551             this.render(this.parent().getChildContainer(true), k);
15552         }
15553         
15554         if(Roo.isIOS && this.useNativeIOS){
15555             this.initIOSView();
15556             return;
15557         }
15558         
15559         /*
15560          * Touch Devices
15561          */
15562         
15563         if(Roo.isTouch && this.mobileTouchView){
15564             this.initTouchView();
15565             return;
15566         }
15567         
15568         if(this.tickable){
15569             this.initTickableEvents();
15570             return;
15571         }
15572         
15573         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15574         
15575         if(this.hiddenName){
15576             
15577             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15578             
15579             this.hiddenField.dom.value =
15580                 this.hiddenValue !== undefined ? this.hiddenValue :
15581                 this.value !== undefined ? this.value : '';
15582
15583             // prevent input submission
15584             this.el.dom.removeAttribute('name');
15585             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15586              
15587              
15588         }
15589         //if(Roo.isGecko){
15590         //    this.el.dom.setAttribute('autocomplete', 'off');
15591         //}
15592         
15593         var cls = 'x-combo-list';
15594         
15595         //this.list = new Roo.Layer({
15596         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15597         //});
15598         
15599         var _this = this;
15600         
15601         (function(){
15602             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15603             _this.list.setWidth(lw);
15604         }).defer(100);
15605         
15606         this.list.on('mouseover', this.onViewOver, this);
15607         this.list.on('mousemove', this.onViewMove, this);
15608         this.list.on('scroll', this.onViewScroll, this);
15609         
15610         /*
15611         this.list.swallowEvent('mousewheel');
15612         this.assetHeight = 0;
15613
15614         if(this.title){
15615             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15616             this.assetHeight += this.header.getHeight();
15617         }
15618
15619         this.innerList = this.list.createChild({cls:cls+'-inner'});
15620         this.innerList.on('mouseover', this.onViewOver, this);
15621         this.innerList.on('mousemove', this.onViewMove, this);
15622         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15623         
15624         if(this.allowBlank && !this.pageSize && !this.disableClear){
15625             this.footer = this.list.createChild({cls:cls+'-ft'});
15626             this.pageTb = new Roo.Toolbar(this.footer);
15627            
15628         }
15629         if(this.pageSize){
15630             this.footer = this.list.createChild({cls:cls+'-ft'});
15631             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15632                     {pageSize: this.pageSize});
15633             
15634         }
15635         
15636         if (this.pageTb && this.allowBlank && !this.disableClear) {
15637             var _this = this;
15638             this.pageTb.add(new Roo.Toolbar.Fill(), {
15639                 cls: 'x-btn-icon x-btn-clear',
15640                 text: '&#160;',
15641                 handler: function()
15642                 {
15643                     _this.collapse();
15644                     _this.clearValue();
15645                     _this.onSelect(false, -1);
15646                 }
15647             });
15648         }
15649         if (this.footer) {
15650             this.assetHeight += this.footer.getHeight();
15651         }
15652         */
15653             
15654         if(!this.tpl){
15655             this.tpl = Roo.bootstrap.version == 4 ?
15656                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15657                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15658         }
15659
15660         this.view = new Roo.View(this.list, this.tpl, {
15661             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15662         });
15663         //this.view.wrapEl.setDisplayed(false);
15664         this.view.on('click', this.onViewClick, this);
15665         
15666         
15667         this.store.on('beforeload', this.onBeforeLoad, this);
15668         this.store.on('load', this.onLoad, this);
15669         this.store.on('loadexception', this.onLoadException, this);
15670         /*
15671         if(this.resizable){
15672             this.resizer = new Roo.Resizable(this.list,  {
15673                pinned:true, handles:'se'
15674             });
15675             this.resizer.on('resize', function(r, w, h){
15676                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15677                 this.listWidth = w;
15678                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15679                 this.restrictHeight();
15680             }, this);
15681             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15682         }
15683         */
15684         if(!this.editable){
15685             this.editable = true;
15686             this.setEditable(false);
15687         }
15688         
15689         /*
15690         
15691         if (typeof(this.events.add.listeners) != 'undefined') {
15692             
15693             this.addicon = this.wrap.createChild(
15694                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15695        
15696             this.addicon.on('click', function(e) {
15697                 this.fireEvent('add', this);
15698             }, this);
15699         }
15700         if (typeof(this.events.edit.listeners) != 'undefined') {
15701             
15702             this.editicon = this.wrap.createChild(
15703                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15704             if (this.addicon) {
15705                 this.editicon.setStyle('margin-left', '40px');
15706             }
15707             this.editicon.on('click', function(e) {
15708                 
15709                 // we fire even  if inothing is selected..
15710                 this.fireEvent('edit', this, this.lastData );
15711                 
15712             }, this);
15713         }
15714         */
15715         
15716         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15717             "up" : function(e){
15718                 this.inKeyMode = true;
15719                 this.selectPrev();
15720             },
15721
15722             "down" : function(e){
15723                 if(!this.isExpanded()){
15724                     this.onTriggerClick();
15725                 }else{
15726                     this.inKeyMode = true;
15727                     this.selectNext();
15728                 }
15729             },
15730
15731             "enter" : function(e){
15732 //                this.onViewClick();
15733                 //return true;
15734                 this.collapse();
15735                 
15736                 if(this.fireEvent("specialkey", this, e)){
15737                     this.onViewClick(false);
15738                 }
15739                 
15740                 return true;
15741             },
15742
15743             "esc" : function(e){
15744                 this.collapse();
15745             },
15746
15747             "tab" : function(e){
15748                 this.collapse();
15749                 
15750                 if(this.fireEvent("specialkey", this, e)){
15751                     this.onViewClick(false);
15752                 }
15753                 
15754                 return true;
15755             },
15756
15757             scope : this,
15758
15759             doRelay : function(foo, bar, hname){
15760                 if(hname == 'down' || this.scope.isExpanded()){
15761                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15762                 }
15763                 return true;
15764             },
15765
15766             forceKeyDown: true
15767         });
15768         
15769         
15770         this.queryDelay = Math.max(this.queryDelay || 10,
15771                 this.mode == 'local' ? 10 : 250);
15772         
15773         
15774         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15775         
15776         if(this.typeAhead){
15777             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15778         }
15779         if(this.editable !== false){
15780             this.inputEl().on("keyup", this.onKeyUp, this);
15781         }
15782         if(this.forceSelection){
15783             this.inputEl().on('blur', this.doForce, this);
15784         }
15785         
15786         if(this.multiple){
15787             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15788             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15789         }
15790     },
15791     
15792     initTickableEvents: function()
15793     {   
15794         this.createList();
15795         
15796         if(this.hiddenName){
15797             
15798             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15799             
15800             this.hiddenField.dom.value =
15801                 this.hiddenValue !== undefined ? this.hiddenValue :
15802                 this.value !== undefined ? this.value : '';
15803
15804             // prevent input submission
15805             this.el.dom.removeAttribute('name');
15806             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15807              
15808              
15809         }
15810         
15811 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15812         
15813         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15814         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15815         if(this.triggerList){
15816             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15817         }
15818          
15819         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15820         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15821         
15822         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15823         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15824         
15825         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15826         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15827         
15828         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15829         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15830         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15831         
15832         this.okBtn.hide();
15833         this.cancelBtn.hide();
15834         
15835         var _this = this;
15836         
15837         (function(){
15838             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15839             _this.list.setWidth(lw);
15840         }).defer(100);
15841         
15842         this.list.on('mouseover', this.onViewOver, this);
15843         this.list.on('mousemove', this.onViewMove, this);
15844         
15845         this.list.on('scroll', this.onViewScroll, this);
15846         
15847         if(!this.tpl){
15848             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15849                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15850         }
15851
15852         this.view = new Roo.View(this.list, this.tpl, {
15853             singleSelect:true,
15854             tickable:true,
15855             parent:this,
15856             store: this.store,
15857             selectedClass: this.selectedClass
15858         });
15859         
15860         //this.view.wrapEl.setDisplayed(false);
15861         this.view.on('click', this.onViewClick, this);
15862         
15863         
15864         
15865         this.store.on('beforeload', this.onBeforeLoad, this);
15866         this.store.on('load', this.onLoad, this);
15867         this.store.on('loadexception', this.onLoadException, this);
15868         
15869         if(this.editable){
15870             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15871                 "up" : function(e){
15872                     this.inKeyMode = true;
15873                     this.selectPrev();
15874                 },
15875
15876                 "down" : function(e){
15877                     this.inKeyMode = true;
15878                     this.selectNext();
15879                 },
15880
15881                 "enter" : function(e){
15882                     if(this.fireEvent("specialkey", this, e)){
15883                         this.onViewClick(false);
15884                     }
15885                     
15886                     return true;
15887                 },
15888
15889                 "esc" : function(e){
15890                     this.onTickableFooterButtonClick(e, false, false);
15891                 },
15892
15893                 "tab" : function(e){
15894                     this.fireEvent("specialkey", this, e);
15895                     
15896                     this.onTickableFooterButtonClick(e, false, false);
15897                     
15898                     return true;
15899                 },
15900
15901                 scope : this,
15902
15903                 doRelay : function(e, fn, key){
15904                     if(this.scope.isExpanded()){
15905                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15906                     }
15907                     return true;
15908                 },
15909
15910                 forceKeyDown: true
15911             });
15912         }
15913         
15914         this.queryDelay = Math.max(this.queryDelay || 10,
15915                 this.mode == 'local' ? 10 : 250);
15916         
15917         
15918         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15919         
15920         if(this.typeAhead){
15921             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15922         }
15923         
15924         if(this.editable !== false){
15925             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15926         }
15927         
15928         this.indicator = this.indicatorEl();
15929         
15930         if(this.indicator){
15931             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15932             this.indicator.hide();
15933         }
15934         
15935     },
15936
15937     onDestroy : function(){
15938         if(this.view){
15939             this.view.setStore(null);
15940             this.view.el.removeAllListeners();
15941             this.view.el.remove();
15942             this.view.purgeListeners();
15943         }
15944         if(this.list){
15945             this.list.dom.innerHTML  = '';
15946         }
15947         
15948         if(this.store){
15949             this.store.un('beforeload', this.onBeforeLoad, this);
15950             this.store.un('load', this.onLoad, this);
15951             this.store.un('loadexception', this.onLoadException, this);
15952         }
15953         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15954     },
15955
15956     // private
15957     fireKey : function(e){
15958         if(e.isNavKeyPress() && !this.list.isVisible()){
15959             this.fireEvent("specialkey", this, e);
15960         }
15961     },
15962
15963     // private
15964     onResize: function(w, h)
15965     {
15966         
15967         
15968 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15969 //        
15970 //        if(typeof w != 'number'){
15971 //            // we do not handle it!?!?
15972 //            return;
15973 //        }
15974 //        var tw = this.trigger.getWidth();
15975 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15976 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15977 //        var x = w - tw;
15978 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15979 //            
15980 //        //this.trigger.setStyle('left', x+'px');
15981 //        
15982 //        if(this.list && this.listWidth === undefined){
15983 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15984 //            this.list.setWidth(lw);
15985 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15986 //        }
15987         
15988     
15989         
15990     },
15991
15992     /**
15993      * Allow or prevent the user from directly editing the field text.  If false is passed,
15994      * the user will only be able to select from the items defined in the dropdown list.  This method
15995      * is the runtime equivalent of setting the 'editable' config option at config time.
15996      * @param {Boolean} value True to allow the user to directly edit the field text
15997      */
15998     setEditable : function(value){
15999         if(value == this.editable){
16000             return;
16001         }
16002         this.editable = value;
16003         if(!value){
16004             this.inputEl().dom.setAttribute('readOnly', true);
16005             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16006             this.inputEl().addClass('x-combo-noedit');
16007         }else{
16008             this.inputEl().dom.setAttribute('readOnly', false);
16009             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16010             this.inputEl().removeClass('x-combo-noedit');
16011         }
16012     },
16013
16014     // private
16015     
16016     onBeforeLoad : function(combo,opts){
16017         if(!this.hasFocus){
16018             return;
16019         }
16020          if (!opts.add) {
16021             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16022          }
16023         this.restrictHeight();
16024         this.selectedIndex = -1;
16025     },
16026
16027     // private
16028     onLoad : function(){
16029         
16030         this.hasQuery = false;
16031         
16032         if(!this.hasFocus){
16033             return;
16034         }
16035         
16036         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16037             this.loading.hide();
16038         }
16039         
16040         if(this.store.getCount() > 0){
16041             
16042             this.expand();
16043             this.restrictHeight();
16044             if(this.lastQuery == this.allQuery){
16045                 if(this.editable && !this.tickable){
16046                     this.inputEl().dom.select();
16047                 }
16048                 
16049                 if(
16050                     !this.selectByValue(this.value, true) &&
16051                     this.autoFocus && 
16052                     (
16053                         !this.store.lastOptions ||
16054                         typeof(this.store.lastOptions.add) == 'undefined' || 
16055                         this.store.lastOptions.add != true
16056                     )
16057                 ){
16058                     this.select(0, true);
16059                 }
16060             }else{
16061                 if(this.autoFocus){
16062                     this.selectNext();
16063                 }
16064                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16065                     this.taTask.delay(this.typeAheadDelay);
16066                 }
16067             }
16068         }else{
16069             this.onEmptyResults();
16070         }
16071         
16072         //this.el.focus();
16073     },
16074     // private
16075     onLoadException : function()
16076     {
16077         this.hasQuery = false;
16078         
16079         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16080             this.loading.hide();
16081         }
16082         
16083         if(this.tickable && this.editable){
16084             return;
16085         }
16086         
16087         this.collapse();
16088         // only causes errors at present
16089         //Roo.log(this.store.reader.jsonData);
16090         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16091             // fixme
16092             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16093         //}
16094         
16095         
16096     },
16097     // private
16098     onTypeAhead : function(){
16099         if(this.store.getCount() > 0){
16100             var r = this.store.getAt(0);
16101             var newValue = r.data[this.displayField];
16102             var len = newValue.length;
16103             var selStart = this.getRawValue().length;
16104             
16105             if(selStart != len){
16106                 this.setRawValue(newValue);
16107                 this.selectText(selStart, newValue.length);
16108             }
16109         }
16110     },
16111
16112     // private
16113     onSelect : function(record, index){
16114         
16115         if(this.fireEvent('beforeselect', this, record, index) !== false){
16116         
16117             this.setFromData(index > -1 ? record.data : false);
16118             
16119             this.collapse();
16120             this.fireEvent('select', this, record, index);
16121         }
16122     },
16123
16124     /**
16125      * Returns the currently selected field value or empty string if no value is set.
16126      * @return {String} value The selected value
16127      */
16128     getValue : function()
16129     {
16130         if(Roo.isIOS && this.useNativeIOS){
16131             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16132         }
16133         
16134         if(this.multiple){
16135             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16136         }
16137         
16138         if(this.valueField){
16139             return typeof this.value != 'undefined' ? this.value : '';
16140         }else{
16141             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16142         }
16143     },
16144     
16145     getRawValue : function()
16146     {
16147         if(Roo.isIOS && this.useNativeIOS){
16148             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16149         }
16150         
16151         var v = this.inputEl().getValue();
16152         
16153         return v;
16154     },
16155
16156     /**
16157      * Clears any text/value currently set in the field
16158      */
16159     clearValue : function(){
16160         
16161         if(this.hiddenField){
16162             this.hiddenField.dom.value = '';
16163         }
16164         this.value = '';
16165         this.setRawValue('');
16166         this.lastSelectionText = '';
16167         this.lastData = false;
16168         
16169         var close = this.closeTriggerEl();
16170         
16171         if(close){
16172             close.hide();
16173         }
16174         
16175         this.validate();
16176         
16177     },
16178
16179     /**
16180      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16181      * will be displayed in the field.  If the value does not match the data value of an existing item,
16182      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16183      * Otherwise the field will be blank (although the value will still be set).
16184      * @param {String} value The value to match
16185      */
16186     setValue : function(v)
16187     {
16188         if(Roo.isIOS && this.useNativeIOS){
16189             this.setIOSValue(v);
16190             return;
16191         }
16192         
16193         if(this.multiple){
16194             this.syncValue();
16195             return;
16196         }
16197         
16198         var text = v;
16199         if(this.valueField){
16200             var r = this.findRecord(this.valueField, v);
16201             if(r){
16202                 text = r.data[this.displayField];
16203             }else if(this.valueNotFoundText !== undefined){
16204                 text = this.valueNotFoundText;
16205             }
16206         }
16207         this.lastSelectionText = text;
16208         if(this.hiddenField){
16209             this.hiddenField.dom.value = v;
16210         }
16211         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16212         this.value = v;
16213         
16214         var close = this.closeTriggerEl();
16215         
16216         if(close){
16217             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16218         }
16219         
16220         this.validate();
16221     },
16222     /**
16223      * @property {Object} the last set data for the element
16224      */
16225     
16226     lastData : false,
16227     /**
16228      * Sets the value of the field based on a object which is related to the record format for the store.
16229      * @param {Object} value the value to set as. or false on reset?
16230      */
16231     setFromData : function(o){
16232         
16233         if(this.multiple){
16234             this.addItem(o);
16235             return;
16236         }
16237             
16238         var dv = ''; // display value
16239         var vv = ''; // value value..
16240         this.lastData = o;
16241         if (this.displayField) {
16242             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16243         } else {
16244             // this is an error condition!!!
16245             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16246         }
16247         
16248         if(this.valueField){
16249             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16250         }
16251         
16252         var close = this.closeTriggerEl();
16253         
16254         if(close){
16255             if(dv.length || vv * 1 > 0){
16256                 close.show() ;
16257                 this.blockFocus=true;
16258             } else {
16259                 close.hide();
16260             }             
16261         }
16262         
16263         if(this.hiddenField){
16264             this.hiddenField.dom.value = vv;
16265             
16266             this.lastSelectionText = dv;
16267             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16268             this.value = vv;
16269             return;
16270         }
16271         // no hidden field.. - we store the value in 'value', but still display
16272         // display field!!!!
16273         this.lastSelectionText = dv;
16274         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275         this.value = vv;
16276         
16277         
16278         
16279     },
16280     // private
16281     reset : function(){
16282         // overridden so that last data is reset..
16283         
16284         if(this.multiple){
16285             this.clearItem();
16286             return;
16287         }
16288         
16289         this.setValue(this.originalValue);
16290         //this.clearInvalid();
16291         this.lastData = false;
16292         if (this.view) {
16293             this.view.clearSelections();
16294         }
16295         
16296         this.validate();
16297     },
16298     // private
16299     findRecord : function(prop, value){
16300         var record;
16301         if(this.store.getCount() > 0){
16302             this.store.each(function(r){
16303                 if(r.data[prop] == value){
16304                     record = r;
16305                     return false;
16306                 }
16307                 return true;
16308             });
16309         }
16310         return record;
16311     },
16312     
16313     getName: function()
16314     {
16315         // returns hidden if it's set..
16316         if (!this.rendered) {return ''};
16317         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16318         
16319     },
16320     // private
16321     onViewMove : function(e, t){
16322         this.inKeyMode = false;
16323     },
16324
16325     // private
16326     onViewOver : function(e, t){
16327         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16328             return;
16329         }
16330         var item = this.view.findItemFromChild(t);
16331         
16332         if(item){
16333             var index = this.view.indexOf(item);
16334             this.select(index, false);
16335         }
16336     },
16337
16338     // private
16339     onViewClick : function(view, doFocus, el, e)
16340     {
16341         var index = this.view.getSelectedIndexes()[0];
16342         
16343         var r = this.store.getAt(index);
16344         
16345         if(this.tickable){
16346             
16347             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16348                 return;
16349             }
16350             
16351             var rm = false;
16352             var _this = this;
16353             
16354             Roo.each(this.tickItems, function(v,k){
16355                 
16356                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16357                     Roo.log(v);
16358                     _this.tickItems.splice(k, 1);
16359                     
16360                     if(typeof(e) == 'undefined' && view == false){
16361                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16362                     }
16363                     
16364                     rm = true;
16365                     return;
16366                 }
16367             });
16368             
16369             if(rm){
16370                 return;
16371             }
16372             
16373             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16374                 this.tickItems.push(r.data);
16375             }
16376             
16377             if(typeof(e) == 'undefined' && view == false){
16378                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16379             }
16380                     
16381             return;
16382         }
16383         
16384         if(r){
16385             this.onSelect(r, index);
16386         }
16387         if(doFocus !== false && !this.blockFocus){
16388             this.inputEl().focus();
16389         }
16390     },
16391
16392     // private
16393     restrictHeight : function(){
16394         //this.innerList.dom.style.height = '';
16395         //var inner = this.innerList.dom;
16396         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16397         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16398         //this.list.beginUpdate();
16399         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16400         this.list.alignTo(this.inputEl(), this.listAlign);
16401         this.list.alignTo(this.inputEl(), this.listAlign);
16402         //this.list.endUpdate();
16403     },
16404
16405     // private
16406     onEmptyResults : function(){
16407         
16408         if(this.tickable && this.editable){
16409             this.hasFocus = false;
16410             this.restrictHeight();
16411             return;
16412         }
16413         
16414         this.collapse();
16415     },
16416
16417     /**
16418      * Returns true if the dropdown list is expanded, else false.
16419      */
16420     isExpanded : function(){
16421         return this.list.isVisible();
16422     },
16423
16424     /**
16425      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16426      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16427      * @param {String} value The data value of the item to select
16428      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16429      * selected item if it is not currently in view (defaults to true)
16430      * @return {Boolean} True if the value matched an item in the list, else false
16431      */
16432     selectByValue : function(v, scrollIntoView){
16433         if(v !== undefined && v !== null){
16434             var r = this.findRecord(this.valueField || this.displayField, v);
16435             if(r){
16436                 this.select(this.store.indexOf(r), scrollIntoView);
16437                 return true;
16438             }
16439         }
16440         return false;
16441     },
16442
16443     /**
16444      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16445      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16446      * @param {Number} index The zero-based index of the list item to select
16447      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16448      * selected item if it is not currently in view (defaults to true)
16449      */
16450     select : function(index, scrollIntoView){
16451         this.selectedIndex = index;
16452         this.view.select(index);
16453         if(scrollIntoView !== false){
16454             var el = this.view.getNode(index);
16455             /*
16456              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16457              */
16458             if(el){
16459                 this.list.scrollChildIntoView(el, false);
16460             }
16461         }
16462     },
16463
16464     // private
16465     selectNext : function(){
16466         var ct = this.store.getCount();
16467         if(ct > 0){
16468             if(this.selectedIndex == -1){
16469                 this.select(0);
16470             }else if(this.selectedIndex < ct-1){
16471                 this.select(this.selectedIndex+1);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectPrev : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex != 0){
16483                 this.select(this.selectedIndex-1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     onKeyUp : function(e){
16490         if(this.editable !== false && !e.isSpecialKey()){
16491             this.lastKey = e.getKey();
16492             this.dqTask.delay(this.queryDelay);
16493         }
16494     },
16495
16496     // private
16497     validateBlur : function(){
16498         return !this.list || !this.list.isVisible();   
16499     },
16500
16501     // private
16502     initQuery : function(){
16503         
16504         var v = this.getRawValue();
16505         
16506         if(this.tickable && this.editable){
16507             v = this.tickableInputEl().getValue();
16508         }
16509         
16510         this.doQuery(v);
16511     },
16512
16513     // private
16514     doForce : function(){
16515         if(this.inputEl().dom.value.length > 0){
16516             this.inputEl().dom.value =
16517                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16518              
16519         }
16520     },
16521
16522     /**
16523      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16524      * query allowing the query action to be canceled if needed.
16525      * @param {String} query The SQL query to execute
16526      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16527      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16528      * saved in the current store (defaults to false)
16529      */
16530     doQuery : function(q, forceAll){
16531         
16532         if(q === undefined || q === null){
16533             q = '';
16534         }
16535         var qe = {
16536             query: q,
16537             forceAll: forceAll,
16538             combo: this,
16539             cancel:false
16540         };
16541         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16542             return false;
16543         }
16544         q = qe.query;
16545         
16546         forceAll = qe.forceAll;
16547         if(forceAll === true || (q.length >= this.minChars)){
16548             
16549             this.hasQuery = true;
16550             
16551             if(this.lastQuery != q || this.alwaysQuery){
16552                 this.lastQuery = q;
16553                 if(this.mode == 'local'){
16554                     this.selectedIndex = -1;
16555                     if(forceAll){
16556                         this.store.clearFilter();
16557                     }else{
16558                         
16559                         if(this.specialFilter){
16560                             this.fireEvent('specialfilter', this);
16561                             this.onLoad();
16562                             return;
16563                         }
16564                         
16565                         this.store.filter(this.displayField, q);
16566                     }
16567                     
16568                     this.store.fireEvent("datachanged", this.store);
16569                     
16570                     this.onLoad();
16571                     
16572                     
16573                 }else{
16574                     
16575                     this.store.baseParams[this.queryParam] = q;
16576                     
16577                     var options = {params : this.getParams(q)};
16578                     
16579                     if(this.loadNext){
16580                         options.add = true;
16581                         options.params.start = this.page * this.pageSize;
16582                     }
16583                     
16584                     this.store.load(options);
16585                     
16586                     /*
16587                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16588                      *  we should expand the list on onLoad
16589                      *  so command out it
16590                      */
16591 //                    this.expand();
16592                 }
16593             }else{
16594                 this.selectedIndex = -1;
16595                 this.onLoad();   
16596             }
16597         }
16598         
16599         this.loadNext = false;
16600     },
16601     
16602     // private
16603     getParams : function(q){
16604         var p = {};
16605         //p[this.queryParam] = q;
16606         
16607         if(this.pageSize){
16608             p.start = 0;
16609             p.limit = this.pageSize;
16610         }
16611         return p;
16612     },
16613
16614     /**
16615      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16616      */
16617     collapse : function(){
16618         if(!this.isExpanded()){
16619             return;
16620         }
16621         
16622         this.list.hide();
16623         
16624         this.hasFocus = false;
16625         
16626         if(this.tickable){
16627             this.okBtn.hide();
16628             this.cancelBtn.hide();
16629             this.trigger.show();
16630             
16631             if(this.editable){
16632                 this.tickableInputEl().dom.value = '';
16633                 this.tickableInputEl().blur();
16634             }
16635             
16636         }
16637         
16638         Roo.get(document).un('mousedown', this.collapseIf, this);
16639         Roo.get(document).un('mousewheel', this.collapseIf, this);
16640         if (!this.editable) {
16641             Roo.get(document).un('keydown', this.listKeyPress, this);
16642         }
16643         this.fireEvent('collapse', this);
16644         
16645         this.validate();
16646     },
16647
16648     // private
16649     collapseIf : function(e){
16650         var in_combo  = e.within(this.el);
16651         var in_list =  e.within(this.list);
16652         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16653         
16654         if (in_combo || in_list || is_list) {
16655             //e.stopPropagation();
16656             return;
16657         }
16658         
16659         if(this.tickable){
16660             this.onTickableFooterButtonClick(e, false, false);
16661         }
16662
16663         this.collapse();
16664         
16665     },
16666
16667     /**
16668      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16669      */
16670     expand : function(){
16671        
16672         if(this.isExpanded() || !this.hasFocus){
16673             return;
16674         }
16675         
16676         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16677         this.list.setWidth(lw);
16678         
16679         Roo.log('expand');
16680         
16681         this.list.show();
16682         
16683         this.restrictHeight();
16684         
16685         if(this.tickable){
16686             
16687             this.tickItems = Roo.apply([], this.item);
16688             
16689             this.okBtn.show();
16690             this.cancelBtn.show();
16691             this.trigger.hide();
16692             
16693             if(this.editable){
16694                 this.tickableInputEl().focus();
16695             }
16696             
16697         }
16698         
16699         Roo.get(document).on('mousedown', this.collapseIf, this);
16700         Roo.get(document).on('mousewheel', this.collapseIf, this);
16701         if (!this.editable) {
16702             Roo.get(document).on('keydown', this.listKeyPress, this);
16703         }
16704         
16705         this.fireEvent('expand', this);
16706     },
16707
16708     // private
16709     // Implements the default empty TriggerField.onTriggerClick function
16710     onTriggerClick : function(e)
16711     {
16712         Roo.log('trigger click');
16713         
16714         if(this.disabled || !this.triggerList){
16715             return;
16716         }
16717         
16718         this.page = 0;
16719         this.loadNext = false;
16720         
16721         if(this.isExpanded()){
16722             this.collapse();
16723             if (!this.blockFocus) {
16724                 this.inputEl().focus();
16725             }
16726             
16727         }else {
16728             this.hasFocus = true;
16729             if(this.triggerAction == 'all') {
16730                 this.doQuery(this.allQuery, true);
16731             } else {
16732                 this.doQuery(this.getRawValue());
16733             }
16734             if (!this.blockFocus) {
16735                 this.inputEl().focus();
16736             }
16737         }
16738     },
16739     
16740     onTickableTriggerClick : function(e)
16741     {
16742         if(this.disabled){
16743             return;
16744         }
16745         
16746         this.page = 0;
16747         this.loadNext = false;
16748         this.hasFocus = true;
16749         
16750         if(this.triggerAction == 'all') {
16751             this.doQuery(this.allQuery, true);
16752         } else {
16753             this.doQuery(this.getRawValue());
16754         }
16755     },
16756     
16757     onSearchFieldClick : function(e)
16758     {
16759         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16760             this.onTickableFooterButtonClick(e, false, false);
16761             return;
16762         }
16763         
16764         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16765             return;
16766         }
16767         
16768         this.page = 0;
16769         this.loadNext = false;
16770         this.hasFocus = true;
16771         
16772         if(this.triggerAction == 'all') {
16773             this.doQuery(this.allQuery, true);
16774         } else {
16775             this.doQuery(this.getRawValue());
16776         }
16777     },
16778     
16779     listKeyPress : function(e)
16780     {
16781         //Roo.log('listkeypress');
16782         // scroll to first matching element based on key pres..
16783         if (e.isSpecialKey()) {
16784             return false;
16785         }
16786         var k = String.fromCharCode(e.getKey()).toUpperCase();
16787         //Roo.log(k);
16788         var match  = false;
16789         var csel = this.view.getSelectedNodes();
16790         var cselitem = false;
16791         if (csel.length) {
16792             var ix = this.view.indexOf(csel[0]);
16793             cselitem  = this.store.getAt(ix);
16794             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16795                 cselitem = false;
16796             }
16797             
16798         }
16799         
16800         this.store.each(function(v) { 
16801             if (cselitem) {
16802                 // start at existing selection.
16803                 if (cselitem.id == v.id) {
16804                     cselitem = false;
16805                 }
16806                 return true;
16807             }
16808                 
16809             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16810                 match = this.store.indexOf(v);
16811                 return false;
16812             }
16813             return true;
16814         }, this);
16815         
16816         if (match === false) {
16817             return true; // no more action?
16818         }
16819         // scroll to?
16820         this.view.select(match);
16821         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16822         sn.scrollIntoView(sn.dom.parentNode, false);
16823     },
16824     
16825     onViewScroll : function(e, t){
16826         
16827         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){
16828             return;
16829         }
16830         
16831         this.hasQuery = true;
16832         
16833         this.loading = this.list.select('.loading', true).first();
16834         
16835         if(this.loading === null){
16836             this.list.createChild({
16837                 tag: 'div',
16838                 cls: 'loading roo-select2-more-results roo-select2-active',
16839                 html: 'Loading more results...'
16840             });
16841             
16842             this.loading = this.list.select('.loading', true).first();
16843             
16844             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16845             
16846             this.loading.hide();
16847         }
16848         
16849         this.loading.show();
16850         
16851         var _combo = this;
16852         
16853         this.page++;
16854         this.loadNext = true;
16855         
16856         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16857         
16858         return;
16859     },
16860     
16861     addItem : function(o)
16862     {   
16863         var dv = ''; // display value
16864         
16865         if (this.displayField) {
16866             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16867         } else {
16868             // this is an error condition!!!
16869             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16870         }
16871         
16872         if(!dv.length){
16873             return;
16874         }
16875         
16876         var choice = this.choices.createChild({
16877             tag: 'li',
16878             cls: 'roo-select2-search-choice',
16879             cn: [
16880                 {
16881                     tag: 'div',
16882                     html: dv
16883                 },
16884                 {
16885                     tag: 'a',
16886                     href: '#',
16887                     cls: 'roo-select2-search-choice-close fa fa-times',
16888                     tabindex: '-1'
16889                 }
16890             ]
16891             
16892         }, this.searchField);
16893         
16894         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16895         
16896         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16897         
16898         this.item.push(o);
16899         
16900         this.lastData = o;
16901         
16902         this.syncValue();
16903         
16904         this.inputEl().dom.value = '';
16905         
16906         this.validate();
16907     },
16908     
16909     onRemoveItem : function(e, _self, o)
16910     {
16911         e.preventDefault();
16912         
16913         this.lastItem = Roo.apply([], this.item);
16914         
16915         var index = this.item.indexOf(o.data) * 1;
16916         
16917         if( index < 0){
16918             Roo.log('not this item?!');
16919             return;
16920         }
16921         
16922         this.item.splice(index, 1);
16923         o.item.remove();
16924         
16925         this.syncValue();
16926         
16927         this.fireEvent('remove', this, e);
16928         
16929         this.validate();
16930         
16931     },
16932     
16933     syncValue : function()
16934     {
16935         if(!this.item.length){
16936             this.clearValue();
16937             return;
16938         }
16939             
16940         var value = [];
16941         var _this = this;
16942         Roo.each(this.item, function(i){
16943             if(_this.valueField){
16944                 value.push(i[_this.valueField]);
16945                 return;
16946             }
16947
16948             value.push(i);
16949         });
16950
16951         this.value = value.join(',');
16952
16953         if(this.hiddenField){
16954             this.hiddenField.dom.value = this.value;
16955         }
16956         
16957         this.store.fireEvent("datachanged", this.store);
16958         
16959         this.validate();
16960     },
16961     
16962     clearItem : function()
16963     {
16964         if(!this.multiple){
16965             return;
16966         }
16967         
16968         this.item = [];
16969         
16970         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16971            c.remove();
16972         });
16973         
16974         this.syncValue();
16975         
16976         this.validate();
16977         
16978         if(this.tickable && !Roo.isTouch){
16979             this.view.refresh();
16980         }
16981     },
16982     
16983     inputEl: function ()
16984     {
16985         if(Roo.isIOS && this.useNativeIOS){
16986             return this.el.select('select.roo-ios-select', true).first();
16987         }
16988         
16989         if(Roo.isTouch && this.mobileTouchView){
16990             return this.el.select('input.form-control',true).first();
16991         }
16992         
16993         if(this.tickable){
16994             return this.searchField;
16995         }
16996         
16997         return this.el.select('input.form-control',true).first();
16998     },
16999     
17000     onTickableFooterButtonClick : function(e, btn, el)
17001     {
17002         e.preventDefault();
17003         
17004         this.lastItem = Roo.apply([], this.item);
17005         
17006         if(btn && btn.name == 'cancel'){
17007             this.tickItems = Roo.apply([], this.item);
17008             this.collapse();
17009             return;
17010         }
17011         
17012         this.clearItem();
17013         
17014         var _this = this;
17015         
17016         Roo.each(this.tickItems, function(o){
17017             _this.addItem(o);
17018         });
17019         
17020         this.collapse();
17021         
17022     },
17023     
17024     validate : function()
17025     {
17026         if(this.getVisibilityEl().hasClass('hidden')){
17027             return true;
17028         }
17029         
17030         var v = this.getRawValue();
17031         
17032         if(this.multiple){
17033             v = this.getValue();
17034         }
17035         
17036         if(this.disabled || this.allowBlank || v.length){
17037             this.markValid();
17038             return true;
17039         }
17040         
17041         this.markInvalid();
17042         return false;
17043     },
17044     
17045     tickableInputEl : function()
17046     {
17047         if(!this.tickable || !this.editable){
17048             return this.inputEl();
17049         }
17050         
17051         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17052     },
17053     
17054     
17055     getAutoCreateTouchView : function()
17056     {
17057         var id = Roo.id();
17058         
17059         var cfg = {
17060             cls: 'form-group' //input-group
17061         };
17062         
17063         var input =  {
17064             tag: 'input',
17065             id : id,
17066             type : this.inputType,
17067             cls : 'form-control x-combo-noedit',
17068             autocomplete: 'new-password',
17069             placeholder : this.placeholder || '',
17070             readonly : true
17071         };
17072         
17073         if (this.name) {
17074             input.name = this.name;
17075         }
17076         
17077         if (this.size) {
17078             input.cls += ' input-' + this.size;
17079         }
17080         
17081         if (this.disabled) {
17082             input.disabled = true;
17083         }
17084         
17085         var inputblock = {
17086             cls : 'roo-combobox-wrap',
17087             cn : [
17088                 input
17089             ]
17090         };
17091         
17092         if(this.before){
17093             inputblock.cls += ' input-group';
17094             
17095             inputblock.cn.unshift({
17096                 tag :'span',
17097                 cls : 'input-group-addon input-group-prepend input-group-text',
17098                 html : this.before
17099             });
17100         }
17101         
17102         if(this.removable && !this.multiple){
17103             inputblock.cls += ' roo-removable';
17104             
17105             inputblock.cn.push({
17106                 tag: 'button',
17107                 html : 'x',
17108                 cls : 'roo-combo-removable-btn close'
17109             });
17110         }
17111
17112         if(this.hasFeedback && !this.allowBlank){
17113             
17114             inputblock.cls += ' has-feedback';
17115             
17116             inputblock.cn.push({
17117                 tag: 'span',
17118                 cls: 'glyphicon form-control-feedback'
17119             });
17120             
17121         }
17122         
17123         if (this.after) {
17124             
17125             inputblock.cls += (this.before) ? '' : ' input-group';
17126             
17127             inputblock.cn.push({
17128                 tag :'span',
17129                 cls : 'input-group-addon input-group-append input-group-text',
17130                 html : this.after
17131             });
17132         }
17133
17134         
17135         var ibwrap = inputblock;
17136         
17137         if(this.multiple){
17138             ibwrap = {
17139                 tag: 'ul',
17140                 cls: 'roo-select2-choices',
17141                 cn:[
17142                     {
17143                         tag: 'li',
17144                         cls: 'roo-select2-search-field',
17145                         cn: [
17146
17147                             inputblock
17148                         ]
17149                     }
17150                 ]
17151             };
17152         
17153             
17154         }
17155         
17156         var combobox = {
17157             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17158             cn: [
17159                 {
17160                     tag: 'input',
17161                     type : 'hidden',
17162                     cls: 'form-hidden-field'
17163                 },
17164                 ibwrap
17165             ]
17166         };
17167         
17168         if(!this.multiple && this.showToggleBtn){
17169             
17170             var caret = {
17171                 cls: 'caret'
17172             };
17173             
17174             if (this.caret != false) {
17175                 caret = {
17176                      tag: 'i',
17177                      cls: 'fa fa-' + this.caret
17178                 };
17179                 
17180             }
17181             
17182             combobox.cn.push({
17183                 tag :'span',
17184                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17185                 cn : [
17186                     Roo.bootstrap.version == 3 ? caret : '',
17187                     {
17188                         tag: 'span',
17189                         cls: 'combobox-clear',
17190                         cn  : [
17191                             {
17192                                 tag : 'i',
17193                                 cls: 'icon-remove'
17194                             }
17195                         ]
17196                     }
17197                 ]
17198
17199             })
17200         }
17201         
17202         if(this.multiple){
17203             combobox.cls += ' roo-select2-container-multi';
17204         }
17205         
17206         var align = this.labelAlign || this.parentLabelAlign();
17207         
17208         if (align ==='left' && this.fieldLabel.length) {
17209
17210             cfg.cn = [
17211                 {
17212                    tag : 'i',
17213                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17214                    tooltip : 'This field is required'
17215                 },
17216                 {
17217                     tag: 'label',
17218                     cls : 'control-label col-form-label',
17219                     html : this.fieldLabel
17220
17221                 },
17222                 {
17223                     cls : 'roo-combobox-wrap ', 
17224                     cn: [
17225                         combobox
17226                     ]
17227                 }
17228             ];
17229             
17230             var labelCfg = cfg.cn[1];
17231             var contentCfg = cfg.cn[2];
17232             
17233
17234             if(this.indicatorpos == 'right'){
17235                 cfg.cn = [
17236                     {
17237                         tag: 'label',
17238                         'for' :  id,
17239                         cls : 'control-label col-form-label',
17240                         cn : [
17241                             {
17242                                 tag : 'span',
17243                                 html : this.fieldLabel
17244                             },
17245                             {
17246                                 tag : 'i',
17247                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17248                                 tooltip : 'This field is required'
17249                             }
17250                         ]
17251                     },
17252                     {
17253                         cls : "roo-combobox-wrap ",
17254                         cn: [
17255                             combobox
17256                         ]
17257                     }
17258
17259                 ];
17260                 
17261                 labelCfg = cfg.cn[0];
17262                 contentCfg = cfg.cn[1];
17263             }
17264             
17265            
17266             
17267             if(this.labelWidth > 12){
17268                 labelCfg.style = "width: " + this.labelWidth + 'px';
17269             }
17270            
17271             if(this.labelWidth < 13 && this.labelmd == 0){
17272                 this.labelmd = this.labelWidth;
17273             }
17274             
17275             if(this.labellg > 0){
17276                 labelCfg.cls += ' col-lg-' + this.labellg;
17277                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17278             }
17279             
17280             if(this.labelmd > 0){
17281                 labelCfg.cls += ' col-md-' + this.labelmd;
17282                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17283             }
17284             
17285             if(this.labelsm > 0){
17286                 labelCfg.cls += ' col-sm-' + this.labelsm;
17287                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17288             }
17289             
17290             if(this.labelxs > 0){
17291                 labelCfg.cls += ' col-xs-' + this.labelxs;
17292                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17293             }
17294                 
17295                 
17296         } else if ( this.fieldLabel.length) {
17297             cfg.cn = [
17298                 {
17299                    tag : 'i',
17300                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17301                    tooltip : 'This field is required'
17302                 },
17303                 {
17304                     tag: 'label',
17305                     cls : 'control-label',
17306                     html : this.fieldLabel
17307
17308                 },
17309                 {
17310                     cls : '', 
17311                     cn: [
17312                         combobox
17313                     ]
17314                 }
17315             ];
17316             
17317             if(this.indicatorpos == 'right'){
17318                 cfg.cn = [
17319                     {
17320                         tag: 'label',
17321                         cls : 'control-label',
17322                         html : this.fieldLabel,
17323                         cn : [
17324                             {
17325                                tag : 'i',
17326                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17327                                tooltip : 'This field is required'
17328                             }
17329                         ]
17330                     },
17331                     {
17332                         cls : '', 
17333                         cn: [
17334                             combobox
17335                         ]
17336                     }
17337                 ];
17338             }
17339         } else {
17340             cfg.cn = combobox;    
17341         }
17342         
17343         
17344         var settings = this;
17345         
17346         ['xs','sm','md','lg'].map(function(size){
17347             if (settings[size]) {
17348                 cfg.cls += ' col-' + size + '-' + settings[size];
17349             }
17350         });
17351         
17352         return cfg;
17353     },
17354     
17355     initTouchView : function()
17356     {
17357         this.renderTouchView();
17358         
17359         this.touchViewEl.on('scroll', function(){
17360             this.el.dom.scrollTop = 0;
17361         }, this);
17362         
17363         this.originalValue = this.getValue();
17364         
17365         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17366         
17367         this.inputEl().on("click", this.showTouchView, this);
17368         if (this.triggerEl) {
17369             this.triggerEl.on("click", this.showTouchView, this);
17370         }
17371         
17372         
17373         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17374         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17375         
17376         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17377         
17378         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17379         this.store.on('load', this.onTouchViewLoad, this);
17380         this.store.on('loadexception', this.onTouchViewLoadException, this);
17381         
17382         if(this.hiddenName){
17383             
17384             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17385             
17386             this.hiddenField.dom.value =
17387                 this.hiddenValue !== undefined ? this.hiddenValue :
17388                 this.value !== undefined ? this.value : '';
17389         
17390             this.el.dom.removeAttribute('name');
17391             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17392         }
17393         
17394         if(this.multiple){
17395             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17396             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17397         }
17398         
17399         if(this.removable && !this.multiple){
17400             var close = this.closeTriggerEl();
17401             if(close){
17402                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17403                 close.on('click', this.removeBtnClick, this, close);
17404             }
17405         }
17406         /*
17407          * fix the bug in Safari iOS8
17408          */
17409         this.inputEl().on("focus", function(e){
17410             document.activeElement.blur();
17411         }, this);
17412         
17413         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17414         
17415         return;
17416         
17417         
17418     },
17419     
17420     renderTouchView : function()
17421     {
17422         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17423         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17424         
17425         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17426         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17427         
17428         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17429         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17430         this.touchViewBodyEl.setStyle('overflow', 'auto');
17431         
17432         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17433         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17436         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         
17438     },
17439     
17440     showTouchView : function()
17441     {
17442         if(this.disabled){
17443             return;
17444         }
17445         
17446         this.touchViewHeaderEl.hide();
17447
17448         if(this.modalTitle.length){
17449             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17450             this.touchViewHeaderEl.show();
17451         }
17452
17453         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17454         this.touchViewEl.show();
17455
17456         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17457         
17458         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17459         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17460
17461         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17462
17463         if(this.modalTitle.length){
17464             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17465         }
17466         
17467         this.touchViewBodyEl.setHeight(bodyHeight);
17468
17469         if(this.animate){
17470             var _this = this;
17471             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17472         }else{
17473             this.touchViewEl.addClass(['in','show']);
17474         }
17475         
17476         if(this._touchViewMask){
17477             Roo.get(document.body).addClass("x-body-masked");
17478             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17479             this._touchViewMask.setStyle('z-index', 10000);
17480             this._touchViewMask.addClass('show');
17481         }
17482         
17483         this.doTouchViewQuery();
17484         
17485     },
17486     
17487     hideTouchView : function()
17488     {
17489         this.touchViewEl.removeClass(['in','show']);
17490
17491         if(this.animate){
17492             var _this = this;
17493             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17494         }else{
17495             this.touchViewEl.setStyle('display', 'none');
17496         }
17497         
17498         if(this._touchViewMask){
17499             this._touchViewMask.removeClass('show');
17500             Roo.get(document.body).removeClass("x-body-masked");
17501         }
17502     },
17503     
17504     setTouchViewValue : function()
17505     {
17506         if(this.multiple){
17507             this.clearItem();
17508         
17509             var _this = this;
17510
17511             Roo.each(this.tickItems, function(o){
17512                 this.addItem(o);
17513             }, this);
17514         }
17515         
17516         this.hideTouchView();
17517     },
17518     
17519     doTouchViewQuery : function()
17520     {
17521         var qe = {
17522             query: '',
17523             forceAll: true,
17524             combo: this,
17525             cancel:false
17526         };
17527         
17528         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17529             return false;
17530         }
17531         
17532         if(!this.alwaysQuery || this.mode == 'local'){
17533             this.onTouchViewLoad();
17534             return;
17535         }
17536         
17537         this.store.load();
17538     },
17539     
17540     onTouchViewBeforeLoad : function(combo,opts)
17541     {
17542         return;
17543     },
17544
17545     // private
17546     onTouchViewLoad : function()
17547     {
17548         if(this.store.getCount() < 1){
17549             this.onTouchViewEmptyResults();
17550             return;
17551         }
17552         
17553         this.clearTouchView();
17554         
17555         var rawValue = this.getRawValue();
17556         
17557         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17558         
17559         this.tickItems = [];
17560         
17561         this.store.data.each(function(d, rowIndex){
17562             var row = this.touchViewListGroup.createChild(template);
17563             
17564             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17565                 row.addClass(d.data.cls);
17566             }
17567             
17568             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17569                 var cfg = {
17570                     data : d.data,
17571                     html : d.data[this.displayField]
17572                 };
17573                 
17574                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17575                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17576                 }
17577             }
17578             row.removeClass('selected');
17579             if(!this.multiple && this.valueField &&
17580                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17581             {
17582                 // radio buttons..
17583                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17584                 row.addClass('selected');
17585             }
17586             
17587             if(this.multiple && this.valueField &&
17588                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17589             {
17590                 
17591                 // checkboxes...
17592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17593                 this.tickItems.push(d.data);
17594             }
17595             
17596             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17597             
17598         }, this);
17599         
17600         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17601         
17602         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17603
17604         if(this.modalTitle.length){
17605             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17606         }
17607
17608         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17609         
17610         if(this.mobile_restrict_height && listHeight < bodyHeight){
17611             this.touchViewBodyEl.setHeight(listHeight);
17612         }
17613         
17614         var _this = this;
17615         
17616         if(firstChecked && listHeight > bodyHeight){
17617             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17618         }
17619         
17620     },
17621     
17622     onTouchViewLoadException : function()
17623     {
17624         this.hideTouchView();
17625     },
17626     
17627     onTouchViewEmptyResults : function()
17628     {
17629         this.clearTouchView();
17630         
17631         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17632         
17633         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17634         
17635     },
17636     
17637     clearTouchView : function()
17638     {
17639         this.touchViewListGroup.dom.innerHTML = '';
17640     },
17641     
17642     onTouchViewClick : function(e, el, o)
17643     {
17644         e.preventDefault();
17645         
17646         var row = o.row;
17647         var rowIndex = o.rowIndex;
17648         
17649         var r = this.store.getAt(rowIndex);
17650         
17651         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17652             
17653             if(!this.multiple){
17654                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17655                     c.dom.removeAttribute('checked');
17656                 }, this);
17657
17658                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17659
17660                 this.setFromData(r.data);
17661
17662                 var close = this.closeTriggerEl();
17663
17664                 if(close){
17665                     close.show();
17666                 }
17667
17668                 this.hideTouchView();
17669
17670                 this.fireEvent('select', this, r, rowIndex);
17671
17672                 return;
17673             }
17674
17675             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17676                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17677                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17678                 return;
17679             }
17680
17681             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17682             this.addItem(r.data);
17683             this.tickItems.push(r.data);
17684         }
17685     },
17686     
17687     getAutoCreateNativeIOS : function()
17688     {
17689         var cfg = {
17690             cls: 'form-group' //input-group,
17691         };
17692         
17693         var combobox =  {
17694             tag: 'select',
17695             cls : 'roo-ios-select'
17696         };
17697         
17698         if (this.name) {
17699             combobox.name = this.name;
17700         }
17701         
17702         if (this.disabled) {
17703             combobox.disabled = true;
17704         }
17705         
17706         var settings = this;
17707         
17708         ['xs','sm','md','lg'].map(function(size){
17709             if (settings[size]) {
17710                 cfg.cls += ' col-' + size + '-' + settings[size];
17711             }
17712         });
17713         
17714         cfg.cn = combobox;
17715         
17716         return cfg;
17717         
17718     },
17719     
17720     initIOSView : function()
17721     {
17722         this.store.on('load', this.onIOSViewLoad, this);
17723         
17724         return;
17725     },
17726     
17727     onIOSViewLoad : function()
17728     {
17729         if(this.store.getCount() < 1){
17730             return;
17731         }
17732         
17733         this.clearIOSView();
17734         
17735         if(this.allowBlank) {
17736             
17737             var default_text = '-- SELECT --';
17738             
17739             if(this.placeholder.length){
17740                 default_text = this.placeholder;
17741             }
17742             
17743             if(this.emptyTitle.length){
17744                 default_text += ' - ' + this.emptyTitle + ' -';
17745             }
17746             
17747             var opt = this.inputEl().createChild({
17748                 tag: 'option',
17749                 value : 0,
17750                 html : default_text
17751             });
17752             
17753             var o = {};
17754             o[this.valueField] = 0;
17755             o[this.displayField] = default_text;
17756             
17757             this.ios_options.push({
17758                 data : o,
17759                 el : opt
17760             });
17761             
17762         }
17763         
17764         this.store.data.each(function(d, rowIndex){
17765             
17766             var html = '';
17767             
17768             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17769                 html = d.data[this.displayField];
17770             }
17771             
17772             var value = '';
17773             
17774             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17775                 value = d.data[this.valueField];
17776             }
17777             
17778             var option = {
17779                 tag: 'option',
17780                 value : value,
17781                 html : html
17782             };
17783             
17784             if(this.value == d.data[this.valueField]){
17785                 option['selected'] = true;
17786             }
17787             
17788             var opt = this.inputEl().createChild(option);
17789             
17790             this.ios_options.push({
17791                 data : d.data,
17792                 el : opt
17793             });
17794             
17795         }, this);
17796         
17797         this.inputEl().on('change', function(){
17798            this.fireEvent('select', this);
17799         }, this);
17800         
17801     },
17802     
17803     clearIOSView: function()
17804     {
17805         this.inputEl().dom.innerHTML = '';
17806         
17807         this.ios_options = [];
17808     },
17809     
17810     setIOSValue: function(v)
17811     {
17812         this.value = v;
17813         
17814         if(!this.ios_options){
17815             return;
17816         }
17817         
17818         Roo.each(this.ios_options, function(opts){
17819            
17820            opts.el.dom.removeAttribute('selected');
17821            
17822            if(opts.data[this.valueField] != v){
17823                return;
17824            }
17825            
17826            opts.el.dom.setAttribute('selected', true);
17827            
17828         }, this);
17829     }
17830
17831     /** 
17832     * @cfg {Boolean} grow 
17833     * @hide 
17834     */
17835     /** 
17836     * @cfg {Number} growMin 
17837     * @hide 
17838     */
17839     /** 
17840     * @cfg {Number} growMax 
17841     * @hide 
17842     */
17843     /**
17844      * @hide
17845      * @method autoSize
17846      */
17847 });
17848
17849 Roo.apply(Roo.bootstrap.ComboBox,  {
17850     
17851     header : {
17852         tag: 'div',
17853         cls: 'modal-header',
17854         cn: [
17855             {
17856                 tag: 'h4',
17857                 cls: 'modal-title'
17858             }
17859         ]
17860     },
17861     
17862     body : {
17863         tag: 'div',
17864         cls: 'modal-body',
17865         cn: [
17866             {
17867                 tag: 'ul',
17868                 cls: 'list-group'
17869             }
17870         ]
17871     },
17872     
17873     listItemRadio : {
17874         tag: 'li',
17875         cls: 'list-group-item',
17876         cn: [
17877             {
17878                 tag: 'span',
17879                 cls: 'roo-combobox-list-group-item-value'
17880             },
17881             {
17882                 tag: 'div',
17883                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17884                 cn: [
17885                     {
17886                         tag: 'input',
17887                         type: 'radio'
17888                     },
17889                     {
17890                         tag: 'label'
17891                     }
17892                 ]
17893             }
17894         ]
17895     },
17896     
17897     listItemCheckbox : {
17898         tag: 'li',
17899         cls: 'list-group-item',
17900         cn: [
17901             {
17902                 tag: 'span',
17903                 cls: 'roo-combobox-list-group-item-value'
17904             },
17905             {
17906                 tag: 'div',
17907                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17908                 cn: [
17909                     {
17910                         tag: 'input',
17911                         type: 'checkbox'
17912                     },
17913                     {
17914                         tag: 'label'
17915                     }
17916                 ]
17917             }
17918         ]
17919     },
17920     
17921     emptyResult : {
17922         tag: 'div',
17923         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17924     },
17925     
17926     footer : {
17927         tag: 'div',
17928         cls: 'modal-footer',
17929         cn: [
17930             {
17931                 tag: 'div',
17932                 cls: 'row',
17933                 cn: [
17934                     {
17935                         tag: 'div',
17936                         cls: 'col-xs-6 text-left',
17937                         cn: {
17938                             tag: 'button',
17939                             cls: 'btn btn-danger roo-touch-view-cancel',
17940                             html: 'Cancel'
17941                         }
17942                     },
17943                     {
17944                         tag: 'div',
17945                         cls: 'col-xs-6 text-right',
17946                         cn: {
17947                             tag: 'button',
17948                             cls: 'btn btn-success roo-touch-view-ok',
17949                             html: 'OK'
17950                         }
17951                     }
17952                 ]
17953             }
17954         ]
17955         
17956     }
17957 });
17958
17959 Roo.apply(Roo.bootstrap.ComboBox,  {
17960     
17961     touchViewTemplate : {
17962         tag: 'div',
17963         cls: 'modal fade roo-combobox-touch-view',
17964         cn: [
17965             {
17966                 tag: 'div',
17967                 cls: 'modal-dialog',
17968                 style : 'position:fixed', // we have to fix position....
17969                 cn: [
17970                     {
17971                         tag: 'div',
17972                         cls: 'modal-content',
17973                         cn: [
17974                             Roo.bootstrap.ComboBox.header,
17975                             Roo.bootstrap.ComboBox.body,
17976                             Roo.bootstrap.ComboBox.footer
17977                         ]
17978                     }
17979                 ]
17980             }
17981         ]
17982     }
17983 });/*
17984  * Based on:
17985  * Ext JS Library 1.1.1
17986  * Copyright(c) 2006-2007, Ext JS, LLC.
17987  *
17988  * Originally Released Under LGPL - original licence link has changed is not relivant.
17989  *
17990  * Fork - LGPL
17991  * <script type="text/javascript">
17992  */
17993
17994 /**
17995  * @class Roo.View
17996  * @extends Roo.util.Observable
17997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17998  * This class also supports single and multi selection modes. <br>
17999  * Create a data model bound view:
18000  <pre><code>
18001  var store = new Roo.data.Store(...);
18002
18003  var view = new Roo.View({
18004     el : "my-element",
18005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18006  
18007     singleSelect: true,
18008     selectedClass: "ydataview-selected",
18009     store: store
18010  });
18011
18012  // listen for node click?
18013  view.on("click", function(vw, index, node, e){
18014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18015  });
18016
18017  // load XML data
18018  dataModel.load("foobar.xml");
18019  </code></pre>
18020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18021  * <br><br>
18022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18024  * 
18025  * Note: old style constructor is still suported (container, template, config)
18026  * 
18027  * @constructor
18028  * Create a new View
18029  * @param {Object} config The config object
18030  * 
18031  */
18032 Roo.View = function(config, depreciated_tpl, depreciated_config){
18033     
18034     this.parent = false;
18035     
18036     if (typeof(depreciated_tpl) == 'undefined') {
18037         // new way.. - universal constructor.
18038         Roo.apply(this, config);
18039         this.el  = Roo.get(this.el);
18040     } else {
18041         // old format..
18042         this.el  = Roo.get(config);
18043         this.tpl = depreciated_tpl;
18044         Roo.apply(this, depreciated_config);
18045     }
18046     this.wrapEl  = this.el.wrap().wrap();
18047     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18048     
18049     
18050     if(typeof(this.tpl) == "string"){
18051         this.tpl = new Roo.Template(this.tpl);
18052     } else {
18053         // support xtype ctors..
18054         this.tpl = new Roo.factory(this.tpl, Roo);
18055     }
18056     
18057     
18058     this.tpl.compile();
18059     
18060     /** @private */
18061     this.addEvents({
18062         /**
18063          * @event beforeclick
18064          * Fires before a click is processed. Returns false to cancel the default action.
18065          * @param {Roo.View} this
18066          * @param {Number} index The index of the target node
18067          * @param {HTMLElement} node The target node
18068          * @param {Roo.EventObject} e The raw event object
18069          */
18070             "beforeclick" : true,
18071         /**
18072          * @event click
18073          * Fires when a template node is clicked.
18074          * @param {Roo.View} this
18075          * @param {Number} index The index of the target node
18076          * @param {HTMLElement} node The target node
18077          * @param {Roo.EventObject} e The raw event object
18078          */
18079             "click" : true,
18080         /**
18081          * @event dblclick
18082          * Fires when a template node is double clicked.
18083          * @param {Roo.View} this
18084          * @param {Number} index The index of the target node
18085          * @param {HTMLElement} node The target node
18086          * @param {Roo.EventObject} e The raw event object
18087          */
18088             "dblclick" : true,
18089         /**
18090          * @event contextmenu
18091          * Fires when a template node is right clicked.
18092          * @param {Roo.View} this
18093          * @param {Number} index The index of the target node
18094          * @param {HTMLElement} node The target node
18095          * @param {Roo.EventObject} e The raw event object
18096          */
18097             "contextmenu" : true,
18098         /**
18099          * @event selectionchange
18100          * Fires when the selected nodes change.
18101          * @param {Roo.View} this
18102          * @param {Array} selections Array of the selected nodes
18103          */
18104             "selectionchange" : true,
18105     
18106         /**
18107          * @event beforeselect
18108          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18109          * @param {Roo.View} this
18110          * @param {HTMLElement} node The node to be selected
18111          * @param {Array} selections Array of currently selected nodes
18112          */
18113             "beforeselect" : true,
18114         /**
18115          * @event preparedata
18116          * Fires on every row to render, to allow you to change the data.
18117          * @param {Roo.View} this
18118          * @param {Object} data to be rendered (change this)
18119          */
18120           "preparedata" : true
18121           
18122           
18123         });
18124
18125
18126
18127     this.el.on({
18128         "click": this.onClick,
18129         "dblclick": this.onDblClick,
18130         "contextmenu": this.onContextMenu,
18131         scope:this
18132     });
18133
18134     this.selections = [];
18135     this.nodes = [];
18136     this.cmp = new Roo.CompositeElementLite([]);
18137     if(this.store){
18138         this.store = Roo.factory(this.store, Roo.data);
18139         this.setStore(this.store, true);
18140     }
18141     
18142     if ( this.footer && this.footer.xtype) {
18143            
18144          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18145         
18146         this.footer.dataSource = this.store;
18147         this.footer.container = fctr;
18148         this.footer = Roo.factory(this.footer, Roo);
18149         fctr.insertFirst(this.el);
18150         
18151         // this is a bit insane - as the paging toolbar seems to detach the el..
18152 //        dom.parentNode.parentNode.parentNode
18153          // they get detached?
18154     }
18155     
18156     
18157     Roo.View.superclass.constructor.call(this);
18158     
18159     
18160 };
18161
18162 Roo.extend(Roo.View, Roo.util.Observable, {
18163     
18164      /**
18165      * @cfg {Roo.data.Store} store Data store to load data from.
18166      */
18167     store : false,
18168     
18169     /**
18170      * @cfg {String|Roo.Element} el The container element.
18171      */
18172     el : '',
18173     
18174     /**
18175      * @cfg {String|Roo.Template} tpl The template used by this View 
18176      */
18177     tpl : false,
18178     /**
18179      * @cfg {String} dataName the named area of the template to use as the data area
18180      *                          Works with domtemplates roo-name="name"
18181      */
18182     dataName: false,
18183     /**
18184      * @cfg {String} selectedClass The css class to add to selected nodes
18185      */
18186     selectedClass : "x-view-selected",
18187      /**
18188      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18189      */
18190     emptyText : "",
18191     
18192     /**
18193      * @cfg {String} text to display on mask (default Loading)
18194      */
18195     mask : false,
18196     /**
18197      * @cfg {Boolean} multiSelect Allow multiple selection
18198      */
18199     multiSelect : false,
18200     /**
18201      * @cfg {Boolean} singleSelect Allow single selection
18202      */
18203     singleSelect:  false,
18204     
18205     /**
18206      * @cfg {Boolean} toggleSelect - selecting 
18207      */
18208     toggleSelect : false,
18209     
18210     /**
18211      * @cfg {Boolean} tickable - selecting 
18212      */
18213     tickable : false,
18214     
18215     /**
18216      * Returns the element this view is bound to.
18217      * @return {Roo.Element}
18218      */
18219     getEl : function(){
18220         return this.wrapEl;
18221     },
18222     
18223     
18224
18225     /**
18226      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18227      */
18228     refresh : function(){
18229         //Roo.log('refresh');
18230         var t = this.tpl;
18231         
18232         // if we are using something like 'domtemplate', then
18233         // the what gets used is:
18234         // t.applySubtemplate(NAME, data, wrapping data..)
18235         // the outer template then get' applied with
18236         //     the store 'extra data'
18237         // and the body get's added to the
18238         //      roo-name="data" node?
18239         //      <span class='roo-tpl-{name}'></span> ?????
18240         
18241         
18242         
18243         this.clearSelections();
18244         this.el.update("");
18245         var html = [];
18246         var records = this.store.getRange();
18247         if(records.length < 1) {
18248             
18249             // is this valid??  = should it render a template??
18250             
18251             this.el.update(this.emptyText);
18252             return;
18253         }
18254         var el = this.el;
18255         if (this.dataName) {
18256             this.el.update(t.apply(this.store.meta)); //????
18257             el = this.el.child('.roo-tpl-' + this.dataName);
18258         }
18259         
18260         for(var i = 0, len = records.length; i < len; i++){
18261             var data = this.prepareData(records[i].data, i, records[i]);
18262             this.fireEvent("preparedata", this, data, i, records[i]);
18263             
18264             var d = Roo.apply({}, data);
18265             
18266             if(this.tickable){
18267                 Roo.apply(d, {'roo-id' : Roo.id()});
18268                 
18269                 var _this = this;
18270             
18271                 Roo.each(this.parent.item, function(item){
18272                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18273                         return;
18274                     }
18275                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18276                 });
18277             }
18278             
18279             html[html.length] = Roo.util.Format.trim(
18280                 this.dataName ?
18281                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18282                     t.apply(d)
18283             );
18284         }
18285         
18286         
18287         
18288         el.update(html.join(""));
18289         this.nodes = el.dom.childNodes;
18290         this.updateIndexes(0);
18291     },
18292     
18293
18294     /**
18295      * Function to override to reformat the data that is sent to
18296      * the template for each node.
18297      * DEPRICATED - use the preparedata event handler.
18298      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18299      * a JSON object for an UpdateManager bound view).
18300      */
18301     prepareData : function(data, index, record)
18302     {
18303         this.fireEvent("preparedata", this, data, index, record);
18304         return data;
18305     },
18306
18307     onUpdate : function(ds, record){
18308         // Roo.log('on update');   
18309         this.clearSelections();
18310         var index = this.store.indexOf(record);
18311         var n = this.nodes[index];
18312         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18313         n.parentNode.removeChild(n);
18314         this.updateIndexes(index, index);
18315     },
18316
18317     
18318     
18319 // --------- FIXME     
18320     onAdd : function(ds, records, index)
18321     {
18322         //Roo.log(['on Add', ds, records, index] );        
18323         this.clearSelections();
18324         if(this.nodes.length == 0){
18325             this.refresh();
18326             return;
18327         }
18328         var n = this.nodes[index];
18329         for(var i = 0, len = records.length; i < len; i++){
18330             var d = this.prepareData(records[i].data, i, records[i]);
18331             if(n){
18332                 this.tpl.insertBefore(n, d);
18333             }else{
18334                 
18335                 this.tpl.append(this.el, d);
18336             }
18337         }
18338         this.updateIndexes(index);
18339     },
18340
18341     onRemove : function(ds, record, index){
18342        // Roo.log('onRemove');
18343         this.clearSelections();
18344         var el = this.dataName  ?
18345             this.el.child('.roo-tpl-' + this.dataName) :
18346             this.el; 
18347         
18348         el.dom.removeChild(this.nodes[index]);
18349         this.updateIndexes(index);
18350     },
18351
18352     /**
18353      * Refresh an individual node.
18354      * @param {Number} index
18355      */
18356     refreshNode : function(index){
18357         this.onUpdate(this.store, this.store.getAt(index));
18358     },
18359
18360     updateIndexes : function(startIndex, endIndex){
18361         var ns = this.nodes;
18362         startIndex = startIndex || 0;
18363         endIndex = endIndex || ns.length - 1;
18364         for(var i = startIndex; i <= endIndex; i++){
18365             ns[i].nodeIndex = i;
18366         }
18367     },
18368
18369     /**
18370      * Changes the data store this view uses and refresh the view.
18371      * @param {Store} store
18372      */
18373     setStore : function(store, initial){
18374         if(!initial && this.store){
18375             this.store.un("datachanged", this.refresh);
18376             this.store.un("add", this.onAdd);
18377             this.store.un("remove", this.onRemove);
18378             this.store.un("update", this.onUpdate);
18379             this.store.un("clear", this.refresh);
18380             this.store.un("beforeload", this.onBeforeLoad);
18381             this.store.un("load", this.onLoad);
18382             this.store.un("loadexception", this.onLoad);
18383         }
18384         if(store){
18385           
18386             store.on("datachanged", this.refresh, this);
18387             store.on("add", this.onAdd, this);
18388             store.on("remove", this.onRemove, this);
18389             store.on("update", this.onUpdate, this);
18390             store.on("clear", this.refresh, this);
18391             store.on("beforeload", this.onBeforeLoad, this);
18392             store.on("load", this.onLoad, this);
18393             store.on("loadexception", this.onLoad, this);
18394         }
18395         
18396         if(store){
18397             this.refresh();
18398         }
18399     },
18400     /**
18401      * onbeforeLoad - masks the loading area.
18402      *
18403      */
18404     onBeforeLoad : function(store,opts)
18405     {
18406          //Roo.log('onBeforeLoad');   
18407         if (!opts.add) {
18408             this.el.update("");
18409         }
18410         this.el.mask(this.mask ? this.mask : "Loading" ); 
18411     },
18412     onLoad : function ()
18413     {
18414         this.el.unmask();
18415     },
18416     
18417
18418     /**
18419      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18420      * @param {HTMLElement} node
18421      * @return {HTMLElement} The template node
18422      */
18423     findItemFromChild : function(node){
18424         var el = this.dataName  ?
18425             this.el.child('.roo-tpl-' + this.dataName,true) :
18426             this.el.dom; 
18427         
18428         if(!node || node.parentNode == el){
18429                     return node;
18430             }
18431             var p = node.parentNode;
18432             while(p && p != el){
18433             if(p.parentNode == el){
18434                 return p;
18435             }
18436             p = p.parentNode;
18437         }
18438             return null;
18439     },
18440
18441     /** @ignore */
18442     onClick : function(e){
18443         var item = this.findItemFromChild(e.getTarget());
18444         if(item){
18445             var index = this.indexOf(item);
18446             if(this.onItemClick(item, index, e) !== false){
18447                 this.fireEvent("click", this, index, item, e);
18448             }
18449         }else{
18450             this.clearSelections();
18451         }
18452     },
18453
18454     /** @ignore */
18455     onContextMenu : function(e){
18456         var item = this.findItemFromChild(e.getTarget());
18457         if(item){
18458             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18459         }
18460     },
18461
18462     /** @ignore */
18463     onDblClick : function(e){
18464         var item = this.findItemFromChild(e.getTarget());
18465         if(item){
18466             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18467         }
18468     },
18469
18470     onItemClick : function(item, index, e)
18471     {
18472         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18473             return false;
18474         }
18475         if (this.toggleSelect) {
18476             var m = this.isSelected(item) ? 'unselect' : 'select';
18477             //Roo.log(m);
18478             var _t = this;
18479             _t[m](item, true, false);
18480             return true;
18481         }
18482         if(this.multiSelect || this.singleSelect){
18483             if(this.multiSelect && e.shiftKey && this.lastSelection){
18484                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18485             }else{
18486                 this.select(item, this.multiSelect && e.ctrlKey);
18487                 this.lastSelection = item;
18488             }
18489             
18490             if(!this.tickable){
18491                 e.preventDefault();
18492             }
18493             
18494         }
18495         return true;
18496     },
18497
18498     /**
18499      * Get the number of selected nodes.
18500      * @return {Number}
18501      */
18502     getSelectionCount : function(){
18503         return this.selections.length;
18504     },
18505
18506     /**
18507      * Get the currently selected nodes.
18508      * @return {Array} An array of HTMLElements
18509      */
18510     getSelectedNodes : function(){
18511         return this.selections;
18512     },
18513
18514     /**
18515      * Get the indexes of the selected nodes.
18516      * @return {Array}
18517      */
18518     getSelectedIndexes : function(){
18519         var indexes = [], s = this.selections;
18520         for(var i = 0, len = s.length; i < len; i++){
18521             indexes.push(s[i].nodeIndex);
18522         }
18523         return indexes;
18524     },
18525
18526     /**
18527      * Clear all selections
18528      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18529      */
18530     clearSelections : function(suppressEvent){
18531         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18532             this.cmp.elements = this.selections;
18533             this.cmp.removeClass(this.selectedClass);
18534             this.selections = [];
18535             if(!suppressEvent){
18536                 this.fireEvent("selectionchange", this, this.selections);
18537             }
18538         }
18539     },
18540
18541     /**
18542      * Returns true if the passed node is selected
18543      * @param {HTMLElement/Number} node The node or node index
18544      * @return {Boolean}
18545      */
18546     isSelected : function(node){
18547         var s = this.selections;
18548         if(s.length < 1){
18549             return false;
18550         }
18551         node = this.getNode(node);
18552         return s.indexOf(node) !== -1;
18553     },
18554
18555     /**
18556      * Selects nodes.
18557      * @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
18558      * @param {Boolean} keepExisting (optional) true to keep existing selections
18559      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18560      */
18561     select : function(nodeInfo, keepExisting, suppressEvent){
18562         if(nodeInfo instanceof Array){
18563             if(!keepExisting){
18564                 this.clearSelections(true);
18565             }
18566             for(var i = 0, len = nodeInfo.length; i < len; i++){
18567                 this.select(nodeInfo[i], true, true);
18568             }
18569             return;
18570         } 
18571         var node = this.getNode(nodeInfo);
18572         if(!node || this.isSelected(node)){
18573             return; // already selected.
18574         }
18575         if(!keepExisting){
18576             this.clearSelections(true);
18577         }
18578         
18579         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18580             Roo.fly(node).addClass(this.selectedClass);
18581             this.selections.push(node);
18582             if(!suppressEvent){
18583                 this.fireEvent("selectionchange", this, this.selections);
18584             }
18585         }
18586         
18587         
18588     },
18589       /**
18590      * Unselects nodes.
18591      * @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
18592      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18594      */
18595     unselect : function(nodeInfo, keepExisting, suppressEvent)
18596     {
18597         if(nodeInfo instanceof Array){
18598             Roo.each(this.selections, function(s) {
18599                 this.unselect(s, nodeInfo);
18600             }, this);
18601             return;
18602         }
18603         var node = this.getNode(nodeInfo);
18604         if(!node || !this.isSelected(node)){
18605             //Roo.log("not selected");
18606             return; // not selected.
18607         }
18608         // fireevent???
18609         var ns = [];
18610         Roo.each(this.selections, function(s) {
18611             if (s == node ) {
18612                 Roo.fly(node).removeClass(this.selectedClass);
18613
18614                 return;
18615             }
18616             ns.push(s);
18617         },this);
18618         
18619         this.selections= ns;
18620         this.fireEvent("selectionchange", this, this.selections);
18621     },
18622
18623     /**
18624      * Gets a template node.
18625      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18626      * @return {HTMLElement} The node or null if it wasn't found
18627      */
18628     getNode : function(nodeInfo){
18629         if(typeof nodeInfo == "string"){
18630             return document.getElementById(nodeInfo);
18631         }else if(typeof nodeInfo == "number"){
18632             return this.nodes[nodeInfo];
18633         }
18634         return nodeInfo;
18635     },
18636
18637     /**
18638      * Gets a range template nodes.
18639      * @param {Number} startIndex
18640      * @param {Number} endIndex
18641      * @return {Array} An array of nodes
18642      */
18643     getNodes : function(start, end){
18644         var ns = this.nodes;
18645         start = start || 0;
18646         end = typeof end == "undefined" ? ns.length - 1 : end;
18647         var nodes = [];
18648         if(start <= end){
18649             for(var i = start; i <= end; i++){
18650                 nodes.push(ns[i]);
18651             }
18652         } else{
18653             for(var i = start; i >= end; i--){
18654                 nodes.push(ns[i]);
18655             }
18656         }
18657         return nodes;
18658     },
18659
18660     /**
18661      * Finds the index of the passed node
18662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18663      * @return {Number} The index of the node or -1
18664      */
18665     indexOf : function(node){
18666         node = this.getNode(node);
18667         if(typeof node.nodeIndex == "number"){
18668             return node.nodeIndex;
18669         }
18670         var ns = this.nodes;
18671         for(var i = 0, len = ns.length; i < len; i++){
18672             if(ns[i] == node){
18673                 return i;
18674             }
18675         }
18676         return -1;
18677     }
18678 });
18679 /*
18680  * - LGPL
18681  *
18682  * based on jquery fullcalendar
18683  * 
18684  */
18685
18686 Roo.bootstrap = Roo.bootstrap || {};
18687 /**
18688  * @class Roo.bootstrap.Calendar
18689  * @extends Roo.bootstrap.Component
18690  * Bootstrap Calendar class
18691  * @cfg {Boolean} loadMask (true|false) default false
18692  * @cfg {Object} header generate the user specific header of the calendar, default false
18693
18694  * @constructor
18695  * Create a new Container
18696  * @param {Object} config The config object
18697  */
18698
18699
18700
18701 Roo.bootstrap.Calendar = function(config){
18702     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18703      this.addEvents({
18704         /**
18705              * @event select
18706              * Fires when a date is selected
18707              * @param {DatePicker} this
18708              * @param {Date} date The selected date
18709              */
18710         'select': true,
18711         /**
18712              * @event monthchange
18713              * Fires when the displayed month changes 
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected month
18716              */
18717         'monthchange': true,
18718         /**
18719              * @event evententer
18720              * Fires when mouse over an event
18721              * @param {Calendar} this
18722              * @param {event} Event
18723              */
18724         'evententer': true,
18725         /**
18726              * @event eventleave
18727              * Fires when the mouse leaves an
18728              * @param {Calendar} this
18729              * @param {event}
18730              */
18731         'eventleave': true,
18732         /**
18733              * @event eventclick
18734              * Fires when the mouse click an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventclick': true
18739         
18740     });
18741
18742 };
18743
18744 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18745     
18746      /**
18747      * @cfg {Number} startDay
18748      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18749      */
18750     startDay : 0,
18751     
18752     loadMask : false,
18753     
18754     header : false,
18755       
18756     getAutoCreate : function(){
18757         
18758         
18759         var fc_button = function(name, corner, style, content ) {
18760             return Roo.apply({},{
18761                 tag : 'span',
18762                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18763                          (corner.length ?
18764                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18765                             ''
18766                         ),
18767                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18768                 unselectable: 'on'
18769             });
18770         };
18771         
18772         var header = {};
18773         
18774         if(!this.header){
18775             header = {
18776                 tag : 'table',
18777                 cls : 'fc-header',
18778                 style : 'width:100%',
18779                 cn : [
18780                     {
18781                         tag: 'tr',
18782                         cn : [
18783                             {
18784                                 tag : 'td',
18785                                 cls : 'fc-header-left',
18786                                 cn : [
18787                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18788                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18789                                     { tag: 'span', cls: 'fc-header-space' },
18790                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18791
18792
18793                                 ]
18794                             },
18795
18796                             {
18797                                 tag : 'td',
18798                                 cls : 'fc-header-center',
18799                                 cn : [
18800                                     {
18801                                         tag: 'span',
18802                                         cls: 'fc-header-title',
18803                                         cn : {
18804                                             tag: 'H2',
18805                                             html : 'month / year'
18806                                         }
18807                                     }
18808
18809                                 ]
18810                             },
18811                             {
18812                                 tag : 'td',
18813                                 cls : 'fc-header-right',
18814                                 cn : [
18815                               /*      fc_button('month', 'left', '', 'month' ),
18816                                     fc_button('week', '', '', 'week' ),
18817                                     fc_button('day', 'right', '', 'day' )
18818                                 */    
18819
18820                                 ]
18821                             }
18822
18823                         ]
18824                     }
18825                 ]
18826             };
18827         }
18828         
18829         header = this.header;
18830         
18831        
18832         var cal_heads = function() {
18833             var ret = [];
18834             // fixme - handle this.
18835             
18836             for (var i =0; i < Date.dayNames.length; i++) {
18837                 var d = Date.dayNames[i];
18838                 ret.push({
18839                     tag: 'th',
18840                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18841                     html : d.substring(0,3)
18842                 });
18843                 
18844             }
18845             ret[0].cls += ' fc-first';
18846             ret[6].cls += ' fc-last';
18847             return ret;
18848         };
18849         var cal_cell = function(n) {
18850             return  {
18851                 tag: 'td',
18852                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18853                 cn : [
18854                     {
18855                         cn : [
18856                             {
18857                                 cls: 'fc-day-number',
18858                                 html: 'D'
18859                             },
18860                             {
18861                                 cls: 'fc-day-content',
18862                              
18863                                 cn : [
18864                                      {
18865                                         style: 'position: relative;' // height: 17px;
18866                                     }
18867                                 ]
18868                             }
18869                             
18870                             
18871                         ]
18872                     }
18873                 ]
18874                 
18875             }
18876         };
18877         var cal_rows = function() {
18878             
18879             var ret = [];
18880             for (var r = 0; r < 6; r++) {
18881                 var row= {
18882                     tag : 'tr',
18883                     cls : 'fc-week',
18884                     cn : []
18885                 };
18886                 
18887                 for (var i =0; i < Date.dayNames.length; i++) {
18888                     var d = Date.dayNames[i];
18889                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18890
18891                 }
18892                 row.cn[0].cls+=' fc-first';
18893                 row.cn[0].cn[0].style = 'min-height:90px';
18894                 row.cn[6].cls+=' fc-last';
18895                 ret.push(row);
18896                 
18897             }
18898             ret[0].cls += ' fc-first';
18899             ret[4].cls += ' fc-prev-last';
18900             ret[5].cls += ' fc-last';
18901             return ret;
18902             
18903         };
18904         
18905         var cal_table = {
18906             tag: 'table',
18907             cls: 'fc-border-separate',
18908             style : 'width:100%',
18909             cellspacing  : 0,
18910             cn : [
18911                 { 
18912                     tag: 'thead',
18913                     cn : [
18914                         { 
18915                             tag: 'tr',
18916                             cls : 'fc-first fc-last',
18917                             cn : cal_heads()
18918                         }
18919                     ]
18920                 },
18921                 { 
18922                     tag: 'tbody',
18923                     cn : cal_rows()
18924                 }
18925                   
18926             ]
18927         };
18928          
18929          var cfg = {
18930             cls : 'fc fc-ltr',
18931             cn : [
18932                 header,
18933                 {
18934                     cls : 'fc-content',
18935                     style : "position: relative;",
18936                     cn : [
18937                         {
18938                             cls : 'fc-view fc-view-month fc-grid',
18939                             style : 'position: relative',
18940                             unselectable : 'on',
18941                             cn : [
18942                                 {
18943                                     cls : 'fc-event-container',
18944                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18945                                 },
18946                                 cal_table
18947                             ]
18948                         }
18949                     ]
18950     
18951                 }
18952            ] 
18953             
18954         };
18955         
18956          
18957         
18958         return cfg;
18959     },
18960     
18961     
18962     initEvents : function()
18963     {
18964         if(!this.store){
18965             throw "can not find store for calendar";
18966         }
18967         
18968         var mark = {
18969             tag: "div",
18970             cls:"x-dlg-mask",
18971             style: "text-align:center",
18972             cn: [
18973                 {
18974                     tag: "div",
18975                     style: "background-color:white;width:50%;margin:250 auto",
18976                     cn: [
18977                         {
18978                             tag: "img",
18979                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18980                         },
18981                         {
18982                             tag: "span",
18983                             html: "Loading"
18984                         }
18985                         
18986                     ]
18987                 }
18988             ]
18989         };
18990         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18991         
18992         var size = this.el.select('.fc-content', true).first().getSize();
18993         this.maskEl.setSize(size.width, size.height);
18994         this.maskEl.enableDisplayMode("block");
18995         if(!this.loadMask){
18996             this.maskEl.hide();
18997         }
18998         
18999         this.store = Roo.factory(this.store, Roo.data);
19000         this.store.on('load', this.onLoad, this);
19001         this.store.on('beforeload', this.onBeforeLoad, this);
19002         
19003         this.resize();
19004         
19005         this.cells = this.el.select('.fc-day',true);
19006         //Roo.log(this.cells);
19007         this.textNodes = this.el.query('.fc-day-number');
19008         this.cells.addClassOnOver('fc-state-hover');
19009         
19010         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19011         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19012         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19013         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19014         
19015         this.on('monthchange', this.onMonthChange, this);
19016         
19017         this.update(new Date().clearTime());
19018     },
19019     
19020     resize : function() {
19021         var sz  = this.el.getSize();
19022         
19023         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19024         this.el.select('.fc-day-content div',true).setHeight(34);
19025     },
19026     
19027     
19028     // private
19029     showPrevMonth : function(e){
19030         this.update(this.activeDate.add("mo", -1));
19031     },
19032     showToday : function(e){
19033         this.update(new Date().clearTime());
19034     },
19035     // private
19036     showNextMonth : function(e){
19037         this.update(this.activeDate.add("mo", 1));
19038     },
19039
19040     // private
19041     showPrevYear : function(){
19042         this.update(this.activeDate.add("y", -1));
19043     },
19044
19045     // private
19046     showNextYear : function(){
19047         this.update(this.activeDate.add("y", 1));
19048     },
19049
19050     
19051    // private
19052     update : function(date)
19053     {
19054         var vd = this.activeDate;
19055         this.activeDate = date;
19056 //        if(vd && this.el){
19057 //            var t = date.getTime();
19058 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19059 //                Roo.log('using add remove');
19060 //                
19061 //                this.fireEvent('monthchange', this, date);
19062 //                
19063 //                this.cells.removeClass("fc-state-highlight");
19064 //                this.cells.each(function(c){
19065 //                   if(c.dateValue == t){
19066 //                       c.addClass("fc-state-highlight");
19067 //                       setTimeout(function(){
19068 //                            try{c.dom.firstChild.focus();}catch(e){}
19069 //                       }, 50);
19070 //                       return false;
19071 //                   }
19072 //                   return true;
19073 //                });
19074 //                return;
19075 //            }
19076 //        }
19077         
19078         var days = date.getDaysInMonth();
19079         
19080         var firstOfMonth = date.getFirstDateOfMonth();
19081         var startingPos = firstOfMonth.getDay()-this.startDay;
19082         
19083         if(startingPos < this.startDay){
19084             startingPos += 7;
19085         }
19086         
19087         var pm = date.add(Date.MONTH, -1);
19088         var prevStart = pm.getDaysInMonth()-startingPos;
19089 //        
19090         this.cells = this.el.select('.fc-day',true);
19091         this.textNodes = this.el.query('.fc-day-number');
19092         this.cells.addClassOnOver('fc-state-hover');
19093         
19094         var cells = this.cells.elements;
19095         var textEls = this.textNodes;
19096         
19097         Roo.each(cells, function(cell){
19098             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19099         });
19100         
19101         days += startingPos;
19102
19103         // convert everything to numbers so it's fast
19104         var day = 86400000;
19105         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19106         //Roo.log(d);
19107         //Roo.log(pm);
19108         //Roo.log(prevStart);
19109         
19110         var today = new Date().clearTime().getTime();
19111         var sel = date.clearTime().getTime();
19112         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19113         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19114         var ddMatch = this.disabledDatesRE;
19115         var ddText = this.disabledDatesText;
19116         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19117         var ddaysText = this.disabledDaysText;
19118         var format = this.format;
19119         
19120         var setCellClass = function(cal, cell){
19121             cell.row = 0;
19122             cell.events = [];
19123             cell.more = [];
19124             //Roo.log('set Cell Class');
19125             cell.title = "";
19126             var t = d.getTime();
19127             
19128             //Roo.log(d);
19129             
19130             cell.dateValue = t;
19131             if(t == today){
19132                 cell.className += " fc-today";
19133                 cell.className += " fc-state-highlight";
19134                 cell.title = cal.todayText;
19135             }
19136             if(t == sel){
19137                 // disable highlight in other month..
19138                 //cell.className += " fc-state-highlight";
19139                 
19140             }
19141             // disabling
19142             if(t < min) {
19143                 cell.className = " fc-state-disabled";
19144                 cell.title = cal.minText;
19145                 return;
19146             }
19147             if(t > max) {
19148                 cell.className = " fc-state-disabled";
19149                 cell.title = cal.maxText;
19150                 return;
19151             }
19152             if(ddays){
19153                 if(ddays.indexOf(d.getDay()) != -1){
19154                     cell.title = ddaysText;
19155                     cell.className = " fc-state-disabled";
19156                 }
19157             }
19158             if(ddMatch && format){
19159                 var fvalue = d.dateFormat(format);
19160                 if(ddMatch.test(fvalue)){
19161                     cell.title = ddText.replace("%0", fvalue);
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             
19166             if (!cell.initialClassName) {
19167                 cell.initialClassName = cell.dom.className;
19168             }
19169             
19170             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19171         };
19172
19173         var i = 0;
19174         
19175         for(; i < startingPos; i++) {
19176             textEls[i].innerHTML = (++prevStart);
19177             d.setDate(d.getDate()+1);
19178             
19179             cells[i].className = "fc-past fc-other-month";
19180             setCellClass(this, cells[i]);
19181         }
19182         
19183         var intDay = 0;
19184         
19185         for(; i < days; i++){
19186             intDay = i - startingPos + 1;
19187             textEls[i].innerHTML = (intDay);
19188             d.setDate(d.getDate()+1);
19189             
19190             cells[i].className = ''; // "x-date-active";
19191             setCellClass(this, cells[i]);
19192         }
19193         var extraDays = 0;
19194         
19195         for(; i < 42; i++) {
19196             textEls[i].innerHTML = (++extraDays);
19197             d.setDate(d.getDate()+1);
19198             
19199             cells[i].className = "fc-future fc-other-month";
19200             setCellClass(this, cells[i]);
19201         }
19202         
19203         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19204         
19205         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19206         
19207         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19208         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19209         
19210         if(totalRows != 6){
19211             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19212             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19213         }
19214         
19215         this.fireEvent('monthchange', this, date);
19216         
19217         
19218         /*
19219         if(!this.internalRender){
19220             var main = this.el.dom.firstChild;
19221             var w = main.offsetWidth;
19222             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19223             Roo.fly(main).setWidth(w);
19224             this.internalRender = true;
19225             // opera does not respect the auto grow header center column
19226             // then, after it gets a width opera refuses to recalculate
19227             // without a second pass
19228             if(Roo.isOpera && !this.secondPass){
19229                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19230                 this.secondPass = true;
19231                 this.update.defer(10, this, [date]);
19232             }
19233         }
19234         */
19235         
19236     },
19237     
19238     findCell : function(dt) {
19239         dt = dt.clearTime().getTime();
19240         var ret = false;
19241         this.cells.each(function(c){
19242             //Roo.log("check " +c.dateValue + '?=' + dt);
19243             if(c.dateValue == dt){
19244                 ret = c;
19245                 return false;
19246             }
19247             return true;
19248         });
19249         
19250         return ret;
19251     },
19252     
19253     findCells : function(ev) {
19254         var s = ev.start.clone().clearTime().getTime();
19255        // Roo.log(s);
19256         var e= ev.end.clone().clearTime().getTime();
19257        // Roo.log(e);
19258         var ret = [];
19259         this.cells.each(function(c){
19260              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19261             
19262             if(c.dateValue > e){
19263                 return ;
19264             }
19265             if(c.dateValue < s){
19266                 return ;
19267             }
19268             ret.push(c);
19269         });
19270         
19271         return ret;    
19272     },
19273     
19274 //    findBestRow: function(cells)
19275 //    {
19276 //        var ret = 0;
19277 //        
19278 //        for (var i =0 ; i < cells.length;i++) {
19279 //            ret  = Math.max(cells[i].rows || 0,ret);
19280 //        }
19281 //        return ret;
19282 //        
19283 //    },
19284     
19285     
19286     addItem : function(ev)
19287     {
19288         // look for vertical location slot in
19289         var cells = this.findCells(ev);
19290         
19291 //        ev.row = this.findBestRow(cells);
19292         
19293         // work out the location.
19294         
19295         var crow = false;
19296         var rows = [];
19297         for(var i =0; i < cells.length; i++) {
19298             
19299             cells[i].row = cells[0].row;
19300             
19301             if(i == 0){
19302                 cells[i].row = cells[i].row + 1;
19303             }
19304             
19305             if (!crow) {
19306                 crow = {
19307                     start : cells[i],
19308                     end :  cells[i]
19309                 };
19310                 continue;
19311             }
19312             if (crow.start.getY() == cells[i].getY()) {
19313                 // on same row.
19314                 crow.end = cells[i];
19315                 continue;
19316             }
19317             // different row.
19318             rows.push(crow);
19319             crow = {
19320                 start: cells[i],
19321                 end : cells[i]
19322             };
19323             
19324         }
19325         
19326         rows.push(crow);
19327         ev.els = [];
19328         ev.rows = rows;
19329         ev.cells = cells;
19330         
19331         cells[0].events.push(ev);
19332         
19333         this.calevents.push(ev);
19334     },
19335     
19336     clearEvents: function() {
19337         
19338         if(!this.calevents){
19339             return;
19340         }
19341         
19342         Roo.each(this.cells.elements, function(c){
19343             c.row = 0;
19344             c.events = [];
19345             c.more = [];
19346         });
19347         
19348         Roo.each(this.calevents, function(e) {
19349             Roo.each(e.els, function(el) {
19350                 el.un('mouseenter' ,this.onEventEnter, this);
19351                 el.un('mouseleave' ,this.onEventLeave, this);
19352                 el.remove();
19353             },this);
19354         },this);
19355         
19356         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19357             e.remove();
19358         });
19359         
19360     },
19361     
19362     renderEvents: function()
19363     {   
19364         var _this = this;
19365         
19366         this.cells.each(function(c) {
19367             
19368             if(c.row < 5){
19369                 return;
19370             }
19371             
19372             var ev = c.events;
19373             
19374             var r = 4;
19375             if(c.row != c.events.length){
19376                 r = 4 - (4 - (c.row - c.events.length));
19377             }
19378             
19379             c.events = ev.slice(0, r);
19380             c.more = ev.slice(r);
19381             
19382             if(c.more.length && c.more.length == 1){
19383                 c.events.push(c.more.pop());
19384             }
19385             
19386             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19387             
19388         });
19389             
19390         this.cells.each(function(c) {
19391             
19392             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19393             
19394             
19395             for (var e = 0; e < c.events.length; e++){
19396                 var ev = c.events[e];
19397                 var rows = ev.rows;
19398                 
19399                 for(var i = 0; i < rows.length; i++) {
19400                 
19401                     // how many rows should it span..
19402
19403                     var  cfg = {
19404                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19405                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19406
19407                         unselectable : "on",
19408                         cn : [
19409                             {
19410                                 cls: 'fc-event-inner',
19411                                 cn : [
19412     //                                {
19413     //                                  tag:'span',
19414     //                                  cls: 'fc-event-time',
19415     //                                  html : cells.length > 1 ? '' : ev.time
19416     //                                },
19417                                     {
19418                                       tag:'span',
19419                                       cls: 'fc-event-title',
19420                                       html : String.format('{0}', ev.title)
19421                                     }
19422
19423
19424                                 ]
19425                             },
19426                             {
19427                                 cls: 'ui-resizable-handle ui-resizable-e',
19428                                 html : '&nbsp;&nbsp;&nbsp'
19429                             }
19430
19431                         ]
19432                     };
19433
19434                     if (i == 0) {
19435                         cfg.cls += ' fc-event-start';
19436                     }
19437                     if ((i+1) == rows.length) {
19438                         cfg.cls += ' fc-event-end';
19439                     }
19440
19441                     var ctr = _this.el.select('.fc-event-container',true).first();
19442                     var cg = ctr.createChild(cfg);
19443
19444                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19445                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19446
19447                     var r = (c.more.length) ? 1 : 0;
19448                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19449                     cg.setWidth(ebox.right - sbox.x -2);
19450
19451                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19452                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19453                     cg.on('click', _this.onEventClick, _this, ev);
19454
19455                     ev.els.push(cg);
19456                     
19457                 }
19458                 
19459             }
19460             
19461             
19462             if(c.more.length){
19463                 var  cfg = {
19464                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19465                     style : 'position: absolute',
19466                     unselectable : "on",
19467                     cn : [
19468                         {
19469                             cls: 'fc-event-inner',
19470                             cn : [
19471                                 {
19472                                   tag:'span',
19473                                   cls: 'fc-event-title',
19474                                   html : 'More'
19475                                 }
19476
19477
19478                             ]
19479                         },
19480                         {
19481                             cls: 'ui-resizable-handle ui-resizable-e',
19482                             html : '&nbsp;&nbsp;&nbsp'
19483                         }
19484
19485                     ]
19486                 };
19487
19488                 var ctr = _this.el.select('.fc-event-container',true).first();
19489                 var cg = ctr.createChild(cfg);
19490
19491                 var sbox = c.select('.fc-day-content',true).first().getBox();
19492                 var ebox = c.select('.fc-day-content',true).first().getBox();
19493                 //Roo.log(cg);
19494                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19495                 cg.setWidth(ebox.right - sbox.x -2);
19496
19497                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19498                 
19499             }
19500             
19501         });
19502         
19503         
19504         
19505     },
19506     
19507     onEventEnter: function (e, el,event,d) {
19508         this.fireEvent('evententer', this, el, event);
19509     },
19510     
19511     onEventLeave: function (e, el,event,d) {
19512         this.fireEvent('eventleave', this, el, event);
19513     },
19514     
19515     onEventClick: function (e, el,event,d) {
19516         this.fireEvent('eventclick', this, el, event);
19517     },
19518     
19519     onMonthChange: function () {
19520         this.store.load();
19521     },
19522     
19523     onMoreEventClick: function(e, el, more)
19524     {
19525         var _this = this;
19526         
19527         this.calpopover.placement = 'right';
19528         this.calpopover.setTitle('More');
19529         
19530         this.calpopover.setContent('');
19531         
19532         var ctr = this.calpopover.el.select('.popover-content', true).first();
19533         
19534         Roo.each(more, function(m){
19535             var cfg = {
19536                 cls : 'fc-event-hori fc-event-draggable',
19537                 html : m.title
19538             };
19539             var cg = ctr.createChild(cfg);
19540             
19541             cg.on('click', _this.onEventClick, _this, m);
19542         });
19543         
19544         this.calpopover.show(el);
19545         
19546         
19547     },
19548     
19549     onLoad: function () 
19550     {   
19551         this.calevents = [];
19552         var cal = this;
19553         
19554         if(this.store.getCount() > 0){
19555             this.store.data.each(function(d){
19556                cal.addItem({
19557                     id : d.data.id,
19558                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19559                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19560                     time : d.data.start_time,
19561                     title : d.data.title,
19562                     description : d.data.description,
19563                     venue : d.data.venue
19564                 });
19565             });
19566         }
19567         
19568         this.renderEvents();
19569         
19570         if(this.calevents.length && this.loadMask){
19571             this.maskEl.hide();
19572         }
19573     },
19574     
19575     onBeforeLoad: function()
19576     {
19577         this.clearEvents();
19578         if(this.loadMask){
19579             this.maskEl.show();
19580         }
19581     }
19582 });
19583
19584  
19585  /*
19586  * - LGPL
19587  *
19588  * element
19589  * 
19590  */
19591
19592 /**
19593  * @class Roo.bootstrap.Popover
19594  * @extends Roo.bootstrap.Component
19595  * Bootstrap Popover class
19596  * @cfg {String} html contents of the popover   (or false to use children..)
19597  * @cfg {String} title of popover (or false to hide)
19598  * @cfg {String} placement how it is placed
19599  * @cfg {String} trigger click || hover (or false to trigger manually)
19600  * @cfg {String} over what (parent or false to trigger manually.)
19601  * @cfg {Number} delay - delay before showing
19602  
19603  * @constructor
19604  * Create a new Popover
19605  * @param {Object} config The config object
19606  */
19607
19608 Roo.bootstrap.Popover = function(config){
19609     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19610     
19611     this.addEvents({
19612         // raw events
19613          /**
19614          * @event show
19615          * After the popover show
19616          * 
19617          * @param {Roo.bootstrap.Popover} this
19618          */
19619         "show" : true,
19620         /**
19621          * @event hide
19622          * After the popover hide
19623          * 
19624          * @param {Roo.bootstrap.Popover} this
19625          */
19626         "hide" : true
19627     });
19628 };
19629
19630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19631     
19632     title: 'Fill in a title',
19633     html: false,
19634     
19635     placement : 'right',
19636     trigger : 'hover', // hover
19637     
19638     delay : 0,
19639     
19640     over: 'parent',
19641     
19642     can_build_overlaid : false,
19643     
19644     getChildContainer : function()
19645     {
19646         return this.el.select('.popover-content',true).first();
19647     },
19648     
19649     getAutoCreate : function(){
19650          
19651         var cfg = {
19652            cls : 'popover roo-dynamic',
19653            style: 'display:block',
19654            cn : [
19655                 {
19656                     cls : 'arrow'
19657                 },
19658                 {
19659                     cls : 'popover-inner',
19660                     cn : [
19661                         {
19662                             tag: 'h3',
19663                             cls: 'popover-title popover-header',
19664                             html : this.title
19665                         },
19666                         {
19667                             cls : 'popover-content popover-body',
19668                             html : this.html
19669                         }
19670                     ]
19671                     
19672                 }
19673            ]
19674         };
19675         
19676         return cfg;
19677     },
19678     setTitle: function(str)
19679     {
19680         this.title = str;
19681         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19682     },
19683     setContent: function(str)
19684     {
19685         this.html = str;
19686         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19687     },
19688     // as it get's added to the bottom of the page.
19689     onRender : function(ct, position)
19690     {
19691         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19692         if(!this.el){
19693             var cfg = Roo.apply({},  this.getAutoCreate());
19694             cfg.id = Roo.id();
19695             
19696             if (this.cls) {
19697                 cfg.cls += ' ' + this.cls;
19698             }
19699             if (this.style) {
19700                 cfg.style = this.style;
19701             }
19702             //Roo.log("adding to ");
19703             this.el = Roo.get(document.body).createChild(cfg, position);
19704 //            Roo.log(this.el);
19705         }
19706         this.initEvents();
19707     },
19708     
19709     initEvents : function()
19710     {
19711         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19712         this.el.enableDisplayMode('block');
19713         this.el.hide();
19714         if (this.over === false) {
19715             return; 
19716         }
19717         if (this.triggers === false) {
19718             return;
19719         }
19720         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19721         var triggers = this.trigger ? this.trigger.split(' ') : [];
19722         Roo.each(triggers, function(trigger) {
19723         
19724             if (trigger == 'click') {
19725                 on_el.on('click', this.toggle, this);
19726             } else if (trigger != 'manual') {
19727                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19728                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19729       
19730                 on_el.on(eventIn  ,this.enter, this);
19731                 on_el.on(eventOut, this.leave, this);
19732             }
19733         }, this);
19734         
19735     },
19736     
19737     
19738     // private
19739     timeout : null,
19740     hoverState : null,
19741     
19742     toggle : function () {
19743         this.hoverState == 'in' ? this.leave() : this.enter();
19744     },
19745     
19746     enter : function () {
19747         
19748         clearTimeout(this.timeout);
19749     
19750         this.hoverState = 'in';
19751     
19752         if (!this.delay || !this.delay.show) {
19753             this.show();
19754             return;
19755         }
19756         var _t = this;
19757         this.timeout = setTimeout(function () {
19758             if (_t.hoverState == 'in') {
19759                 _t.show();
19760             }
19761         }, this.delay.show)
19762     },
19763     
19764     leave : function() {
19765         clearTimeout(this.timeout);
19766     
19767         this.hoverState = 'out';
19768     
19769         if (!this.delay || !this.delay.hide) {
19770             this.hide();
19771             return;
19772         }
19773         var _t = this;
19774         this.timeout = setTimeout(function () {
19775             if (_t.hoverState == 'out') {
19776                 _t.hide();
19777             }
19778         }, this.delay.hide)
19779     },
19780     
19781     show : function (on_el)
19782     {
19783         if (!on_el) {
19784             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19785         }
19786         
19787         // set content.
19788         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19789         if (this.html !== false) {
19790             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19791         }
19792         this.el.removeClass([
19793             'fade','top','bottom', 'left', 'right','in',
19794             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19795         ]);
19796         if (!this.title.length) {
19797             this.el.select('.popover-title',true).hide();
19798         }
19799         
19800         var placement = typeof this.placement == 'function' ?
19801             this.placement.call(this, this.el, on_el) :
19802             this.placement;
19803             
19804         var autoToken = /\s?auto?\s?/i;
19805         var autoPlace = autoToken.test(placement);
19806         if (autoPlace) {
19807             placement = placement.replace(autoToken, '') || 'top';
19808         }
19809         
19810         //this.el.detach()
19811         //this.el.setXY([0,0]);
19812         this.el.show();
19813         this.el.dom.style.display='block';
19814         this.el.addClass(placement);
19815         
19816         //this.el.appendTo(on_el);
19817         
19818         var p = this.getPosition();
19819         var box = this.el.getBox();
19820         
19821         if (autoPlace) {
19822             // fixme..
19823         }
19824         var align = Roo.bootstrap.Popover.alignment[placement];
19825         
19826 //        Roo.log(align);
19827         this.el.alignTo(on_el, align[0],align[1]);
19828         //var arrow = this.el.select('.arrow',true).first();
19829         //arrow.set(align[2], 
19830         
19831         this.el.addClass('in');
19832         
19833         
19834         if (this.el.hasClass('fade')) {
19835             // fade it?
19836         }
19837         
19838         this.hoverState = 'in';
19839         
19840         this.fireEvent('show', this);
19841         
19842     },
19843     hide : function()
19844     {
19845         this.el.setXY([0,0]);
19846         this.el.removeClass('in');
19847         this.el.hide();
19848         this.hoverState = null;
19849         
19850         this.fireEvent('hide', this);
19851     }
19852     
19853 });
19854
19855 Roo.bootstrap.Popover.alignment = {
19856     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19857     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19858     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19859     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19860 };
19861
19862  /*
19863  * - LGPL
19864  *
19865  * Progress
19866  * 
19867  */
19868
19869 /**
19870  * @class Roo.bootstrap.Progress
19871  * @extends Roo.bootstrap.Component
19872  * Bootstrap Progress class
19873  * @cfg {Boolean} striped striped of the progress bar
19874  * @cfg {Boolean} active animated of the progress bar
19875  * 
19876  * 
19877  * @constructor
19878  * Create a new Progress
19879  * @param {Object} config The config object
19880  */
19881
19882 Roo.bootstrap.Progress = function(config){
19883     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19884 };
19885
19886 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19887     
19888     striped : false,
19889     active: false,
19890     
19891     getAutoCreate : function(){
19892         var cfg = {
19893             tag: 'div',
19894             cls: 'progress'
19895         };
19896         
19897         
19898         if(this.striped){
19899             cfg.cls += ' progress-striped';
19900         }
19901       
19902         if(this.active){
19903             cfg.cls += ' active';
19904         }
19905         
19906         
19907         return cfg;
19908     }
19909    
19910 });
19911
19912  
19913
19914  /*
19915  * - LGPL
19916  *
19917  * ProgressBar
19918  * 
19919  */
19920
19921 /**
19922  * @class Roo.bootstrap.ProgressBar
19923  * @extends Roo.bootstrap.Component
19924  * Bootstrap ProgressBar class
19925  * @cfg {Number} aria_valuenow aria-value now
19926  * @cfg {Number} aria_valuemin aria-value min
19927  * @cfg {Number} aria_valuemax aria-value max
19928  * @cfg {String} label label for the progress bar
19929  * @cfg {String} panel (success | info | warning | danger )
19930  * @cfg {String} role role of the progress bar
19931  * @cfg {String} sr_only text
19932  * 
19933  * 
19934  * @constructor
19935  * Create a new ProgressBar
19936  * @param {Object} config The config object
19937  */
19938
19939 Roo.bootstrap.ProgressBar = function(config){
19940     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19941 };
19942
19943 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19944     
19945     aria_valuenow : 0,
19946     aria_valuemin : 0,
19947     aria_valuemax : 100,
19948     label : false,
19949     panel : false,
19950     role : false,
19951     sr_only: false,
19952     
19953     getAutoCreate : function()
19954     {
19955         
19956         var cfg = {
19957             tag: 'div',
19958             cls: 'progress-bar',
19959             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19960         };
19961         
19962         if(this.sr_only){
19963             cfg.cn = {
19964                 tag: 'span',
19965                 cls: 'sr-only',
19966                 html: this.sr_only
19967             }
19968         }
19969         
19970         if(this.role){
19971             cfg.role = this.role;
19972         }
19973         
19974         if(this.aria_valuenow){
19975             cfg['aria-valuenow'] = this.aria_valuenow;
19976         }
19977         
19978         if(this.aria_valuemin){
19979             cfg['aria-valuemin'] = this.aria_valuemin;
19980         }
19981         
19982         if(this.aria_valuemax){
19983             cfg['aria-valuemax'] = this.aria_valuemax;
19984         }
19985         
19986         if(this.label && !this.sr_only){
19987             cfg.html = this.label;
19988         }
19989         
19990         if(this.panel){
19991             cfg.cls += ' progress-bar-' + this.panel;
19992         }
19993         
19994         return cfg;
19995     },
19996     
19997     update : function(aria_valuenow)
19998     {
19999         this.aria_valuenow = aria_valuenow;
20000         
20001         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20002     }
20003    
20004 });
20005
20006  
20007
20008  /*
20009  * - LGPL
20010  *
20011  * column
20012  * 
20013  */
20014
20015 /**
20016  * @class Roo.bootstrap.TabGroup
20017  * @extends Roo.bootstrap.Column
20018  * Bootstrap Column class
20019  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20020  * @cfg {Boolean} carousel true to make the group behave like a carousel
20021  * @cfg {Boolean} bullets show bullets for the panels
20022  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20023  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20024  * @cfg {Boolean} showarrow (true|false) show arrow default true
20025  * 
20026  * @constructor
20027  * Create a new TabGroup
20028  * @param {Object} config The config object
20029  */
20030
20031 Roo.bootstrap.TabGroup = function(config){
20032     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20033     if (!this.navId) {
20034         this.navId = Roo.id();
20035     }
20036     this.tabs = [];
20037     Roo.bootstrap.TabGroup.register(this);
20038     
20039 };
20040
20041 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20042     
20043     carousel : false,
20044     transition : false,
20045     bullets : 0,
20046     timer : 0,
20047     autoslide : false,
20048     slideFn : false,
20049     slideOnTouch : false,
20050     showarrow : true,
20051     
20052     getAutoCreate : function()
20053     {
20054         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20055         
20056         cfg.cls += ' tab-content';
20057         
20058         if (this.carousel) {
20059             cfg.cls += ' carousel slide';
20060             
20061             cfg.cn = [{
20062                cls : 'carousel-inner',
20063                cn : []
20064             }];
20065         
20066             if(this.bullets  && !Roo.isTouch){
20067                 
20068                 var bullets = {
20069                     cls : 'carousel-bullets',
20070                     cn : []
20071                 };
20072                
20073                 if(this.bullets_cls){
20074                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20075                 }
20076                 
20077                 bullets.cn.push({
20078                     cls : 'clear'
20079                 });
20080                 
20081                 cfg.cn[0].cn.push(bullets);
20082             }
20083             
20084             if(this.showarrow){
20085                 cfg.cn[0].cn.push({
20086                     tag : 'div',
20087                     class : 'carousel-arrow',
20088                     cn : [
20089                         {
20090                             tag : 'div',
20091                             class : 'carousel-prev',
20092                             cn : [
20093                                 {
20094                                     tag : 'i',
20095                                     class : 'fa fa-chevron-left'
20096                                 }
20097                             ]
20098                         },
20099                         {
20100                             tag : 'div',
20101                             class : 'carousel-next',
20102                             cn : [
20103                                 {
20104                                     tag : 'i',
20105                                     class : 'fa fa-chevron-right'
20106                                 }
20107                             ]
20108                         }
20109                     ]
20110                 });
20111             }
20112             
20113         }
20114         
20115         return cfg;
20116     },
20117     
20118     initEvents:  function()
20119     {
20120 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20121 //            this.el.on("touchstart", this.onTouchStart, this);
20122 //        }
20123         
20124         if(this.autoslide){
20125             var _this = this;
20126             
20127             this.slideFn = window.setInterval(function() {
20128                 _this.showPanelNext();
20129             }, this.timer);
20130         }
20131         
20132         if(this.showarrow){
20133             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20134             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20135         }
20136         
20137         
20138     },
20139     
20140 //    onTouchStart : function(e, el, o)
20141 //    {
20142 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20143 //            return;
20144 //        }
20145 //        
20146 //        this.showPanelNext();
20147 //    },
20148     
20149     
20150     getChildContainer : function()
20151     {
20152         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20153     },
20154     
20155     /**
20156     * register a Navigation item
20157     * @param {Roo.bootstrap.NavItem} the navitem to add
20158     */
20159     register : function(item)
20160     {
20161         this.tabs.push( item);
20162         item.navId = this.navId; // not really needed..
20163         this.addBullet();
20164     
20165     },
20166     
20167     getActivePanel : function()
20168     {
20169         var r = false;
20170         Roo.each(this.tabs, function(t) {
20171             if (t.active) {
20172                 r = t;
20173                 return false;
20174             }
20175             return null;
20176         });
20177         return r;
20178         
20179     },
20180     getPanelByName : function(n)
20181     {
20182         var r = false;
20183         Roo.each(this.tabs, function(t) {
20184             if (t.tabId == n) {
20185                 r = t;
20186                 return false;
20187             }
20188             return null;
20189         });
20190         return r;
20191     },
20192     indexOfPanel : function(p)
20193     {
20194         var r = false;
20195         Roo.each(this.tabs, function(t,i) {
20196             if (t.tabId == p.tabId) {
20197                 r = i;
20198                 return false;
20199             }
20200             return null;
20201         });
20202         return r;
20203     },
20204     /**
20205      * show a specific panel
20206      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20207      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20208      */
20209     showPanel : function (pan)
20210     {
20211         if(this.transition || typeof(pan) == 'undefined'){
20212             Roo.log("waiting for the transitionend");
20213             return false;
20214         }
20215         
20216         if (typeof(pan) == 'number') {
20217             pan = this.tabs[pan];
20218         }
20219         
20220         if (typeof(pan) == 'string') {
20221             pan = this.getPanelByName(pan);
20222         }
20223         
20224         var cur = this.getActivePanel();
20225         
20226         if(!pan || !cur){
20227             Roo.log('pan or acitve pan is undefined');
20228             return false;
20229         }
20230         
20231         if (pan.tabId == this.getActivePanel().tabId) {
20232             return true;
20233         }
20234         
20235         if (false === cur.fireEvent('beforedeactivate')) {
20236             return false;
20237         }
20238         
20239         if(this.bullets > 0 && !Roo.isTouch){
20240             this.setActiveBullet(this.indexOfPanel(pan));
20241         }
20242         
20243         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20244             
20245             //class="carousel-item carousel-item-next carousel-item-left"
20246             
20247             this.transition = true;
20248             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20249             var lr = dir == 'next' ? 'left' : 'right';
20250             pan.el.addClass(dir); // or prev
20251             pan.el.addClass('carousel-item-' + dir); // or prev
20252             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20253             cur.el.addClass(lr); // or right
20254             pan.el.addClass(lr);
20255             cur.el.addClass('carousel-item-' +lr); // or right
20256             pan.el.addClass('carousel-item-' +lr);
20257             
20258             
20259             var _this = this;
20260             cur.el.on('transitionend', function() {
20261                 Roo.log("trans end?");
20262                 
20263                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20264                 pan.setActive(true);
20265                 
20266                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20267                 cur.setActive(false);
20268                 
20269                 _this.transition = false;
20270                 
20271             }, this, { single:  true } );
20272             
20273             return true;
20274         }
20275         
20276         cur.setActive(false);
20277         pan.setActive(true);
20278         
20279         return true;
20280         
20281     },
20282     showPanelNext : function()
20283     {
20284         var i = this.indexOfPanel(this.getActivePanel());
20285         
20286         if (i >= this.tabs.length - 1 && !this.autoslide) {
20287             return;
20288         }
20289         
20290         if (i >= this.tabs.length - 1 && this.autoslide) {
20291             i = -1;
20292         }
20293         
20294         this.showPanel(this.tabs[i+1]);
20295     },
20296     
20297     showPanelPrev : function()
20298     {
20299         var i = this.indexOfPanel(this.getActivePanel());
20300         
20301         if (i  < 1 && !this.autoslide) {
20302             return;
20303         }
20304         
20305         if (i < 1 && this.autoslide) {
20306             i = this.tabs.length;
20307         }
20308         
20309         this.showPanel(this.tabs[i-1]);
20310     },
20311     
20312     
20313     addBullet: function()
20314     {
20315         if(!this.bullets || Roo.isTouch){
20316             return;
20317         }
20318         var ctr = this.el.select('.carousel-bullets',true).first();
20319         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20320         var bullet = ctr.createChild({
20321             cls : 'bullet bullet-' + i
20322         },ctr.dom.lastChild);
20323         
20324         
20325         var _this = this;
20326         
20327         bullet.on('click', (function(e, el, o, ii, t){
20328
20329             e.preventDefault();
20330
20331             this.showPanel(ii);
20332
20333             if(this.autoslide && this.slideFn){
20334                 clearInterval(this.slideFn);
20335                 this.slideFn = window.setInterval(function() {
20336                     _this.showPanelNext();
20337                 }, this.timer);
20338             }
20339
20340         }).createDelegate(this, [i, bullet], true));
20341                 
20342         
20343     },
20344      
20345     setActiveBullet : function(i)
20346     {
20347         if(Roo.isTouch){
20348             return;
20349         }
20350         
20351         Roo.each(this.el.select('.bullet', true).elements, function(el){
20352             el.removeClass('selected');
20353         });
20354
20355         var bullet = this.el.select('.bullet-' + i, true).first();
20356         
20357         if(!bullet){
20358             return;
20359         }
20360         
20361         bullet.addClass('selected');
20362     }
20363     
20364     
20365   
20366 });
20367
20368  
20369
20370  
20371  
20372 Roo.apply(Roo.bootstrap.TabGroup, {
20373     
20374     groups: {},
20375      /**
20376     * register a Navigation Group
20377     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20378     */
20379     register : function(navgrp)
20380     {
20381         this.groups[navgrp.navId] = navgrp;
20382         
20383     },
20384     /**
20385     * fetch a Navigation Group based on the navigation ID
20386     * if one does not exist , it will get created.
20387     * @param {string} the navgroup to add
20388     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20389     */
20390     get: function(navId) {
20391         if (typeof(this.groups[navId]) == 'undefined') {
20392             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20393         }
20394         return this.groups[navId] ;
20395     }
20396     
20397     
20398     
20399 });
20400
20401  /*
20402  * - LGPL
20403  *
20404  * TabPanel
20405  * 
20406  */
20407
20408 /**
20409  * @class Roo.bootstrap.TabPanel
20410  * @extends Roo.bootstrap.Component
20411  * Bootstrap TabPanel class
20412  * @cfg {Boolean} active panel active
20413  * @cfg {String} html panel content
20414  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20415  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20416  * @cfg {String} href click to link..
20417  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20418  * 
20419  * 
20420  * @constructor
20421  * Create a new TabPanel
20422  * @param {Object} config The config object
20423  */
20424
20425 Roo.bootstrap.TabPanel = function(config){
20426     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20427     this.addEvents({
20428         /**
20429              * @event changed
20430              * Fires when the active status changes
20431              * @param {Roo.bootstrap.TabPanel} this
20432              * @param {Boolean} state the new state
20433             
20434          */
20435         'changed': true,
20436         /**
20437              * @event beforedeactivate
20438              * Fires before a tab is de-activated - can be used to do validation on a form.
20439              * @param {Roo.bootstrap.TabPanel} this
20440              * @return {Boolean} false if there is an error
20441             
20442          */
20443         'beforedeactivate': true
20444      });
20445     
20446     this.tabId = this.tabId || Roo.id();
20447   
20448 };
20449
20450 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20451     
20452     active: false,
20453     html: false,
20454     tabId: false,
20455     navId : false,
20456     href : '',
20457     touchSlide : false,
20458     getAutoCreate : function(){
20459         
20460         
20461         var cfg = {
20462             tag: 'div',
20463             // item is needed for carousel - not sure if it has any effect otherwise
20464             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20465             html: this.html || ''
20466         };
20467         
20468         if(this.active){
20469             cfg.cls += ' active';
20470         }
20471         
20472         if(this.tabId){
20473             cfg.tabId = this.tabId;
20474         }
20475         
20476         
20477         
20478         return cfg;
20479     },
20480     
20481     initEvents:  function()
20482     {
20483         var p = this.parent();
20484         
20485         this.navId = this.navId || p.navId;
20486         
20487         if (typeof(this.navId) != 'undefined') {
20488             // not really needed.. but just in case.. parent should be a NavGroup.
20489             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20490             
20491             tg.register(this);
20492             
20493             var i = tg.tabs.length - 1;
20494             
20495             if(this.active && tg.bullets > 0 && i < tg.bullets){
20496                 tg.setActiveBullet(i);
20497             }
20498         }
20499         
20500         this.el.on('click', this.onClick, this);
20501         
20502         if(Roo.isTouch && this.touchSlide){
20503             this.el.on("touchstart", this.onTouchStart, this);
20504             this.el.on("touchmove", this.onTouchMove, this);
20505             this.el.on("touchend", this.onTouchEnd, this);
20506         }
20507         
20508     },
20509     
20510     onRender : function(ct, position)
20511     {
20512         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20513     },
20514     
20515     setActive : function(state)
20516     {
20517         Roo.log("panel - set active " + this.tabId + "=" + state);
20518         
20519         this.active = state;
20520         if (!state) {
20521             this.el.removeClass('active');
20522             
20523         } else  if (!this.el.hasClass('active')) {
20524             this.el.addClass('active');
20525         }
20526         
20527         this.fireEvent('changed', this, state);
20528     },
20529     
20530     onClick : function(e)
20531     {
20532         e.preventDefault();
20533         
20534         if(!this.href.length){
20535             return;
20536         }
20537         
20538         window.location.href = this.href;
20539     },
20540     
20541     startX : 0,
20542     startY : 0,
20543     endX : 0,
20544     endY : 0,
20545     swiping : false,
20546     
20547     onTouchStart : function(e)
20548     {
20549         this.swiping = false;
20550         
20551         this.startX = e.browserEvent.touches[0].clientX;
20552         this.startY = e.browserEvent.touches[0].clientY;
20553     },
20554     
20555     onTouchMove : function(e)
20556     {
20557         this.swiping = true;
20558         
20559         this.endX = e.browserEvent.touches[0].clientX;
20560         this.endY = e.browserEvent.touches[0].clientY;
20561     },
20562     
20563     onTouchEnd : function(e)
20564     {
20565         if(!this.swiping){
20566             this.onClick(e);
20567             return;
20568         }
20569         
20570         var tabGroup = this.parent();
20571         
20572         if(this.endX > this.startX){ // swiping right
20573             tabGroup.showPanelPrev();
20574             return;
20575         }
20576         
20577         if(this.startX > this.endX){ // swiping left
20578             tabGroup.showPanelNext();
20579             return;
20580         }
20581     }
20582     
20583     
20584 });
20585  
20586
20587  
20588
20589  /*
20590  * - LGPL
20591  *
20592  * DateField
20593  * 
20594  */
20595
20596 /**
20597  * @class Roo.bootstrap.DateField
20598  * @extends Roo.bootstrap.Input
20599  * Bootstrap DateField class
20600  * @cfg {Number} weekStart default 0
20601  * @cfg {String} viewMode default empty, (months|years)
20602  * @cfg {String} minViewMode default empty, (months|years)
20603  * @cfg {Number} startDate default -Infinity
20604  * @cfg {Number} endDate default Infinity
20605  * @cfg {Boolean} todayHighlight default false
20606  * @cfg {Boolean} todayBtn default false
20607  * @cfg {Boolean} calendarWeeks default false
20608  * @cfg {Object} daysOfWeekDisabled default empty
20609  * @cfg {Boolean} singleMode default false (true | false)
20610  * 
20611  * @cfg {Boolean} keyboardNavigation default true
20612  * @cfg {String} language default en
20613  * 
20614  * @constructor
20615  * Create a new DateField
20616  * @param {Object} config The config object
20617  */
20618
20619 Roo.bootstrap.DateField = function(config){
20620     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20621      this.addEvents({
20622             /**
20623              * @event show
20624              * Fires when this field show.
20625              * @param {Roo.bootstrap.DateField} this
20626              * @param {Mixed} date The date value
20627              */
20628             show : true,
20629             /**
20630              * @event show
20631              * Fires when this field hide.
20632              * @param {Roo.bootstrap.DateField} this
20633              * @param {Mixed} date The date value
20634              */
20635             hide : true,
20636             /**
20637              * @event select
20638              * Fires when select a date.
20639              * @param {Roo.bootstrap.DateField} this
20640              * @param {Mixed} date The date value
20641              */
20642             select : true,
20643             /**
20644              * @event beforeselect
20645              * Fires when before select a date.
20646              * @param {Roo.bootstrap.DateField} this
20647              * @param {Mixed} date The date value
20648              */
20649             beforeselect : true
20650         });
20651 };
20652
20653 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20654     
20655     /**
20656      * @cfg {String} format
20657      * The default date format string which can be overriden for localization support.  The format must be
20658      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20659      */
20660     format : "m/d/y",
20661     /**
20662      * @cfg {String} altFormats
20663      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20664      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20665      */
20666     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20667     
20668     weekStart : 0,
20669     
20670     viewMode : '',
20671     
20672     minViewMode : '',
20673     
20674     todayHighlight : false,
20675     
20676     todayBtn: false,
20677     
20678     language: 'en',
20679     
20680     keyboardNavigation: true,
20681     
20682     calendarWeeks: false,
20683     
20684     startDate: -Infinity,
20685     
20686     endDate: Infinity,
20687     
20688     daysOfWeekDisabled: [],
20689     
20690     _events: [],
20691     
20692     singleMode : false,
20693     
20694     UTCDate: function()
20695     {
20696         return new Date(Date.UTC.apply(Date, arguments));
20697     },
20698     
20699     UTCToday: function()
20700     {
20701         var today = new Date();
20702         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20703     },
20704     
20705     getDate: function() {
20706             var d = this.getUTCDate();
20707             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20708     },
20709     
20710     getUTCDate: function() {
20711             return this.date;
20712     },
20713     
20714     setDate: function(d) {
20715             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20716     },
20717     
20718     setUTCDate: function(d) {
20719             this.date = d;
20720             this.setValue(this.formatDate(this.date));
20721     },
20722         
20723     onRender: function(ct, position)
20724     {
20725         
20726         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20727         
20728         this.language = this.language || 'en';
20729         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20730         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20731         
20732         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20733         this.format = this.format || 'm/d/y';
20734         this.isInline = false;
20735         this.isInput = true;
20736         this.component = this.el.select('.add-on', true).first() || false;
20737         this.component = (this.component && this.component.length === 0) ? false : this.component;
20738         this.hasInput = this.component && this.inputEl().length;
20739         
20740         if (typeof(this.minViewMode === 'string')) {
20741             switch (this.minViewMode) {
20742                 case 'months':
20743                     this.minViewMode = 1;
20744                     break;
20745                 case 'years':
20746                     this.minViewMode = 2;
20747                     break;
20748                 default:
20749                     this.minViewMode = 0;
20750                     break;
20751             }
20752         }
20753         
20754         if (typeof(this.viewMode === 'string')) {
20755             switch (this.viewMode) {
20756                 case 'months':
20757                     this.viewMode = 1;
20758                     break;
20759                 case 'years':
20760                     this.viewMode = 2;
20761                     break;
20762                 default:
20763                     this.viewMode = 0;
20764                     break;
20765             }
20766         }
20767                 
20768         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20769         
20770 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20771         
20772         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20773         
20774         this.picker().on('mousedown', this.onMousedown, this);
20775         this.picker().on('click', this.onClick, this);
20776         
20777         this.picker().addClass('datepicker-dropdown');
20778         
20779         this.startViewMode = this.viewMode;
20780         
20781         if(this.singleMode){
20782             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20783                 v.setVisibilityMode(Roo.Element.DISPLAY);
20784                 v.hide();
20785             });
20786             
20787             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20788                 v.setStyle('width', '189px');
20789             });
20790         }
20791         
20792         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20793             if(!this.calendarWeeks){
20794                 v.remove();
20795                 return;
20796             }
20797             
20798             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20799             v.attr('colspan', function(i, val){
20800                 return parseInt(val) + 1;
20801             });
20802         });
20803                         
20804         
20805         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20806         
20807         this.setStartDate(this.startDate);
20808         this.setEndDate(this.endDate);
20809         
20810         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20811         
20812         this.fillDow();
20813         this.fillMonths();
20814         this.update();
20815         this.showMode();
20816         
20817         if(this.isInline) {
20818             this.showPopup();
20819         }
20820     },
20821     
20822     picker : function()
20823     {
20824         return this.pickerEl;
20825 //        return this.el.select('.datepicker', true).first();
20826     },
20827     
20828     fillDow: function()
20829     {
20830         var dowCnt = this.weekStart;
20831         
20832         var dow = {
20833             tag: 'tr',
20834             cn: [
20835                 
20836             ]
20837         };
20838         
20839         if(this.calendarWeeks){
20840             dow.cn.push({
20841                 tag: 'th',
20842                 cls: 'cw',
20843                 html: '&nbsp;'
20844             })
20845         }
20846         
20847         while (dowCnt < this.weekStart + 7) {
20848             dow.cn.push({
20849                 tag: 'th',
20850                 cls: 'dow',
20851                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20852             });
20853         }
20854         
20855         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20856     },
20857     
20858     fillMonths: function()
20859     {    
20860         var i = 0;
20861         var months = this.picker().select('>.datepicker-months td', true).first();
20862         
20863         months.dom.innerHTML = '';
20864         
20865         while (i < 12) {
20866             var month = {
20867                 tag: 'span',
20868                 cls: 'month',
20869                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20870             };
20871             
20872             months.createChild(month);
20873         }
20874         
20875     },
20876     
20877     update: function()
20878     {
20879         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;
20880         
20881         if (this.date < this.startDate) {
20882             this.viewDate = new Date(this.startDate);
20883         } else if (this.date > this.endDate) {
20884             this.viewDate = new Date(this.endDate);
20885         } else {
20886             this.viewDate = new Date(this.date);
20887         }
20888         
20889         this.fill();
20890     },
20891     
20892     fill: function() 
20893     {
20894         var d = new Date(this.viewDate),
20895                 year = d.getUTCFullYear(),
20896                 month = d.getUTCMonth(),
20897                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20898                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20899                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20900                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20901                 currentDate = this.date && this.date.valueOf(),
20902                 today = this.UTCToday();
20903         
20904         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20905         
20906 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20907         
20908 //        this.picker.select('>tfoot th.today').
20909 //                                              .text(dates[this.language].today)
20910 //                                              .toggle(this.todayBtn !== false);
20911     
20912         this.updateNavArrows();
20913         this.fillMonths();
20914                                                 
20915         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20916         
20917         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20918          
20919         prevMonth.setUTCDate(day);
20920         
20921         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20922         
20923         var nextMonth = new Date(prevMonth);
20924         
20925         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20926         
20927         nextMonth = nextMonth.valueOf();
20928         
20929         var fillMonths = false;
20930         
20931         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20932         
20933         while(prevMonth.valueOf() <= nextMonth) {
20934             var clsName = '';
20935             
20936             if (prevMonth.getUTCDay() === this.weekStart) {
20937                 if(fillMonths){
20938                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20939                 }
20940                     
20941                 fillMonths = {
20942                     tag: 'tr',
20943                     cn: []
20944                 };
20945                 
20946                 if(this.calendarWeeks){
20947                     // ISO 8601: First week contains first thursday.
20948                     // ISO also states week starts on Monday, but we can be more abstract here.
20949                     var
20950                     // Start of current week: based on weekstart/current date
20951                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20952                     // Thursday of this week
20953                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20954                     // First Thursday of year, year from thursday
20955                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20956                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20957                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20958                     
20959                     fillMonths.cn.push({
20960                         tag: 'td',
20961                         cls: 'cw',
20962                         html: calWeek
20963                     });
20964                 }
20965             }
20966             
20967             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20968                 clsName += ' old';
20969             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20970                 clsName += ' new';
20971             }
20972             if (this.todayHighlight &&
20973                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20974                 prevMonth.getUTCMonth() == today.getMonth() &&
20975                 prevMonth.getUTCDate() == today.getDate()) {
20976                 clsName += ' today';
20977             }
20978             
20979             if (currentDate && prevMonth.valueOf() === currentDate) {
20980                 clsName += ' active';
20981             }
20982             
20983             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20984                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20985                     clsName += ' disabled';
20986             }
20987             
20988             fillMonths.cn.push({
20989                 tag: 'td',
20990                 cls: 'day ' + clsName,
20991                 html: prevMonth.getDate()
20992             });
20993             
20994             prevMonth.setDate(prevMonth.getDate()+1);
20995         }
20996           
20997         var currentYear = this.date && this.date.getUTCFullYear();
20998         var currentMonth = this.date && this.date.getUTCMonth();
20999         
21000         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21001         
21002         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21003             v.removeClass('active');
21004             
21005             if(currentYear === year && k === currentMonth){
21006                 v.addClass('active');
21007             }
21008             
21009             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21010                 v.addClass('disabled');
21011             }
21012             
21013         });
21014         
21015         
21016         year = parseInt(year/10, 10) * 10;
21017         
21018         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21019         
21020         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21021         
21022         year -= 1;
21023         for (var i = -1; i < 11; i++) {
21024             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21025                 tag: 'span',
21026                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21027                 html: year
21028             });
21029             
21030             year += 1;
21031         }
21032     },
21033     
21034     showMode: function(dir) 
21035     {
21036         if (dir) {
21037             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21038         }
21039         
21040         Roo.each(this.picker().select('>div',true).elements, function(v){
21041             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21042             v.hide();
21043         });
21044         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21045     },
21046     
21047     place: function()
21048     {
21049         if(this.isInline) {
21050             return;
21051         }
21052         
21053         this.picker().removeClass(['bottom', 'top']);
21054         
21055         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21056             /*
21057              * place to the top of element!
21058              *
21059              */
21060             
21061             this.picker().addClass('top');
21062             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21063             
21064             return;
21065         }
21066         
21067         this.picker().addClass('bottom');
21068         
21069         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21070     },
21071     
21072     parseDate : function(value)
21073     {
21074         if(!value || value instanceof Date){
21075             return value;
21076         }
21077         var v = Date.parseDate(value, this.format);
21078         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21079             v = Date.parseDate(value, 'Y-m-d');
21080         }
21081         if(!v && this.altFormats){
21082             if(!this.altFormatsArray){
21083                 this.altFormatsArray = this.altFormats.split("|");
21084             }
21085             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21086                 v = Date.parseDate(value, this.altFormatsArray[i]);
21087             }
21088         }
21089         return v;
21090     },
21091     
21092     formatDate : function(date, fmt)
21093     {   
21094         return (!date || !(date instanceof Date)) ?
21095         date : date.dateFormat(fmt || this.format);
21096     },
21097     
21098     onFocus : function()
21099     {
21100         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21101         this.showPopup();
21102     },
21103     
21104     onBlur : function()
21105     {
21106         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21107         
21108         var d = this.inputEl().getValue();
21109         
21110         this.setValue(d);
21111                 
21112         this.hidePopup();
21113     },
21114     
21115     showPopup : function()
21116     {
21117         this.picker().show();
21118         this.update();
21119         this.place();
21120         
21121         this.fireEvent('showpopup', this, this.date);
21122     },
21123     
21124     hidePopup : function()
21125     {
21126         if(this.isInline) {
21127             return;
21128         }
21129         this.picker().hide();
21130         this.viewMode = this.startViewMode;
21131         this.showMode();
21132         
21133         this.fireEvent('hidepopup', this, this.date);
21134         
21135     },
21136     
21137     onMousedown: function(e)
21138     {
21139         e.stopPropagation();
21140         e.preventDefault();
21141     },
21142     
21143     keyup: function(e)
21144     {
21145         Roo.bootstrap.DateField.superclass.keyup.call(this);
21146         this.update();
21147     },
21148
21149     setValue: function(v)
21150     {
21151         if(this.fireEvent('beforeselect', this, v) !== false){
21152             var d = new Date(this.parseDate(v) ).clearTime();
21153         
21154             if(isNaN(d.getTime())){
21155                 this.date = this.viewDate = '';
21156                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21157                 return;
21158             }
21159
21160             v = this.formatDate(d);
21161
21162             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21163
21164             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21165
21166             this.update();
21167
21168             this.fireEvent('select', this, this.date);
21169         }
21170     },
21171     
21172     getValue: function()
21173     {
21174         return this.formatDate(this.date);
21175     },
21176     
21177     fireKey: function(e)
21178     {
21179         if (!this.picker().isVisible()){
21180             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21181                 this.showPopup();
21182             }
21183             return;
21184         }
21185         
21186         var dateChanged = false,
21187         dir, day, month,
21188         newDate, newViewDate;
21189         
21190         switch(e.keyCode){
21191             case 27: // escape
21192                 this.hidePopup();
21193                 e.preventDefault();
21194                 break;
21195             case 37: // left
21196             case 39: // right
21197                 if (!this.keyboardNavigation) {
21198                     break;
21199                 }
21200                 dir = e.keyCode == 37 ? -1 : 1;
21201                 
21202                 if (e.ctrlKey){
21203                     newDate = this.moveYear(this.date, dir);
21204                     newViewDate = this.moveYear(this.viewDate, dir);
21205                 } else if (e.shiftKey){
21206                     newDate = this.moveMonth(this.date, dir);
21207                     newViewDate = this.moveMonth(this.viewDate, dir);
21208                 } else {
21209                     newDate = new Date(this.date);
21210                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21211                     newViewDate = new Date(this.viewDate);
21212                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21213                 }
21214                 if (this.dateWithinRange(newDate)){
21215                     this.date = newDate;
21216                     this.viewDate = newViewDate;
21217                     this.setValue(this.formatDate(this.date));
21218 //                    this.update();
21219                     e.preventDefault();
21220                     dateChanged = true;
21221                 }
21222                 break;
21223             case 38: // up
21224             case 40: // down
21225                 if (!this.keyboardNavigation) {
21226                     break;
21227                 }
21228                 dir = e.keyCode == 38 ? -1 : 1;
21229                 if (e.ctrlKey){
21230                     newDate = this.moveYear(this.date, dir);
21231                     newViewDate = this.moveYear(this.viewDate, dir);
21232                 } else if (e.shiftKey){
21233                     newDate = this.moveMonth(this.date, dir);
21234                     newViewDate = this.moveMonth(this.viewDate, dir);
21235                 } else {
21236                     newDate = new Date(this.date);
21237                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21238                     newViewDate = new Date(this.viewDate);
21239                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21240                 }
21241                 if (this.dateWithinRange(newDate)){
21242                     this.date = newDate;
21243                     this.viewDate = newViewDate;
21244                     this.setValue(this.formatDate(this.date));
21245 //                    this.update();
21246                     e.preventDefault();
21247                     dateChanged = true;
21248                 }
21249                 break;
21250             case 13: // enter
21251                 this.setValue(this.formatDate(this.date));
21252                 this.hidePopup();
21253                 e.preventDefault();
21254                 break;
21255             case 9: // tab
21256                 this.setValue(this.formatDate(this.date));
21257                 this.hidePopup();
21258                 break;
21259             case 16: // shift
21260             case 17: // ctrl
21261             case 18: // alt
21262                 break;
21263             default :
21264                 this.hidePopup();
21265                 
21266         }
21267     },
21268     
21269     
21270     onClick: function(e) 
21271     {
21272         e.stopPropagation();
21273         e.preventDefault();
21274         
21275         var target = e.getTarget();
21276         
21277         if(target.nodeName.toLowerCase() === 'i'){
21278             target = Roo.get(target).dom.parentNode;
21279         }
21280         
21281         var nodeName = target.nodeName;
21282         var className = target.className;
21283         var html = target.innerHTML;
21284         //Roo.log(nodeName);
21285         
21286         switch(nodeName.toLowerCase()) {
21287             case 'th':
21288                 switch(className) {
21289                     case 'switch':
21290                         this.showMode(1);
21291                         break;
21292                     case 'prev':
21293                     case 'next':
21294                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21295                         switch(this.viewMode){
21296                                 case 0:
21297                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21298                                         break;
21299                                 case 1:
21300                                 case 2:
21301                                         this.viewDate = this.moveYear(this.viewDate, dir);
21302                                         break;
21303                         }
21304                         this.fill();
21305                         break;
21306                     case 'today':
21307                         var date = new Date();
21308                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21309 //                        this.fill()
21310                         this.setValue(this.formatDate(this.date));
21311                         
21312                         this.hidePopup();
21313                         break;
21314                 }
21315                 break;
21316             case 'span':
21317                 if (className.indexOf('disabled') < 0) {
21318                     this.viewDate.setUTCDate(1);
21319                     if (className.indexOf('month') > -1) {
21320                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21321                     } else {
21322                         var year = parseInt(html, 10) || 0;
21323                         this.viewDate.setUTCFullYear(year);
21324                         
21325                     }
21326                     
21327                     if(this.singleMode){
21328                         this.setValue(this.formatDate(this.viewDate));
21329                         this.hidePopup();
21330                         return;
21331                     }
21332                     
21333                     this.showMode(-1);
21334                     this.fill();
21335                 }
21336                 break;
21337                 
21338             case 'td':
21339                 //Roo.log(className);
21340                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21341                     var day = parseInt(html, 10) || 1;
21342                     var year = this.viewDate.getUTCFullYear(),
21343                         month = this.viewDate.getUTCMonth();
21344
21345                     if (className.indexOf('old') > -1) {
21346                         if(month === 0 ){
21347                             month = 11;
21348                             year -= 1;
21349                         }else{
21350                             month -= 1;
21351                         }
21352                     } else if (className.indexOf('new') > -1) {
21353                         if (month == 11) {
21354                             month = 0;
21355                             year += 1;
21356                         } else {
21357                             month += 1;
21358                         }
21359                     }
21360                     //Roo.log([year,month,day]);
21361                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21362                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21363 //                    this.fill();
21364                     //Roo.log(this.formatDate(this.date));
21365                     this.setValue(this.formatDate(this.date));
21366                     this.hidePopup();
21367                 }
21368                 break;
21369         }
21370     },
21371     
21372     setStartDate: function(startDate)
21373     {
21374         this.startDate = startDate || -Infinity;
21375         if (this.startDate !== -Infinity) {
21376             this.startDate = this.parseDate(this.startDate);
21377         }
21378         this.update();
21379         this.updateNavArrows();
21380     },
21381
21382     setEndDate: function(endDate)
21383     {
21384         this.endDate = endDate || Infinity;
21385         if (this.endDate !== Infinity) {
21386             this.endDate = this.parseDate(this.endDate);
21387         }
21388         this.update();
21389         this.updateNavArrows();
21390     },
21391     
21392     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21393     {
21394         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21395         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21396             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21397         }
21398         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21399             return parseInt(d, 10);
21400         });
21401         this.update();
21402         this.updateNavArrows();
21403     },
21404     
21405     updateNavArrows: function() 
21406     {
21407         if(this.singleMode){
21408             return;
21409         }
21410         
21411         var d = new Date(this.viewDate),
21412         year = d.getUTCFullYear(),
21413         month = d.getUTCMonth();
21414         
21415         Roo.each(this.picker().select('.prev', true).elements, function(v){
21416             v.show();
21417             switch (this.viewMode) {
21418                 case 0:
21419
21420                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21421                         v.hide();
21422                     }
21423                     break;
21424                 case 1:
21425                 case 2:
21426                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21427                         v.hide();
21428                     }
21429                     break;
21430             }
21431         });
21432         
21433         Roo.each(this.picker().select('.next', true).elements, function(v){
21434             v.show();
21435             switch (this.viewMode) {
21436                 case 0:
21437
21438                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21439                         v.hide();
21440                     }
21441                     break;
21442                 case 1:
21443                 case 2:
21444                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21445                         v.hide();
21446                     }
21447                     break;
21448             }
21449         })
21450     },
21451     
21452     moveMonth: function(date, dir)
21453     {
21454         if (!dir) {
21455             return date;
21456         }
21457         var new_date = new Date(date.valueOf()),
21458         day = new_date.getUTCDate(),
21459         month = new_date.getUTCMonth(),
21460         mag = Math.abs(dir),
21461         new_month, test;
21462         dir = dir > 0 ? 1 : -1;
21463         if (mag == 1){
21464             test = dir == -1
21465             // If going back one month, make sure month is not current month
21466             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21467             ? function(){
21468                 return new_date.getUTCMonth() == month;
21469             }
21470             // If going forward one month, make sure month is as expected
21471             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21472             : function(){
21473                 return new_date.getUTCMonth() != new_month;
21474             };
21475             new_month = month + dir;
21476             new_date.setUTCMonth(new_month);
21477             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21478             if (new_month < 0 || new_month > 11) {
21479                 new_month = (new_month + 12) % 12;
21480             }
21481         } else {
21482             // For magnitudes >1, move one month at a time...
21483             for (var i=0; i<mag; i++) {
21484                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21485                 new_date = this.moveMonth(new_date, dir);
21486             }
21487             // ...then reset the day, keeping it in the new month
21488             new_month = new_date.getUTCMonth();
21489             new_date.setUTCDate(day);
21490             test = function(){
21491                 return new_month != new_date.getUTCMonth();
21492             };
21493         }
21494         // Common date-resetting loop -- if date is beyond end of month, make it
21495         // end of month
21496         while (test()){
21497             new_date.setUTCDate(--day);
21498             new_date.setUTCMonth(new_month);
21499         }
21500         return new_date;
21501     },
21502
21503     moveYear: function(date, dir)
21504     {
21505         return this.moveMonth(date, dir*12);
21506     },
21507
21508     dateWithinRange: function(date)
21509     {
21510         return date >= this.startDate && date <= this.endDate;
21511     },
21512
21513     
21514     remove: function() 
21515     {
21516         this.picker().remove();
21517     },
21518     
21519     validateValue : function(value)
21520     {
21521         if(this.getVisibilityEl().hasClass('hidden')){
21522             return true;
21523         }
21524         
21525         if(value.length < 1)  {
21526             if(this.allowBlank){
21527                 return true;
21528             }
21529             return false;
21530         }
21531         
21532         if(value.length < this.minLength){
21533             return false;
21534         }
21535         if(value.length > this.maxLength){
21536             return false;
21537         }
21538         if(this.vtype){
21539             var vt = Roo.form.VTypes;
21540             if(!vt[this.vtype](value, this)){
21541                 return false;
21542             }
21543         }
21544         if(typeof this.validator == "function"){
21545             var msg = this.validator(value);
21546             if(msg !== true){
21547                 return false;
21548             }
21549         }
21550         
21551         if(this.regex && !this.regex.test(value)){
21552             return false;
21553         }
21554         
21555         if(typeof(this.parseDate(value)) == 'undefined'){
21556             return false;
21557         }
21558         
21559         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21560             return false;
21561         }      
21562         
21563         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21564             return false;
21565         } 
21566         
21567         
21568         return true;
21569     },
21570     
21571     reset : function()
21572     {
21573         this.date = this.viewDate = '';
21574         
21575         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21576     }
21577    
21578 });
21579
21580 Roo.apply(Roo.bootstrap.DateField,  {
21581     
21582     head : {
21583         tag: 'thead',
21584         cn: [
21585         {
21586             tag: 'tr',
21587             cn: [
21588             {
21589                 tag: 'th',
21590                 cls: 'prev',
21591                 html: '<i class="fa fa-arrow-left"/>'
21592             },
21593             {
21594                 tag: 'th',
21595                 cls: 'switch',
21596                 colspan: '5'
21597             },
21598             {
21599                 tag: 'th',
21600                 cls: 'next',
21601                 html: '<i class="fa fa-arrow-right"/>'
21602             }
21603
21604             ]
21605         }
21606         ]
21607     },
21608     
21609     content : {
21610         tag: 'tbody',
21611         cn: [
21612         {
21613             tag: 'tr',
21614             cn: [
21615             {
21616                 tag: 'td',
21617                 colspan: '7'
21618             }
21619             ]
21620         }
21621         ]
21622     },
21623     
21624     footer : {
21625         tag: 'tfoot',
21626         cn: [
21627         {
21628             tag: 'tr',
21629             cn: [
21630             {
21631                 tag: 'th',
21632                 colspan: '7',
21633                 cls: 'today'
21634             }
21635                     
21636             ]
21637         }
21638         ]
21639     },
21640     
21641     dates:{
21642         en: {
21643             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21644             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21645             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21646             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21647             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21648             today: "Today"
21649         }
21650     },
21651     
21652     modes: [
21653     {
21654         clsName: 'days',
21655         navFnc: 'Month',
21656         navStep: 1
21657     },
21658     {
21659         clsName: 'months',
21660         navFnc: 'FullYear',
21661         navStep: 1
21662     },
21663     {
21664         clsName: 'years',
21665         navFnc: 'FullYear',
21666         navStep: 10
21667     }]
21668 });
21669
21670 Roo.apply(Roo.bootstrap.DateField,  {
21671   
21672     template : {
21673         tag: 'div',
21674         cls: 'datepicker dropdown-menu roo-dynamic',
21675         cn: [
21676         {
21677             tag: 'div',
21678             cls: 'datepicker-days',
21679             cn: [
21680             {
21681                 tag: 'table',
21682                 cls: 'table-condensed',
21683                 cn:[
21684                 Roo.bootstrap.DateField.head,
21685                 {
21686                     tag: 'tbody'
21687                 },
21688                 Roo.bootstrap.DateField.footer
21689                 ]
21690             }
21691             ]
21692         },
21693         {
21694             tag: 'div',
21695             cls: 'datepicker-months',
21696             cn: [
21697             {
21698                 tag: 'table',
21699                 cls: 'table-condensed',
21700                 cn:[
21701                 Roo.bootstrap.DateField.head,
21702                 Roo.bootstrap.DateField.content,
21703                 Roo.bootstrap.DateField.footer
21704                 ]
21705             }
21706             ]
21707         },
21708         {
21709             tag: 'div',
21710             cls: 'datepicker-years',
21711             cn: [
21712             {
21713                 tag: 'table',
21714                 cls: 'table-condensed',
21715                 cn:[
21716                 Roo.bootstrap.DateField.head,
21717                 Roo.bootstrap.DateField.content,
21718                 Roo.bootstrap.DateField.footer
21719                 ]
21720             }
21721             ]
21722         }
21723         ]
21724     }
21725 });
21726
21727  
21728
21729  /*
21730  * - LGPL
21731  *
21732  * TimeField
21733  * 
21734  */
21735
21736 /**
21737  * @class Roo.bootstrap.TimeField
21738  * @extends Roo.bootstrap.Input
21739  * Bootstrap DateField class
21740  * 
21741  * 
21742  * @constructor
21743  * Create a new TimeField
21744  * @param {Object} config The config object
21745  */
21746
21747 Roo.bootstrap.TimeField = function(config){
21748     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21749     this.addEvents({
21750             /**
21751              * @event show
21752              * Fires when this field show.
21753              * @param {Roo.bootstrap.DateField} thisthis
21754              * @param {Mixed} date The date value
21755              */
21756             show : true,
21757             /**
21758              * @event show
21759              * Fires when this field hide.
21760              * @param {Roo.bootstrap.DateField} this
21761              * @param {Mixed} date The date value
21762              */
21763             hide : true,
21764             /**
21765              * @event select
21766              * Fires when select a date.
21767              * @param {Roo.bootstrap.DateField} this
21768              * @param {Mixed} date The date value
21769              */
21770             select : true
21771         });
21772 };
21773
21774 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21775     
21776     /**
21777      * @cfg {String} format
21778      * The default time format string which can be overriden for localization support.  The format must be
21779      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21780      */
21781     format : "H:i",
21782        
21783     onRender: function(ct, position)
21784     {
21785         
21786         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21787                 
21788         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21789         
21790         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21791         
21792         this.pop = this.picker().select('>.datepicker-time',true).first();
21793         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21794         
21795         this.picker().on('mousedown', this.onMousedown, this);
21796         this.picker().on('click', this.onClick, this);
21797         
21798         this.picker().addClass('datepicker-dropdown');
21799     
21800         this.fillTime();
21801         this.update();
21802             
21803         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21804         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21805         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21806         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21807         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21808         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21809
21810     },
21811     
21812     fireKey: function(e){
21813         if (!this.picker().isVisible()){
21814             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21815                 this.show();
21816             }
21817             return;
21818         }
21819
21820         e.preventDefault();
21821         
21822         switch(e.keyCode){
21823             case 27: // escape
21824                 this.hide();
21825                 break;
21826             case 37: // left
21827             case 39: // right
21828                 this.onTogglePeriod();
21829                 break;
21830             case 38: // up
21831                 this.onIncrementMinutes();
21832                 break;
21833             case 40: // down
21834                 this.onDecrementMinutes();
21835                 break;
21836             case 13: // enter
21837             case 9: // tab
21838                 this.setTime();
21839                 break;
21840         }
21841     },
21842     
21843     onClick: function(e) {
21844         e.stopPropagation();
21845         e.preventDefault();
21846     },
21847     
21848     picker : function()
21849     {
21850         return this.el.select('.datepicker', true).first();
21851     },
21852     
21853     fillTime: function()
21854     {    
21855         var time = this.pop.select('tbody', true).first();
21856         
21857         time.dom.innerHTML = '';
21858         
21859         time.createChild({
21860             tag: 'tr',
21861             cn: [
21862                 {
21863                     tag: 'td',
21864                     cn: [
21865                         {
21866                             tag: 'a',
21867                             href: '#',
21868                             cls: 'btn',
21869                             cn: [
21870                                 {
21871                                     tag: 'span',
21872                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21873                                 }
21874                             ]
21875                         } 
21876                     ]
21877                 },
21878                 {
21879                     tag: 'td',
21880                     cls: 'separator'
21881                 },
21882                 {
21883                     tag: 'td',
21884                     cn: [
21885                         {
21886                             tag: 'a',
21887                             href: '#',
21888                             cls: 'btn',
21889                             cn: [
21890                                 {
21891                                     tag: 'span',
21892                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21893                                 }
21894                             ]
21895                         }
21896                     ]
21897                 },
21898                 {
21899                     tag: 'td',
21900                     cls: 'separator'
21901                 }
21902             ]
21903         });
21904         
21905         time.createChild({
21906             tag: 'tr',
21907             cn: [
21908                 {
21909                     tag: 'td',
21910                     cn: [
21911                         {
21912                             tag: 'span',
21913                             cls: 'timepicker-hour',
21914                             html: '00'
21915                         }  
21916                     ]
21917                 },
21918                 {
21919                     tag: 'td',
21920                     cls: 'separator',
21921                     html: ':'
21922                 },
21923                 {
21924                     tag: 'td',
21925                     cn: [
21926                         {
21927                             tag: 'span',
21928                             cls: 'timepicker-minute',
21929                             html: '00'
21930                         }  
21931                     ]
21932                 },
21933                 {
21934                     tag: 'td',
21935                     cls: 'separator'
21936                 },
21937                 {
21938                     tag: 'td',
21939                     cn: [
21940                         {
21941                             tag: 'button',
21942                             type: 'button',
21943                             cls: 'btn btn-primary period',
21944                             html: 'AM'
21945                             
21946                         }
21947                     ]
21948                 }
21949             ]
21950         });
21951         
21952         time.createChild({
21953             tag: 'tr',
21954             cn: [
21955                 {
21956                     tag: 'td',
21957                     cn: [
21958                         {
21959                             tag: 'a',
21960                             href: '#',
21961                             cls: 'btn',
21962                             cn: [
21963                                 {
21964                                     tag: 'span',
21965                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21966                                 }
21967                             ]
21968                         }
21969                     ]
21970                 },
21971                 {
21972                     tag: 'td',
21973                     cls: 'separator'
21974                 },
21975                 {
21976                     tag: 'td',
21977                     cn: [
21978                         {
21979                             tag: 'a',
21980                             href: '#',
21981                             cls: 'btn',
21982                             cn: [
21983                                 {
21984                                     tag: 'span',
21985                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21986                                 }
21987                             ]
21988                         }
21989                     ]
21990                 },
21991                 {
21992                     tag: 'td',
21993                     cls: 'separator'
21994                 }
21995             ]
21996         });
21997         
21998     },
21999     
22000     update: function()
22001     {
22002         
22003         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22004         
22005         this.fill();
22006     },
22007     
22008     fill: function() 
22009     {
22010         var hours = this.time.getHours();
22011         var minutes = this.time.getMinutes();
22012         var period = 'AM';
22013         
22014         if(hours > 11){
22015             period = 'PM';
22016         }
22017         
22018         if(hours == 0){
22019             hours = 12;
22020         }
22021         
22022         
22023         if(hours > 12){
22024             hours = hours - 12;
22025         }
22026         
22027         if(hours < 10){
22028             hours = '0' + hours;
22029         }
22030         
22031         if(minutes < 10){
22032             minutes = '0' + minutes;
22033         }
22034         
22035         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22036         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22037         this.pop.select('button', true).first().dom.innerHTML = period;
22038         
22039     },
22040     
22041     place: function()
22042     {   
22043         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22044         
22045         var cls = ['bottom'];
22046         
22047         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22048             cls.pop();
22049             cls.push('top');
22050         }
22051         
22052         cls.push('right');
22053         
22054         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22055             cls.pop();
22056             cls.push('left');
22057         }
22058         
22059         this.picker().addClass(cls.join('-'));
22060         
22061         var _this = this;
22062         
22063         Roo.each(cls, function(c){
22064             if(c == 'bottom'){
22065                 _this.picker().setTop(_this.inputEl().getHeight());
22066                 return;
22067             }
22068             if(c == 'top'){
22069                 _this.picker().setTop(0 - _this.picker().getHeight());
22070                 return;
22071             }
22072             
22073             if(c == 'left'){
22074                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22075                 return;
22076             }
22077             if(c == 'right'){
22078                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22079                 return;
22080             }
22081         });
22082         
22083     },
22084   
22085     onFocus : function()
22086     {
22087         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22088         this.show();
22089     },
22090     
22091     onBlur : function()
22092     {
22093         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22094         this.hide();
22095     },
22096     
22097     show : function()
22098     {
22099         this.picker().show();
22100         this.pop.show();
22101         this.update();
22102         this.place();
22103         
22104         this.fireEvent('show', this, this.date);
22105     },
22106     
22107     hide : function()
22108     {
22109         this.picker().hide();
22110         this.pop.hide();
22111         
22112         this.fireEvent('hide', this, this.date);
22113     },
22114     
22115     setTime : function()
22116     {
22117         this.hide();
22118         this.setValue(this.time.format(this.format));
22119         
22120         this.fireEvent('select', this, this.date);
22121         
22122         
22123     },
22124     
22125     onMousedown: function(e){
22126         e.stopPropagation();
22127         e.preventDefault();
22128     },
22129     
22130     onIncrementHours: function()
22131     {
22132         Roo.log('onIncrementHours');
22133         this.time = this.time.add(Date.HOUR, 1);
22134         this.update();
22135         
22136     },
22137     
22138     onDecrementHours: function()
22139     {
22140         Roo.log('onDecrementHours');
22141         this.time = this.time.add(Date.HOUR, -1);
22142         this.update();
22143     },
22144     
22145     onIncrementMinutes: function()
22146     {
22147         Roo.log('onIncrementMinutes');
22148         this.time = this.time.add(Date.MINUTE, 1);
22149         this.update();
22150     },
22151     
22152     onDecrementMinutes: function()
22153     {
22154         Roo.log('onDecrementMinutes');
22155         this.time = this.time.add(Date.MINUTE, -1);
22156         this.update();
22157     },
22158     
22159     onTogglePeriod: function()
22160     {
22161         Roo.log('onTogglePeriod');
22162         this.time = this.time.add(Date.HOUR, 12);
22163         this.update();
22164     }
22165     
22166    
22167 });
22168
22169 Roo.apply(Roo.bootstrap.TimeField,  {
22170     
22171     content : {
22172         tag: 'tbody',
22173         cn: [
22174             {
22175                 tag: 'tr',
22176                 cn: [
22177                 {
22178                     tag: 'td',
22179                     colspan: '7'
22180                 }
22181                 ]
22182             }
22183         ]
22184     },
22185     
22186     footer : {
22187         tag: 'tfoot',
22188         cn: [
22189             {
22190                 tag: 'tr',
22191                 cn: [
22192                 {
22193                     tag: 'th',
22194                     colspan: '7',
22195                     cls: '',
22196                     cn: [
22197                         {
22198                             tag: 'button',
22199                             cls: 'btn btn-info ok',
22200                             html: 'OK'
22201                         }
22202                     ]
22203                 }
22204
22205                 ]
22206             }
22207         ]
22208     }
22209 });
22210
22211 Roo.apply(Roo.bootstrap.TimeField,  {
22212   
22213     template : {
22214         tag: 'div',
22215         cls: 'datepicker dropdown-menu',
22216         cn: [
22217             {
22218                 tag: 'div',
22219                 cls: 'datepicker-time',
22220                 cn: [
22221                 {
22222                     tag: 'table',
22223                     cls: 'table-condensed',
22224                     cn:[
22225                     Roo.bootstrap.TimeField.content,
22226                     Roo.bootstrap.TimeField.footer
22227                     ]
22228                 }
22229                 ]
22230             }
22231         ]
22232     }
22233 });
22234
22235  
22236
22237  /*
22238  * - LGPL
22239  *
22240  * MonthField
22241  * 
22242  */
22243
22244 /**
22245  * @class Roo.bootstrap.MonthField
22246  * @extends Roo.bootstrap.Input
22247  * Bootstrap MonthField class
22248  * 
22249  * @cfg {String} language default en
22250  * 
22251  * @constructor
22252  * Create a new MonthField
22253  * @param {Object} config The config object
22254  */
22255
22256 Roo.bootstrap.MonthField = function(config){
22257     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22258     
22259     this.addEvents({
22260         /**
22261          * @event show
22262          * Fires when this field show.
22263          * @param {Roo.bootstrap.MonthField} this
22264          * @param {Mixed} date The date value
22265          */
22266         show : true,
22267         /**
22268          * @event show
22269          * Fires when this field hide.
22270          * @param {Roo.bootstrap.MonthField} this
22271          * @param {Mixed} date The date value
22272          */
22273         hide : true,
22274         /**
22275          * @event select
22276          * Fires when select a date.
22277          * @param {Roo.bootstrap.MonthField} this
22278          * @param {String} oldvalue The old value
22279          * @param {String} newvalue The new value
22280          */
22281         select : true
22282     });
22283 };
22284
22285 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22286     
22287     onRender: function(ct, position)
22288     {
22289         
22290         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22291         
22292         this.language = this.language || 'en';
22293         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22294         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22295         
22296         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22297         this.isInline = false;
22298         this.isInput = true;
22299         this.component = this.el.select('.add-on', true).first() || false;
22300         this.component = (this.component && this.component.length === 0) ? false : this.component;
22301         this.hasInput = this.component && this.inputEL().length;
22302         
22303         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22304         
22305         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22306         
22307         this.picker().on('mousedown', this.onMousedown, this);
22308         this.picker().on('click', this.onClick, this);
22309         
22310         this.picker().addClass('datepicker-dropdown');
22311         
22312         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22313             v.setStyle('width', '189px');
22314         });
22315         
22316         this.fillMonths();
22317         
22318         this.update();
22319         
22320         if(this.isInline) {
22321             this.show();
22322         }
22323         
22324     },
22325     
22326     setValue: function(v, suppressEvent)
22327     {   
22328         var o = this.getValue();
22329         
22330         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22331         
22332         this.update();
22333
22334         if(suppressEvent !== true){
22335             this.fireEvent('select', this, o, v);
22336         }
22337         
22338     },
22339     
22340     getValue: function()
22341     {
22342         return this.value;
22343     },
22344     
22345     onClick: function(e) 
22346     {
22347         e.stopPropagation();
22348         e.preventDefault();
22349         
22350         var target = e.getTarget();
22351         
22352         if(target.nodeName.toLowerCase() === 'i'){
22353             target = Roo.get(target).dom.parentNode;
22354         }
22355         
22356         var nodeName = target.nodeName;
22357         var className = target.className;
22358         var html = target.innerHTML;
22359         
22360         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22361             return;
22362         }
22363         
22364         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22365         
22366         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22367         
22368         this.hide();
22369                         
22370     },
22371     
22372     picker : function()
22373     {
22374         return this.pickerEl;
22375     },
22376     
22377     fillMonths: function()
22378     {    
22379         var i = 0;
22380         var months = this.picker().select('>.datepicker-months td', true).first();
22381         
22382         months.dom.innerHTML = '';
22383         
22384         while (i < 12) {
22385             var month = {
22386                 tag: 'span',
22387                 cls: 'month',
22388                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22389             };
22390             
22391             months.createChild(month);
22392         }
22393         
22394     },
22395     
22396     update: function()
22397     {
22398         var _this = this;
22399         
22400         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22401             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22402         }
22403         
22404         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22405             e.removeClass('active');
22406             
22407             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22408                 e.addClass('active');
22409             }
22410         })
22411     },
22412     
22413     place: function()
22414     {
22415         if(this.isInline) {
22416             return;
22417         }
22418         
22419         this.picker().removeClass(['bottom', 'top']);
22420         
22421         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22422             /*
22423              * place to the top of element!
22424              *
22425              */
22426             
22427             this.picker().addClass('top');
22428             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22429             
22430             return;
22431         }
22432         
22433         this.picker().addClass('bottom');
22434         
22435         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22436     },
22437     
22438     onFocus : function()
22439     {
22440         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22441         this.show();
22442     },
22443     
22444     onBlur : function()
22445     {
22446         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22447         
22448         var d = this.inputEl().getValue();
22449         
22450         this.setValue(d);
22451                 
22452         this.hide();
22453     },
22454     
22455     show : function()
22456     {
22457         this.picker().show();
22458         this.picker().select('>.datepicker-months', true).first().show();
22459         this.update();
22460         this.place();
22461         
22462         this.fireEvent('show', this, this.date);
22463     },
22464     
22465     hide : function()
22466     {
22467         if(this.isInline) {
22468             return;
22469         }
22470         this.picker().hide();
22471         this.fireEvent('hide', this, this.date);
22472         
22473     },
22474     
22475     onMousedown: function(e)
22476     {
22477         e.stopPropagation();
22478         e.preventDefault();
22479     },
22480     
22481     keyup: function(e)
22482     {
22483         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22484         this.update();
22485     },
22486
22487     fireKey: function(e)
22488     {
22489         if (!this.picker().isVisible()){
22490             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22491                 this.show();
22492             }
22493             return;
22494         }
22495         
22496         var dir;
22497         
22498         switch(e.keyCode){
22499             case 27: // escape
22500                 this.hide();
22501                 e.preventDefault();
22502                 break;
22503             case 37: // left
22504             case 39: // right
22505                 dir = e.keyCode == 37 ? -1 : 1;
22506                 
22507                 this.vIndex = this.vIndex + dir;
22508                 
22509                 if(this.vIndex < 0){
22510                     this.vIndex = 0;
22511                 }
22512                 
22513                 if(this.vIndex > 11){
22514                     this.vIndex = 11;
22515                 }
22516                 
22517                 if(isNaN(this.vIndex)){
22518                     this.vIndex = 0;
22519                 }
22520                 
22521                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22522                 
22523                 break;
22524             case 38: // up
22525             case 40: // down
22526                 
22527                 dir = e.keyCode == 38 ? -1 : 1;
22528                 
22529                 this.vIndex = this.vIndex + dir * 4;
22530                 
22531                 if(this.vIndex < 0){
22532                     this.vIndex = 0;
22533                 }
22534                 
22535                 if(this.vIndex > 11){
22536                     this.vIndex = 11;
22537                 }
22538                 
22539                 if(isNaN(this.vIndex)){
22540                     this.vIndex = 0;
22541                 }
22542                 
22543                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22544                 break;
22545                 
22546             case 13: // enter
22547                 
22548                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22549                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22550                 }
22551                 
22552                 this.hide();
22553                 e.preventDefault();
22554                 break;
22555             case 9: // tab
22556                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22557                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22558                 }
22559                 this.hide();
22560                 break;
22561             case 16: // shift
22562             case 17: // ctrl
22563             case 18: // alt
22564                 break;
22565             default :
22566                 this.hide();
22567                 
22568         }
22569     },
22570     
22571     remove: function() 
22572     {
22573         this.picker().remove();
22574     }
22575    
22576 });
22577
22578 Roo.apply(Roo.bootstrap.MonthField,  {
22579     
22580     content : {
22581         tag: 'tbody',
22582         cn: [
22583         {
22584             tag: 'tr',
22585             cn: [
22586             {
22587                 tag: 'td',
22588                 colspan: '7'
22589             }
22590             ]
22591         }
22592         ]
22593     },
22594     
22595     dates:{
22596         en: {
22597             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22598             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22599         }
22600     }
22601 });
22602
22603 Roo.apply(Roo.bootstrap.MonthField,  {
22604   
22605     template : {
22606         tag: 'div',
22607         cls: 'datepicker dropdown-menu roo-dynamic',
22608         cn: [
22609             {
22610                 tag: 'div',
22611                 cls: 'datepicker-months',
22612                 cn: [
22613                 {
22614                     tag: 'table',
22615                     cls: 'table-condensed',
22616                     cn:[
22617                         Roo.bootstrap.DateField.content
22618                     ]
22619                 }
22620                 ]
22621             }
22622         ]
22623     }
22624 });
22625
22626  
22627
22628  
22629  /*
22630  * - LGPL
22631  *
22632  * CheckBox
22633  * 
22634  */
22635
22636 /**
22637  * @class Roo.bootstrap.CheckBox
22638  * @extends Roo.bootstrap.Input
22639  * Bootstrap CheckBox class
22640  * 
22641  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22642  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22643  * @cfg {String} boxLabel The text that appears beside the checkbox
22644  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22645  * @cfg {Boolean} checked initnal the element
22646  * @cfg {Boolean} inline inline the element (default false)
22647  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22648  * @cfg {String} tooltip label tooltip
22649  * 
22650  * @constructor
22651  * Create a new CheckBox
22652  * @param {Object} config The config object
22653  */
22654
22655 Roo.bootstrap.CheckBox = function(config){
22656     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22657    
22658     this.addEvents({
22659         /**
22660         * @event check
22661         * Fires when the element is checked or unchecked.
22662         * @param {Roo.bootstrap.CheckBox} this This input
22663         * @param {Boolean} checked The new checked value
22664         */
22665        check : true,
22666        /**
22667         * @event click
22668         * Fires when the element is click.
22669         * @param {Roo.bootstrap.CheckBox} this This input
22670         */
22671        click : true
22672     });
22673     
22674 };
22675
22676 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22677   
22678     inputType: 'checkbox',
22679     inputValue: 1,
22680     valueOff: 0,
22681     boxLabel: false,
22682     checked: false,
22683     weight : false,
22684     inline: false,
22685     tooltip : '',
22686     
22687     // checkbox success does not make any sense really.. 
22688     invalidClass : "",
22689     validClass : "",
22690     
22691     
22692     getAutoCreate : function()
22693     {
22694         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22695         
22696         var id = Roo.id();
22697         
22698         var cfg = {};
22699         
22700         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22701         
22702         if(this.inline){
22703             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22704         }
22705         
22706         var input =  {
22707             tag: 'input',
22708             id : id,
22709             type : this.inputType,
22710             value : this.inputValue,
22711             cls : 'roo-' + this.inputType, //'form-box',
22712             placeholder : this.placeholder || ''
22713             
22714         };
22715         
22716         if(this.inputType != 'radio'){
22717             var hidden =  {
22718                 tag: 'input',
22719                 type : 'hidden',
22720                 cls : 'roo-hidden-value',
22721                 value : this.checked ? this.inputValue : this.valueOff
22722             };
22723         }
22724         
22725             
22726         if (this.weight) { // Validity check?
22727             cfg.cls += " " + this.inputType + "-" + this.weight;
22728         }
22729         
22730         if (this.disabled) {
22731             input.disabled=true;
22732         }
22733         
22734         if(this.checked){
22735             input.checked = this.checked;
22736         }
22737         
22738         if (this.name) {
22739             
22740             input.name = this.name;
22741             
22742             if(this.inputType != 'radio'){
22743                 hidden.name = this.name;
22744                 input.name = '_hidden_' + this.name;
22745             }
22746         }
22747         
22748         if (this.size) {
22749             input.cls += ' input-' + this.size;
22750         }
22751         
22752         var settings=this;
22753         
22754         ['xs','sm','md','lg'].map(function(size){
22755             if (settings[size]) {
22756                 cfg.cls += ' col-' + size + '-' + settings[size];
22757             }
22758         });
22759         
22760         var inputblock = input;
22761          
22762         if (this.before || this.after) {
22763             
22764             inputblock = {
22765                 cls : 'input-group',
22766                 cn :  [] 
22767             };
22768             
22769             if (this.before) {
22770                 inputblock.cn.push({
22771                     tag :'span',
22772                     cls : 'input-group-addon',
22773                     html : this.before
22774                 });
22775             }
22776             
22777             inputblock.cn.push(input);
22778             
22779             if(this.inputType != 'radio'){
22780                 inputblock.cn.push(hidden);
22781             }
22782             
22783             if (this.after) {
22784                 inputblock.cn.push({
22785                     tag :'span',
22786                     cls : 'input-group-addon',
22787                     html : this.after
22788                 });
22789             }
22790             
22791         }
22792         var boxLabelCfg = false;
22793         
22794         if(this.boxLabel){
22795            
22796             boxLabelCfg = {
22797                 tag: 'label',
22798                 //'for': id, // box label is handled by onclick - so no for...
22799                 cls: 'box-label',
22800                 html: this.boxLabel
22801             };
22802             if(this.tooltip){
22803                 boxLabelCfg.tooltip = this.tooltip;
22804             }
22805              
22806         }
22807         
22808         
22809         if (align ==='left' && this.fieldLabel.length) {
22810 //                Roo.log("left and has label");
22811             cfg.cn = [
22812                 {
22813                     tag: 'label',
22814                     'for' :  id,
22815                     cls : 'control-label',
22816                     html : this.fieldLabel
22817                 },
22818                 {
22819                     cls : "", 
22820                     cn: [
22821                         inputblock
22822                     ]
22823                 }
22824             ];
22825             
22826             if (boxLabelCfg) {
22827                 cfg.cn[1].cn.push(boxLabelCfg);
22828             }
22829             
22830             if(this.labelWidth > 12){
22831                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22832             }
22833             
22834             if(this.labelWidth < 13 && this.labelmd == 0){
22835                 this.labelmd = this.labelWidth;
22836             }
22837             
22838             if(this.labellg > 0){
22839                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22840                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22841             }
22842             
22843             if(this.labelmd > 0){
22844                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22845                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22846             }
22847             
22848             if(this.labelsm > 0){
22849                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22850                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22851             }
22852             
22853             if(this.labelxs > 0){
22854                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22855                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22856             }
22857             
22858         } else if ( this.fieldLabel.length) {
22859 //                Roo.log(" label");
22860                 cfg.cn = [
22861                    
22862                     {
22863                         tag: this.boxLabel ? 'span' : 'label',
22864                         'for': id,
22865                         cls: 'control-label box-input-label',
22866                         //cls : 'input-group-addon',
22867                         html : this.fieldLabel
22868                     },
22869                     
22870                     inputblock
22871                     
22872                 ];
22873                 if (boxLabelCfg) {
22874                     cfg.cn.push(boxLabelCfg);
22875                 }
22876
22877         } else {
22878             
22879 //                Roo.log(" no label && no align");
22880                 cfg.cn = [  inputblock ] ;
22881                 if (boxLabelCfg) {
22882                     cfg.cn.push(boxLabelCfg);
22883                 }
22884
22885                 
22886         }
22887         
22888        
22889         
22890         if(this.inputType != 'radio'){
22891             cfg.cn.push(hidden);
22892         }
22893         
22894         return cfg;
22895         
22896     },
22897     
22898     /**
22899      * return the real input element.
22900      */
22901     inputEl: function ()
22902     {
22903         return this.el.select('input.roo-' + this.inputType,true).first();
22904     },
22905     hiddenEl: function ()
22906     {
22907         return this.el.select('input.roo-hidden-value',true).first();
22908     },
22909     
22910     labelEl: function()
22911     {
22912         return this.el.select('label.control-label',true).first();
22913     },
22914     /* depricated... */
22915     
22916     label: function()
22917     {
22918         return this.labelEl();
22919     },
22920     
22921     boxLabelEl: function()
22922     {
22923         return this.el.select('label.box-label',true).first();
22924     },
22925     
22926     initEvents : function()
22927     {
22928 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22929         
22930         this.inputEl().on('click', this.onClick,  this);
22931         
22932         if (this.boxLabel) { 
22933             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22934         }
22935         
22936         this.startValue = this.getValue();
22937         
22938         if(this.groupId){
22939             Roo.bootstrap.CheckBox.register(this);
22940         }
22941     },
22942     
22943     onClick : function(e)
22944     {   
22945         if(this.fireEvent('click', this, e) !== false){
22946             this.setChecked(!this.checked);
22947         }
22948         
22949     },
22950     
22951     setChecked : function(state,suppressEvent)
22952     {
22953         this.startValue = this.getValue();
22954
22955         if(this.inputType == 'radio'){
22956             
22957             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22958                 e.dom.checked = false;
22959             });
22960             
22961             this.inputEl().dom.checked = true;
22962             
22963             this.inputEl().dom.value = this.inputValue;
22964             
22965             if(suppressEvent !== true){
22966                 this.fireEvent('check', this, true);
22967             }
22968             
22969             this.validate();
22970             
22971             return;
22972         }
22973         
22974         this.checked = state;
22975         
22976         this.inputEl().dom.checked = state;
22977         
22978         
22979         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22980         
22981         if(suppressEvent !== true){
22982             this.fireEvent('check', this, state);
22983         }
22984         
22985         this.validate();
22986     },
22987     
22988     getValue : function()
22989     {
22990         if(this.inputType == 'radio'){
22991             return this.getGroupValue();
22992         }
22993         
22994         return this.hiddenEl().dom.value;
22995         
22996     },
22997     
22998     getGroupValue : function()
22999     {
23000         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23001             return '';
23002         }
23003         
23004         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23005     },
23006     
23007     setValue : function(v,suppressEvent)
23008     {
23009         if(this.inputType == 'radio'){
23010             this.setGroupValue(v, suppressEvent);
23011             return;
23012         }
23013         
23014         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23015         
23016         this.validate();
23017     },
23018     
23019     setGroupValue : function(v, suppressEvent)
23020     {
23021         this.startValue = this.getValue();
23022         
23023         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23024             e.dom.checked = false;
23025             
23026             if(e.dom.value == v){
23027                 e.dom.checked = true;
23028             }
23029         });
23030         
23031         if(suppressEvent !== true){
23032             this.fireEvent('check', this, true);
23033         }
23034
23035         this.validate();
23036         
23037         return;
23038     },
23039     
23040     validate : function()
23041     {
23042         if(this.getVisibilityEl().hasClass('hidden')){
23043             return true;
23044         }
23045         
23046         if(
23047                 this.disabled || 
23048                 (this.inputType == 'radio' && this.validateRadio()) ||
23049                 (this.inputType == 'checkbox' && this.validateCheckbox())
23050         ){
23051             this.markValid();
23052             return true;
23053         }
23054         
23055         this.markInvalid();
23056         return false;
23057     },
23058     
23059     validateRadio : function()
23060     {
23061         if(this.getVisibilityEl().hasClass('hidden')){
23062             return true;
23063         }
23064         
23065         if(this.allowBlank){
23066             return true;
23067         }
23068         
23069         var valid = false;
23070         
23071         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23072             if(!e.dom.checked){
23073                 return;
23074             }
23075             
23076             valid = true;
23077             
23078             return false;
23079         });
23080         
23081         return valid;
23082     },
23083     
23084     validateCheckbox : function()
23085     {
23086         if(!this.groupId){
23087             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23088             //return (this.getValue() == this.inputValue) ? true : false;
23089         }
23090         
23091         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23092         
23093         if(!group){
23094             return false;
23095         }
23096         
23097         var r = false;
23098         
23099         for(var i in group){
23100             if(group[i].el.isVisible(true)){
23101                 r = false;
23102                 break;
23103             }
23104             
23105             r = true;
23106         }
23107         
23108         for(var i in group){
23109             if(r){
23110                 break;
23111             }
23112             
23113             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23114         }
23115         
23116         return r;
23117     },
23118     
23119     /**
23120      * Mark this field as valid
23121      */
23122     markValid : function()
23123     {
23124         var _this = this;
23125         
23126         this.fireEvent('valid', this);
23127         
23128         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23129         
23130         if(this.groupId){
23131             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23132         }
23133         
23134         if(label){
23135             label.markValid();
23136         }
23137
23138         if(this.inputType == 'radio'){
23139             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140                 var fg = e.findParent('.form-group', false, true);
23141                 if (Roo.bootstrap.version == 3) {
23142                     fg.removeClass([_this.invalidClass, _this.validClass]);
23143                     fg.addClass(_this.validClass);
23144                 } else {
23145                     fg.removeClass(['is-valid', 'is-invalid']);
23146                     fg.addClass('is-valid');
23147                 }
23148             });
23149             
23150             return;
23151         }
23152
23153         if(!this.groupId){
23154             var fg = this.el.findParent('.form-group', false, true);
23155             if (Roo.bootstrap.version == 3) {
23156                 fg.removeClass([this.invalidClass, this.validClass]);
23157                 fg.addClass(this.validClass);
23158             } else {
23159                 fg.removeClass(['is-valid', 'is-invalid']);
23160                 fg.addClass('is-valid');
23161             }
23162             return;
23163         }
23164         
23165         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23166         
23167         if(!group){
23168             return;
23169         }
23170         
23171         for(var i in group){
23172             var fg = group[i].el.findParent('.form-group', false, true);
23173             if (Roo.bootstrap.version == 3) {
23174                 fg.removeClass([this.invalidClass, this.validClass]);
23175                 fg.addClass(this.validClass);
23176             } else {
23177                 fg.removeClass(['is-valid', 'is-invalid']);
23178                 fg.addClass('is-valid');
23179             }
23180         }
23181     },
23182     
23183      /**
23184      * Mark this field as invalid
23185      * @param {String} msg The validation message
23186      */
23187     markInvalid : function(msg)
23188     {
23189         if(this.allowBlank){
23190             return;
23191         }
23192         
23193         var _this = this;
23194         
23195         this.fireEvent('invalid', this, msg);
23196         
23197         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23198         
23199         if(this.groupId){
23200             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23201         }
23202         
23203         if(label){
23204             label.markInvalid();
23205         }
23206             
23207         if(this.inputType == 'radio'){
23208             
23209             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23210                 var fg = e.findParent('.form-group', false, true);
23211                 if (Roo.bootstrap.version == 3) {
23212                     fg.removeClass([_this.invalidClass, _this.validClass]);
23213                     fg.addClass(_this.invalidClass);
23214                 } else {
23215                     fg.removeClass(['is-invalid', 'is-valid']);
23216                     fg.addClass('is-invalid');
23217                 }
23218             });
23219             
23220             return;
23221         }
23222         
23223         if(!this.groupId){
23224             var fg = this.el.findParent('.form-group', false, true);
23225             if (Roo.bootstrap.version == 3) {
23226                 fg.removeClass([_this.invalidClass, _this.validClass]);
23227                 fg.addClass(_this.invalidClass);
23228             } else {
23229                 fg.removeClass(['is-invalid', 'is-valid']);
23230                 fg.addClass('is-invalid');
23231             }
23232             return;
23233         }
23234         
23235         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23236         
23237         if(!group){
23238             return;
23239         }
23240         
23241         for(var i in group){
23242             var fg = group[i].el.findParent('.form-group', false, true);
23243             if (Roo.bootstrap.version == 3) {
23244                 fg.removeClass([_this.invalidClass, _this.validClass]);
23245                 fg.addClass(_this.invalidClass);
23246             } else {
23247                 fg.removeClass(['is-invalid', 'is-valid']);
23248                 fg.addClass('is-invalid');
23249             }
23250         }
23251         
23252     },
23253     
23254     clearInvalid : function()
23255     {
23256         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23257         
23258         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23259         
23260         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23261         
23262         if (label && label.iconEl) {
23263             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23264             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23265         }
23266     },
23267     
23268     disable : function()
23269     {
23270         if(this.inputType != 'radio'){
23271             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23272             return;
23273         }
23274         
23275         var _this = this;
23276         
23277         if(this.rendered){
23278             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23279                 _this.getActionEl().addClass(this.disabledClass);
23280                 e.dom.disabled = true;
23281             });
23282         }
23283         
23284         this.disabled = true;
23285         this.fireEvent("disable", this);
23286         return this;
23287     },
23288
23289     enable : function()
23290     {
23291         if(this.inputType != 'radio'){
23292             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23293             return;
23294         }
23295         
23296         var _this = this;
23297         
23298         if(this.rendered){
23299             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23300                 _this.getActionEl().removeClass(this.disabledClass);
23301                 e.dom.disabled = false;
23302             });
23303         }
23304         
23305         this.disabled = false;
23306         this.fireEvent("enable", this);
23307         return this;
23308     },
23309     
23310     setBoxLabel : function(v)
23311     {
23312         this.boxLabel = v;
23313         
23314         if(this.rendered){
23315             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23316         }
23317     }
23318
23319 });
23320
23321 Roo.apply(Roo.bootstrap.CheckBox, {
23322     
23323     groups: {},
23324     
23325      /**
23326     * register a CheckBox Group
23327     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23328     */
23329     register : function(checkbox)
23330     {
23331         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23332             this.groups[checkbox.groupId] = {};
23333         }
23334         
23335         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23336             return;
23337         }
23338         
23339         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23340         
23341     },
23342     /**
23343     * fetch a CheckBox Group based on the group ID
23344     * @param {string} the group ID
23345     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23346     */
23347     get: function(groupId) {
23348         if (typeof(this.groups[groupId]) == 'undefined') {
23349             return false;
23350         }
23351         
23352         return this.groups[groupId] ;
23353     }
23354     
23355     
23356 });
23357 /*
23358  * - LGPL
23359  *
23360  * RadioItem
23361  * 
23362  */
23363
23364 /**
23365  * @class Roo.bootstrap.Radio
23366  * @extends Roo.bootstrap.Component
23367  * Bootstrap Radio class
23368  * @cfg {String} boxLabel - the label associated
23369  * @cfg {String} value - the value of radio
23370  * 
23371  * @constructor
23372  * Create a new Radio
23373  * @param {Object} config The config object
23374  */
23375 Roo.bootstrap.Radio = function(config){
23376     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23377     
23378 };
23379
23380 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23381     
23382     boxLabel : '',
23383     
23384     value : '',
23385     
23386     getAutoCreate : function()
23387     {
23388         var cfg = {
23389             tag : 'div',
23390             cls : 'form-group radio',
23391             cn : [
23392                 {
23393                     tag : 'label',
23394                     cls : 'box-label',
23395                     html : this.boxLabel
23396                 }
23397             ]
23398         };
23399         
23400         return cfg;
23401     },
23402     
23403     initEvents : function() 
23404     {
23405         this.parent().register(this);
23406         
23407         this.el.on('click', this.onClick, this);
23408         
23409     },
23410     
23411     onClick : function(e)
23412     {
23413         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23414             this.setChecked(true);
23415         }
23416     },
23417     
23418     setChecked : function(state, suppressEvent)
23419     {
23420         this.parent().setValue(this.value, suppressEvent);
23421         
23422     },
23423     
23424     setBoxLabel : function(v)
23425     {
23426         this.boxLabel = v;
23427         
23428         if(this.rendered){
23429             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23430         }
23431     }
23432     
23433 });
23434  
23435
23436  /*
23437  * - LGPL
23438  *
23439  * Input
23440  * 
23441  */
23442
23443 /**
23444  * @class Roo.bootstrap.SecurePass
23445  * @extends Roo.bootstrap.Input
23446  * Bootstrap SecurePass class
23447  *
23448  * 
23449  * @constructor
23450  * Create a new SecurePass
23451  * @param {Object} config The config object
23452  */
23453  
23454 Roo.bootstrap.SecurePass = function (config) {
23455     // these go here, so the translation tool can replace them..
23456     this.errors = {
23457         PwdEmpty: "Please type a password, and then retype it to confirm.",
23458         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23459         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23460         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23461         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23462         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23463         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23464         TooWeak: "Your password is Too Weak."
23465     },
23466     this.meterLabel = "Password strength:";
23467     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23468     this.meterClass = [
23469         "roo-password-meter-tooweak", 
23470         "roo-password-meter-weak", 
23471         "roo-password-meter-medium", 
23472         "roo-password-meter-strong", 
23473         "roo-password-meter-grey"
23474     ];
23475     
23476     this.errors = {};
23477     
23478     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23479 }
23480
23481 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23482     /**
23483      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23484      * {
23485      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23486      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23487      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23488      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23489      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23490      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23491      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23492      * })
23493      */
23494     // private
23495     
23496     meterWidth: 300,
23497     errorMsg :'',    
23498     errors: false,
23499     imageRoot: '/',
23500     /**
23501      * @cfg {String/Object} Label for the strength meter (defaults to
23502      * 'Password strength:')
23503      */
23504     // private
23505     meterLabel: '',
23506     /**
23507      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23508      * ['Weak', 'Medium', 'Strong'])
23509      */
23510     // private    
23511     pwdStrengths: false,    
23512     // private
23513     strength: 0,
23514     // private
23515     _lastPwd: null,
23516     // private
23517     kCapitalLetter: 0,
23518     kSmallLetter: 1,
23519     kDigit: 2,
23520     kPunctuation: 3,
23521     
23522     insecure: false,
23523     // private
23524     initEvents: function ()
23525     {
23526         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23527
23528         if (this.el.is('input[type=password]') && Roo.isSafari) {
23529             this.el.on('keydown', this.SafariOnKeyDown, this);
23530         }
23531
23532         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23533     },
23534     // private
23535     onRender: function (ct, position)
23536     {
23537         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23538         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23539         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23540
23541         this.trigger.createChild({
23542                    cn: [
23543                     {
23544                     //id: 'PwdMeter',
23545                     tag: 'div',
23546                     cls: 'roo-password-meter-grey col-xs-12',
23547                     style: {
23548                         //width: 0,
23549                         //width: this.meterWidth + 'px'                                                
23550                         }
23551                     },
23552                     {                            
23553                          cls: 'roo-password-meter-text'                          
23554                     }
23555                 ]            
23556         });
23557
23558          
23559         if (this.hideTrigger) {
23560             this.trigger.setDisplayed(false);
23561         }
23562         this.setSize(this.width || '', this.height || '');
23563     },
23564     // private
23565     onDestroy: function ()
23566     {
23567         if (this.trigger) {
23568             this.trigger.removeAllListeners();
23569             this.trigger.remove();
23570         }
23571         if (this.wrap) {
23572             this.wrap.remove();
23573         }
23574         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23575     },
23576     // private
23577     checkStrength: function ()
23578     {
23579         var pwd = this.inputEl().getValue();
23580         if (pwd == this._lastPwd) {
23581             return;
23582         }
23583
23584         var strength;
23585         if (this.ClientSideStrongPassword(pwd)) {
23586             strength = 3;
23587         } else if (this.ClientSideMediumPassword(pwd)) {
23588             strength = 2;
23589         } else if (this.ClientSideWeakPassword(pwd)) {
23590             strength = 1;
23591         } else {
23592             strength = 0;
23593         }
23594         
23595         Roo.log('strength1: ' + strength);
23596         
23597         //var pm = this.trigger.child('div/div/div').dom;
23598         var pm = this.trigger.child('div/div');
23599         pm.removeClass(this.meterClass);
23600         pm.addClass(this.meterClass[strength]);
23601                 
23602         
23603         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23604                 
23605         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23606         
23607         this._lastPwd = pwd;
23608     },
23609     reset: function ()
23610     {
23611         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23612         
23613         this._lastPwd = '';
23614         
23615         var pm = this.trigger.child('div/div');
23616         pm.removeClass(this.meterClass);
23617         pm.addClass('roo-password-meter-grey');        
23618         
23619         
23620         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23621         
23622         pt.innerHTML = '';
23623         this.inputEl().dom.type='password';
23624     },
23625     // private
23626     validateValue: function (value)
23627     {
23628         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23629             return false;
23630         }
23631         if (value.length == 0) {
23632             if (this.allowBlank) {
23633                 this.clearInvalid();
23634                 return true;
23635             }
23636
23637             this.markInvalid(this.errors.PwdEmpty);
23638             this.errorMsg = this.errors.PwdEmpty;
23639             return false;
23640         }
23641         
23642         if(this.insecure){
23643             return true;
23644         }
23645         
23646         if (!value.match(/[\x21-\x7e]+/)) {
23647             this.markInvalid(this.errors.PwdBadChar);
23648             this.errorMsg = this.errors.PwdBadChar;
23649             return false;
23650         }
23651         if (value.length < 6) {
23652             this.markInvalid(this.errors.PwdShort);
23653             this.errorMsg = this.errors.PwdShort;
23654             return false;
23655         }
23656         if (value.length > 16) {
23657             this.markInvalid(this.errors.PwdLong);
23658             this.errorMsg = this.errors.PwdLong;
23659             return false;
23660         }
23661         var strength;
23662         if (this.ClientSideStrongPassword(value)) {
23663             strength = 3;
23664         } else if (this.ClientSideMediumPassword(value)) {
23665             strength = 2;
23666         } else if (this.ClientSideWeakPassword(value)) {
23667             strength = 1;
23668         } else {
23669             strength = 0;
23670         }
23671
23672         
23673         if (strength < 2) {
23674             //this.markInvalid(this.errors.TooWeak);
23675             this.errorMsg = this.errors.TooWeak;
23676             //return false;
23677         }
23678         
23679         
23680         console.log('strength2: ' + strength);
23681         
23682         //var pm = this.trigger.child('div/div/div').dom;
23683         
23684         var pm = this.trigger.child('div/div');
23685         pm.removeClass(this.meterClass);
23686         pm.addClass(this.meterClass[strength]);
23687                 
23688         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23689                 
23690         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23691         
23692         this.errorMsg = ''; 
23693         return true;
23694     },
23695     // private
23696     CharacterSetChecks: function (type)
23697     {
23698         this.type = type;
23699         this.fResult = false;
23700     },
23701     // private
23702     isctype: function (character, type)
23703     {
23704         switch (type) {  
23705             case this.kCapitalLetter:
23706                 if (character >= 'A' && character <= 'Z') {
23707                     return true;
23708                 }
23709                 break;
23710             
23711             case this.kSmallLetter:
23712                 if (character >= 'a' && character <= 'z') {
23713                     return true;
23714                 }
23715                 break;
23716             
23717             case this.kDigit:
23718                 if (character >= '0' && character <= '9') {
23719                     return true;
23720                 }
23721                 break;
23722             
23723             case this.kPunctuation:
23724                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23725                     return true;
23726                 }
23727                 break;
23728             
23729             default:
23730                 return false;
23731         }
23732
23733     },
23734     // private
23735     IsLongEnough: function (pwd, size)
23736     {
23737         return !(pwd == null || isNaN(size) || pwd.length < size);
23738     },
23739     // private
23740     SpansEnoughCharacterSets: function (word, nb)
23741     {
23742         if (!this.IsLongEnough(word, nb))
23743         {
23744             return false;
23745         }
23746
23747         var characterSetChecks = new Array(
23748             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23749             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23750         );
23751         
23752         for (var index = 0; index < word.length; ++index) {
23753             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23754                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23755                     characterSetChecks[nCharSet].fResult = true;
23756                     break;
23757                 }
23758             }
23759         }
23760
23761         var nCharSets = 0;
23762         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23763             if (characterSetChecks[nCharSet].fResult) {
23764                 ++nCharSets;
23765             }
23766         }
23767
23768         if (nCharSets < nb) {
23769             return false;
23770         }
23771         return true;
23772     },
23773     // private
23774     ClientSideStrongPassword: function (pwd)
23775     {
23776         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23777     },
23778     // private
23779     ClientSideMediumPassword: function (pwd)
23780     {
23781         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23782     },
23783     // private
23784     ClientSideWeakPassword: function (pwd)
23785     {
23786         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23787     }
23788           
23789 })//<script type="text/javascript">
23790
23791 /*
23792  * Based  Ext JS Library 1.1.1
23793  * Copyright(c) 2006-2007, Ext JS, LLC.
23794  * LGPL
23795  *
23796  */
23797  
23798 /**
23799  * @class Roo.HtmlEditorCore
23800  * @extends Roo.Component
23801  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23802  *
23803  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23804  */
23805
23806 Roo.HtmlEditorCore = function(config){
23807     
23808     
23809     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23810     
23811     
23812     this.addEvents({
23813         /**
23814          * @event initialize
23815          * Fires when the editor is fully initialized (including the iframe)
23816          * @param {Roo.HtmlEditorCore} this
23817          */
23818         initialize: true,
23819         /**
23820          * @event activate
23821          * Fires when the editor is first receives the focus. Any insertion must wait
23822          * until after this event.
23823          * @param {Roo.HtmlEditorCore} this
23824          */
23825         activate: true,
23826          /**
23827          * @event beforesync
23828          * Fires before the textarea is updated with content from the editor iframe. Return false
23829          * to cancel the sync.
23830          * @param {Roo.HtmlEditorCore} this
23831          * @param {String} html
23832          */
23833         beforesync: true,
23834          /**
23835          * @event beforepush
23836          * Fires before the iframe editor is updated with content from the textarea. Return false
23837          * to cancel the push.
23838          * @param {Roo.HtmlEditorCore} this
23839          * @param {String} html
23840          */
23841         beforepush: true,
23842          /**
23843          * @event sync
23844          * Fires when the textarea is updated with content from the editor iframe.
23845          * @param {Roo.HtmlEditorCore} this
23846          * @param {String} html
23847          */
23848         sync: true,
23849          /**
23850          * @event push
23851          * Fires when the iframe editor is updated with content from the textarea.
23852          * @param {Roo.HtmlEditorCore} this
23853          * @param {String} html
23854          */
23855         push: true,
23856         
23857         /**
23858          * @event editorevent
23859          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23860          * @param {Roo.HtmlEditorCore} this
23861          */
23862         editorevent: true
23863         
23864     });
23865     
23866     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23867     
23868     // defaults : white / black...
23869     this.applyBlacklists();
23870     
23871     
23872     
23873 };
23874
23875
23876 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23877
23878
23879      /**
23880      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23881      */
23882     
23883     owner : false,
23884     
23885      /**
23886      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23887      *                        Roo.resizable.
23888      */
23889     resizable : false,
23890      /**
23891      * @cfg {Number} height (in pixels)
23892      */   
23893     height: 300,
23894    /**
23895      * @cfg {Number} width (in pixels)
23896      */   
23897     width: 500,
23898     
23899     /**
23900      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23901      * 
23902      */
23903     stylesheets: false,
23904     
23905     // id of frame..
23906     frameId: false,
23907     
23908     // private properties
23909     validationEvent : false,
23910     deferHeight: true,
23911     initialized : false,
23912     activated : false,
23913     sourceEditMode : false,
23914     onFocus : Roo.emptyFn,
23915     iframePad:3,
23916     hideMode:'offsets',
23917     
23918     clearUp: true,
23919     
23920     // blacklist + whitelisted elements..
23921     black: false,
23922     white: false,
23923      
23924     bodyCls : '',
23925
23926     /**
23927      * Protected method that will not generally be called directly. It
23928      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23929      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23930      */
23931     getDocMarkup : function(){
23932         // body styles..
23933         var st = '';
23934         
23935         // inherit styels from page...?? 
23936         if (this.stylesheets === false) {
23937             
23938             Roo.get(document.head).select('style').each(function(node) {
23939                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23940             });
23941             
23942             Roo.get(document.head).select('link').each(function(node) { 
23943                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23944             });
23945             
23946         } else if (!this.stylesheets.length) {
23947                 // simple..
23948                 st = '<style type="text/css">' +
23949                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23950                    '</style>';
23951         } else {
23952             for (var i in this.stylesheets) { 
23953                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23954             }
23955             
23956         }
23957         
23958         st +=  '<style type="text/css">' +
23959             'IMG { cursor: pointer } ' +
23960         '</style>';
23961
23962         var cls = 'roo-htmleditor-body';
23963         
23964         if(this.bodyCls.length){
23965             cls += ' ' + this.bodyCls;
23966         }
23967         
23968         return '<html><head>' + st  +
23969             //<style type="text/css">' +
23970             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23971             //'</style>' +
23972             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23973     },
23974
23975     // private
23976     onRender : function(ct, position)
23977     {
23978         var _t = this;
23979         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23980         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23981         
23982         
23983         this.el.dom.style.border = '0 none';
23984         this.el.dom.setAttribute('tabIndex', -1);
23985         this.el.addClass('x-hidden hide');
23986         
23987         
23988         
23989         if(Roo.isIE){ // fix IE 1px bogus margin
23990             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23991         }
23992        
23993         
23994         this.frameId = Roo.id();
23995         
23996          
23997         
23998         var iframe = this.owner.wrap.createChild({
23999             tag: 'iframe',
24000             cls: 'form-control', // bootstrap..
24001             id: this.frameId,
24002             name: this.frameId,
24003             frameBorder : 'no',
24004             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24005         }, this.el
24006         );
24007         
24008         
24009         this.iframe = iframe.dom;
24010
24011          this.assignDocWin();
24012         
24013         this.doc.designMode = 'on';
24014        
24015         this.doc.open();
24016         this.doc.write(this.getDocMarkup());
24017         this.doc.close();
24018
24019         
24020         var task = { // must defer to wait for browser to be ready
24021             run : function(){
24022                 //console.log("run task?" + this.doc.readyState);
24023                 this.assignDocWin();
24024                 if(this.doc.body || this.doc.readyState == 'complete'){
24025                     try {
24026                         this.doc.designMode="on";
24027                     } catch (e) {
24028                         return;
24029                     }
24030                     Roo.TaskMgr.stop(task);
24031                     this.initEditor.defer(10, this);
24032                 }
24033             },
24034             interval : 10,
24035             duration: 10000,
24036             scope: this
24037         };
24038         Roo.TaskMgr.start(task);
24039
24040     },
24041
24042     // private
24043     onResize : function(w, h)
24044     {
24045          Roo.log('resize: ' +w + ',' + h );
24046         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24047         if(!this.iframe){
24048             return;
24049         }
24050         if(typeof w == 'number'){
24051             
24052             this.iframe.style.width = w + 'px';
24053         }
24054         if(typeof h == 'number'){
24055             
24056             this.iframe.style.height = h + 'px';
24057             if(this.doc){
24058                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24059             }
24060         }
24061         
24062     },
24063
24064     /**
24065      * Toggles the editor between standard and source edit mode.
24066      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24067      */
24068     toggleSourceEdit : function(sourceEditMode){
24069         
24070         this.sourceEditMode = sourceEditMode === true;
24071         
24072         if(this.sourceEditMode){
24073  
24074             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24075             
24076         }else{
24077             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24078             //this.iframe.className = '';
24079             this.deferFocus();
24080         }
24081         //this.setSize(this.owner.wrap.getSize());
24082         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24083     },
24084
24085     
24086   
24087
24088     /**
24089      * Protected method that will not generally be called directly. If you need/want
24090      * custom HTML cleanup, this is the method you should override.
24091      * @param {String} html The HTML to be cleaned
24092      * return {String} The cleaned HTML
24093      */
24094     cleanHtml : function(html){
24095         html = String(html);
24096         if(html.length > 5){
24097             if(Roo.isSafari){ // strip safari nonsense
24098                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24099             }
24100         }
24101         if(html == '&nbsp;'){
24102             html = '';
24103         }
24104         return html;
24105     },
24106
24107     /**
24108      * HTML Editor -> Textarea
24109      * Protected method that will not generally be called directly. Syncs the contents
24110      * of the editor iframe with the textarea.
24111      */
24112     syncValue : function(){
24113         if(this.initialized){
24114             var bd = (this.doc.body || this.doc.documentElement);
24115             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24116             var html = bd.innerHTML;
24117             if(Roo.isSafari){
24118                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24119                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24120                 if(m && m[1]){
24121                     html = '<div style="'+m[0]+'">' + html + '</div>';
24122                 }
24123             }
24124             html = this.cleanHtml(html);
24125             // fix up the special chars.. normaly like back quotes in word...
24126             // however we do not want to do this with chinese..
24127             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24128                 
24129                 var cc = match.charCodeAt();
24130
24131                 // Get the character value, handling surrogate pairs
24132                 if (match.length == 2) {
24133                     // It's a surrogate pair, calculate the Unicode code point
24134                     var high = match.charCodeAt(0) - 0xD800;
24135                     var low  = match.charCodeAt(1) - 0xDC00;
24136                     cc = (high * 0x400) + low + 0x10000;
24137                 }  else if (
24138                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24139                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24140                     (cc >= 0xf900 && cc < 0xfb00 )
24141                 ) {
24142                         return match;
24143                 }  
24144          
24145                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24146                 return "&#" + cc + ";";
24147                 
24148                 
24149             });
24150             
24151             
24152              
24153             if(this.owner.fireEvent('beforesync', this, html) !== false){
24154                 this.el.dom.value = html;
24155                 this.owner.fireEvent('sync', this, html);
24156             }
24157         }
24158     },
24159
24160     /**
24161      * Protected method that will not generally be called directly. Pushes the value of the textarea
24162      * into the iframe editor.
24163      */
24164     pushValue : function(){
24165         if(this.initialized){
24166             var v = this.el.dom.value.trim();
24167             
24168 //            if(v.length < 1){
24169 //                v = '&#160;';
24170 //            }
24171             
24172             if(this.owner.fireEvent('beforepush', this, v) !== false){
24173                 var d = (this.doc.body || this.doc.documentElement);
24174                 d.innerHTML = v;
24175                 this.cleanUpPaste();
24176                 this.el.dom.value = d.innerHTML;
24177                 this.owner.fireEvent('push', this, v);
24178             }
24179         }
24180     },
24181
24182     // private
24183     deferFocus : function(){
24184         this.focus.defer(10, this);
24185     },
24186
24187     // doc'ed in Field
24188     focus : function(){
24189         if(this.win && !this.sourceEditMode){
24190             this.win.focus();
24191         }else{
24192             this.el.focus();
24193         }
24194     },
24195     
24196     assignDocWin: function()
24197     {
24198         var iframe = this.iframe;
24199         
24200          if(Roo.isIE){
24201             this.doc = iframe.contentWindow.document;
24202             this.win = iframe.contentWindow;
24203         } else {
24204 //            if (!Roo.get(this.frameId)) {
24205 //                return;
24206 //            }
24207 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24208 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24209             
24210             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24211                 return;
24212             }
24213             
24214             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24215             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24216         }
24217     },
24218     
24219     // private
24220     initEditor : function(){
24221         //console.log("INIT EDITOR");
24222         this.assignDocWin();
24223         
24224         
24225         
24226         this.doc.designMode="on";
24227         this.doc.open();
24228         this.doc.write(this.getDocMarkup());
24229         this.doc.close();
24230         
24231         var dbody = (this.doc.body || this.doc.documentElement);
24232         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24233         // this copies styles from the containing element into thsi one..
24234         // not sure why we need all of this..
24235         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24236         
24237         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24238         //ss['background-attachment'] = 'fixed'; // w3c
24239         dbody.bgProperties = 'fixed'; // ie
24240         //Roo.DomHelper.applyStyles(dbody, ss);
24241         Roo.EventManager.on(this.doc, {
24242             //'mousedown': this.onEditorEvent,
24243             'mouseup': this.onEditorEvent,
24244             'dblclick': this.onEditorEvent,
24245             'click': this.onEditorEvent,
24246             'keyup': this.onEditorEvent,
24247             buffer:100,
24248             scope: this
24249         });
24250         if(Roo.isGecko){
24251             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24252         }
24253         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24254             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24255         }
24256         this.initialized = true;
24257
24258         this.owner.fireEvent('initialize', this);
24259         this.pushValue();
24260     },
24261
24262     // private
24263     onDestroy : function(){
24264         
24265         
24266         
24267         if(this.rendered){
24268             
24269             //for (var i =0; i < this.toolbars.length;i++) {
24270             //    // fixme - ask toolbars for heights?
24271             //    this.toolbars[i].onDestroy();
24272            // }
24273             
24274             //this.wrap.dom.innerHTML = '';
24275             //this.wrap.remove();
24276         }
24277     },
24278
24279     // private
24280     onFirstFocus : function(){
24281         
24282         this.assignDocWin();
24283         
24284         
24285         this.activated = true;
24286          
24287     
24288         if(Roo.isGecko){ // prevent silly gecko errors
24289             this.win.focus();
24290             var s = this.win.getSelection();
24291             if(!s.focusNode || s.focusNode.nodeType != 3){
24292                 var r = s.getRangeAt(0);
24293                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24294                 r.collapse(true);
24295                 this.deferFocus();
24296             }
24297             try{
24298                 this.execCmd('useCSS', true);
24299                 this.execCmd('styleWithCSS', false);
24300             }catch(e){}
24301         }
24302         this.owner.fireEvent('activate', this);
24303     },
24304
24305     // private
24306     adjustFont: function(btn){
24307         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24308         //if(Roo.isSafari){ // safari
24309         //    adjust *= 2;
24310        // }
24311         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24312         if(Roo.isSafari){ // safari
24313             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24314             v =  (v < 10) ? 10 : v;
24315             v =  (v > 48) ? 48 : v;
24316             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24317             
24318         }
24319         
24320         
24321         v = Math.max(1, v+adjust);
24322         
24323         this.execCmd('FontSize', v  );
24324     },
24325
24326     onEditorEvent : function(e)
24327     {
24328         this.owner.fireEvent('editorevent', this, e);
24329       //  this.updateToolbar();
24330         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24331     },
24332
24333     insertTag : function(tg)
24334     {
24335         // could be a bit smarter... -> wrap the current selected tRoo..
24336         if (tg.toLowerCase() == 'span' ||
24337             tg.toLowerCase() == 'code' ||
24338             tg.toLowerCase() == 'sup' ||
24339             tg.toLowerCase() == 'sub' 
24340             ) {
24341             
24342             range = this.createRange(this.getSelection());
24343             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24344             wrappingNode.appendChild(range.extractContents());
24345             range.insertNode(wrappingNode);
24346
24347             return;
24348             
24349             
24350             
24351         }
24352         this.execCmd("formatblock",   tg);
24353         
24354     },
24355     
24356     insertText : function(txt)
24357     {
24358         
24359         
24360         var range = this.createRange();
24361         range.deleteContents();
24362                //alert(Sender.getAttribute('label'));
24363                
24364         range.insertNode(this.doc.createTextNode(txt));
24365     } ,
24366     
24367      
24368
24369     /**
24370      * Executes a Midas editor command on the editor document and performs necessary focus and
24371      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24372      * @param {String} cmd The Midas command
24373      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24374      */
24375     relayCmd : function(cmd, value){
24376         this.win.focus();
24377         this.execCmd(cmd, value);
24378         this.owner.fireEvent('editorevent', this);
24379         //this.updateToolbar();
24380         this.owner.deferFocus();
24381     },
24382
24383     /**
24384      * Executes a Midas editor command directly on the editor document.
24385      * For visual commands, you should use {@link #relayCmd} instead.
24386      * <b>This should only be called after the editor is initialized.</b>
24387      * @param {String} cmd The Midas command
24388      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24389      */
24390     execCmd : function(cmd, value){
24391         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24392         this.syncValue();
24393     },
24394  
24395  
24396    
24397     /**
24398      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24399      * to insert tRoo.
24400      * @param {String} text | dom node.. 
24401      */
24402     insertAtCursor : function(text)
24403     {
24404         
24405         if(!this.activated){
24406             return;
24407         }
24408         /*
24409         if(Roo.isIE){
24410             this.win.focus();
24411             var r = this.doc.selection.createRange();
24412             if(r){
24413                 r.collapse(true);
24414                 r.pasteHTML(text);
24415                 this.syncValue();
24416                 this.deferFocus();
24417             
24418             }
24419             return;
24420         }
24421         */
24422         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24423             this.win.focus();
24424             
24425             
24426             // from jquery ui (MIT licenced)
24427             var range, node;
24428             var win = this.win;
24429             
24430             if (win.getSelection && win.getSelection().getRangeAt) {
24431                 range = win.getSelection().getRangeAt(0);
24432                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24433                 range.insertNode(node);
24434             } else if (win.document.selection && win.document.selection.createRange) {
24435                 // no firefox support
24436                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24437                 win.document.selection.createRange().pasteHTML(txt);
24438             } else {
24439                 // no firefox support
24440                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24441                 this.execCmd('InsertHTML', txt);
24442             } 
24443             
24444             this.syncValue();
24445             
24446             this.deferFocus();
24447         }
24448     },
24449  // private
24450     mozKeyPress : function(e){
24451         if(e.ctrlKey){
24452             var c = e.getCharCode(), cmd;
24453           
24454             if(c > 0){
24455                 c = String.fromCharCode(c).toLowerCase();
24456                 switch(c){
24457                     case 'b':
24458                         cmd = 'bold';
24459                         break;
24460                     case 'i':
24461                         cmd = 'italic';
24462                         break;
24463                     
24464                     case 'u':
24465                         cmd = 'underline';
24466                         break;
24467                     
24468                     case 'v':
24469                         this.cleanUpPaste.defer(100, this);
24470                         return;
24471                         
24472                 }
24473                 if(cmd){
24474                     this.win.focus();
24475                     this.execCmd(cmd);
24476                     this.deferFocus();
24477                     e.preventDefault();
24478                 }
24479                 
24480             }
24481         }
24482     },
24483
24484     // private
24485     fixKeys : function(){ // load time branching for fastest keydown performance
24486         if(Roo.isIE){
24487             return function(e){
24488                 var k = e.getKey(), r;
24489                 if(k == e.TAB){
24490                     e.stopEvent();
24491                     r = this.doc.selection.createRange();
24492                     if(r){
24493                         r.collapse(true);
24494                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24495                         this.deferFocus();
24496                     }
24497                     return;
24498                 }
24499                 
24500                 if(k == e.ENTER){
24501                     r = this.doc.selection.createRange();
24502                     if(r){
24503                         var target = r.parentElement();
24504                         if(!target || target.tagName.toLowerCase() != 'li'){
24505                             e.stopEvent();
24506                             r.pasteHTML('<br />');
24507                             r.collapse(false);
24508                             r.select();
24509                         }
24510                     }
24511                 }
24512                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24513                     this.cleanUpPaste.defer(100, this);
24514                     return;
24515                 }
24516                 
24517                 
24518             };
24519         }else if(Roo.isOpera){
24520             return function(e){
24521                 var k = e.getKey();
24522                 if(k == e.TAB){
24523                     e.stopEvent();
24524                     this.win.focus();
24525                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24526                     this.deferFocus();
24527                 }
24528                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24529                     this.cleanUpPaste.defer(100, this);
24530                     return;
24531                 }
24532                 
24533             };
24534         }else if(Roo.isSafari){
24535             return function(e){
24536                 var k = e.getKey();
24537                 
24538                 if(k == e.TAB){
24539                     e.stopEvent();
24540                     this.execCmd('InsertText','\t');
24541                     this.deferFocus();
24542                     return;
24543                 }
24544                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24545                     this.cleanUpPaste.defer(100, this);
24546                     return;
24547                 }
24548                 
24549              };
24550         }
24551     }(),
24552     
24553     getAllAncestors: function()
24554     {
24555         var p = this.getSelectedNode();
24556         var a = [];
24557         if (!p) {
24558             a.push(p); // push blank onto stack..
24559             p = this.getParentElement();
24560         }
24561         
24562         
24563         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24564             a.push(p);
24565             p = p.parentNode;
24566         }
24567         a.push(this.doc.body);
24568         return a;
24569     },
24570     lastSel : false,
24571     lastSelNode : false,
24572     
24573     
24574     getSelection : function() 
24575     {
24576         this.assignDocWin();
24577         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24578     },
24579     
24580     getSelectedNode: function() 
24581     {
24582         // this may only work on Gecko!!!
24583         
24584         // should we cache this!!!!
24585         
24586         
24587         
24588          
24589         var range = this.createRange(this.getSelection()).cloneRange();
24590         
24591         if (Roo.isIE) {
24592             var parent = range.parentElement();
24593             while (true) {
24594                 var testRange = range.duplicate();
24595                 testRange.moveToElementText(parent);
24596                 if (testRange.inRange(range)) {
24597                     break;
24598                 }
24599                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24600                     break;
24601                 }
24602                 parent = parent.parentElement;
24603             }
24604             return parent;
24605         }
24606         
24607         // is ancestor a text element.
24608         var ac =  range.commonAncestorContainer;
24609         if (ac.nodeType == 3) {
24610             ac = ac.parentNode;
24611         }
24612         
24613         var ar = ac.childNodes;
24614          
24615         var nodes = [];
24616         var other_nodes = [];
24617         var has_other_nodes = false;
24618         for (var i=0;i<ar.length;i++) {
24619             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24620                 continue;
24621             }
24622             // fullly contained node.
24623             
24624             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24625                 nodes.push(ar[i]);
24626                 continue;
24627             }
24628             
24629             // probably selected..
24630             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24631                 other_nodes.push(ar[i]);
24632                 continue;
24633             }
24634             // outer..
24635             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24636                 continue;
24637             }
24638             
24639             
24640             has_other_nodes = true;
24641         }
24642         if (!nodes.length && other_nodes.length) {
24643             nodes= other_nodes;
24644         }
24645         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24646             return false;
24647         }
24648         
24649         return nodes[0];
24650     },
24651     createRange: function(sel)
24652     {
24653         // this has strange effects when using with 
24654         // top toolbar - not sure if it's a great idea.
24655         //this.editor.contentWindow.focus();
24656         if (typeof sel != "undefined") {
24657             try {
24658                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24659             } catch(e) {
24660                 return this.doc.createRange();
24661             }
24662         } else {
24663             return this.doc.createRange();
24664         }
24665     },
24666     getParentElement: function()
24667     {
24668         
24669         this.assignDocWin();
24670         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24671         
24672         var range = this.createRange(sel);
24673          
24674         try {
24675             var p = range.commonAncestorContainer;
24676             while (p.nodeType == 3) { // text node
24677                 p = p.parentNode;
24678             }
24679             return p;
24680         } catch (e) {
24681             return null;
24682         }
24683     
24684     },
24685     /***
24686      *
24687      * Range intersection.. the hard stuff...
24688      *  '-1' = before
24689      *  '0' = hits..
24690      *  '1' = after.
24691      *         [ -- selected range --- ]
24692      *   [fail]                        [fail]
24693      *
24694      *    basically..
24695      *      if end is before start or  hits it. fail.
24696      *      if start is after end or hits it fail.
24697      *
24698      *   if either hits (but other is outside. - then it's not 
24699      *   
24700      *    
24701      **/
24702     
24703     
24704     // @see http://www.thismuchiknow.co.uk/?p=64.
24705     rangeIntersectsNode : function(range, node)
24706     {
24707         var nodeRange = node.ownerDocument.createRange();
24708         try {
24709             nodeRange.selectNode(node);
24710         } catch (e) {
24711             nodeRange.selectNodeContents(node);
24712         }
24713     
24714         var rangeStartRange = range.cloneRange();
24715         rangeStartRange.collapse(true);
24716     
24717         var rangeEndRange = range.cloneRange();
24718         rangeEndRange.collapse(false);
24719     
24720         var nodeStartRange = nodeRange.cloneRange();
24721         nodeStartRange.collapse(true);
24722     
24723         var nodeEndRange = nodeRange.cloneRange();
24724         nodeEndRange.collapse(false);
24725     
24726         return rangeStartRange.compareBoundaryPoints(
24727                  Range.START_TO_START, nodeEndRange) == -1 &&
24728                rangeEndRange.compareBoundaryPoints(
24729                  Range.START_TO_START, nodeStartRange) == 1;
24730         
24731          
24732     },
24733     rangeCompareNode : function(range, node)
24734     {
24735         var nodeRange = node.ownerDocument.createRange();
24736         try {
24737             nodeRange.selectNode(node);
24738         } catch (e) {
24739             nodeRange.selectNodeContents(node);
24740         }
24741         
24742         
24743         range.collapse(true);
24744     
24745         nodeRange.collapse(true);
24746      
24747         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24748         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24749          
24750         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24751         
24752         var nodeIsBefore   =  ss == 1;
24753         var nodeIsAfter    = ee == -1;
24754         
24755         if (nodeIsBefore && nodeIsAfter) {
24756             return 0; // outer
24757         }
24758         if (!nodeIsBefore && nodeIsAfter) {
24759             return 1; //right trailed.
24760         }
24761         
24762         if (nodeIsBefore && !nodeIsAfter) {
24763             return 2;  // left trailed.
24764         }
24765         // fully contined.
24766         return 3;
24767     },
24768
24769     // private? - in a new class?
24770     cleanUpPaste :  function()
24771     {
24772         // cleans up the whole document..
24773         Roo.log('cleanuppaste');
24774         
24775         this.cleanUpChildren(this.doc.body);
24776         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24777         if (clean != this.doc.body.innerHTML) {
24778             this.doc.body.innerHTML = clean;
24779         }
24780         
24781     },
24782     
24783     cleanWordChars : function(input) {// change the chars to hex code
24784         var he = Roo.HtmlEditorCore;
24785         
24786         var output = input;
24787         Roo.each(he.swapCodes, function(sw) { 
24788             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24789             
24790             output = output.replace(swapper, sw[1]);
24791         });
24792         
24793         return output;
24794     },
24795     
24796     
24797     cleanUpChildren : function (n)
24798     {
24799         if (!n.childNodes.length) {
24800             return;
24801         }
24802         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24803            this.cleanUpChild(n.childNodes[i]);
24804         }
24805     },
24806     
24807     
24808         
24809     
24810     cleanUpChild : function (node)
24811     {
24812         var ed = this;
24813         //console.log(node);
24814         if (node.nodeName == "#text") {
24815             // clean up silly Windows -- stuff?
24816             return; 
24817         }
24818         if (node.nodeName == "#comment") {
24819             node.parentNode.removeChild(node);
24820             // clean up silly Windows -- stuff?
24821             return; 
24822         }
24823         var lcname = node.tagName.toLowerCase();
24824         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24825         // whitelist of tags..
24826         
24827         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24828             // remove node.
24829             node.parentNode.removeChild(node);
24830             return;
24831             
24832         }
24833         
24834         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24835         
24836         // spans with no attributes - just remove them..
24837         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24838             remove_keep_children = true;
24839         }
24840         
24841         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24842         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24843         
24844         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24845         //    remove_keep_children = true;
24846         //}
24847         
24848         if (remove_keep_children) {
24849             this.cleanUpChildren(node);
24850             // inserts everything just before this node...
24851             while (node.childNodes.length) {
24852                 var cn = node.childNodes[0];
24853                 node.removeChild(cn);
24854                 node.parentNode.insertBefore(cn, node);
24855             }
24856             node.parentNode.removeChild(node);
24857             return;
24858         }
24859         
24860         if (!node.attributes || !node.attributes.length) {
24861             
24862           
24863             
24864             
24865             this.cleanUpChildren(node);
24866             return;
24867         }
24868         
24869         function cleanAttr(n,v)
24870         {
24871             
24872             if (v.match(/^\./) || v.match(/^\//)) {
24873                 return;
24874             }
24875             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24876                 return;
24877             }
24878             if (v.match(/^#/)) {
24879                 return;
24880             }
24881             if (v.match(/^\{/)) { // allow template editing.
24882                 return;
24883             }
24884 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24885             node.removeAttribute(n);
24886             
24887         }
24888         
24889         var cwhite = this.cwhite;
24890         var cblack = this.cblack;
24891             
24892         function cleanStyle(n,v)
24893         {
24894             if (v.match(/expression/)) { //XSS?? should we even bother..
24895                 node.removeAttribute(n);
24896                 return;
24897             }
24898             
24899             var parts = v.split(/;/);
24900             var clean = [];
24901             
24902             Roo.each(parts, function(p) {
24903                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24904                 if (!p.length) {
24905                     return true;
24906                 }
24907                 var l = p.split(':').shift().replace(/\s+/g,'');
24908                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24909                 
24910                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24911 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24912                     //node.removeAttribute(n);
24913                     return true;
24914                 }
24915                 //Roo.log()
24916                 // only allow 'c whitelisted system attributes'
24917                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24918 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24919                     //node.removeAttribute(n);
24920                     return true;
24921                 }
24922                 
24923                 
24924                  
24925                 
24926                 clean.push(p);
24927                 return true;
24928             });
24929             if (clean.length) { 
24930                 node.setAttribute(n, clean.join(';'));
24931             } else {
24932                 node.removeAttribute(n);
24933             }
24934             
24935         }
24936         
24937         
24938         for (var i = node.attributes.length-1; i > -1 ; i--) {
24939             var a = node.attributes[i];
24940             //console.log(a);
24941             
24942             if (a.name.toLowerCase().substr(0,2)=='on')  {
24943                 node.removeAttribute(a.name);
24944                 continue;
24945             }
24946             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24947                 node.removeAttribute(a.name);
24948                 continue;
24949             }
24950             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24951                 cleanAttr(a.name,a.value); // fixme..
24952                 continue;
24953             }
24954             if (a.name == 'style') {
24955                 cleanStyle(a.name,a.value);
24956                 continue;
24957             }
24958             /// clean up MS crap..
24959             // tecnically this should be a list of valid class'es..
24960             
24961             
24962             if (a.name == 'class') {
24963                 if (a.value.match(/^Mso/)) {
24964                     node.removeAttribute('class');
24965                 }
24966                 
24967                 if (a.value.match(/^body$/)) {
24968                     node.removeAttribute('class');
24969                 }
24970                 continue;
24971             }
24972             
24973             // style cleanup!?
24974             // class cleanup?
24975             
24976         }
24977         
24978         
24979         this.cleanUpChildren(node);
24980         
24981         
24982     },
24983     
24984     /**
24985      * Clean up MS wordisms...
24986      */
24987     cleanWord : function(node)
24988     {
24989         if (!node) {
24990             this.cleanWord(this.doc.body);
24991             return;
24992         }
24993         
24994         if(
24995                 node.nodeName == 'SPAN' &&
24996                 !node.hasAttributes() &&
24997                 node.childNodes.length == 1 &&
24998                 node.firstChild.nodeName == "#text"  
24999         ) {
25000             var textNode = node.firstChild;
25001             node.removeChild(textNode);
25002             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25003                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25004             }
25005             node.parentNode.insertBefore(textNode, node);
25006             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25007                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25008             }
25009             node.parentNode.removeChild(node);
25010         }
25011         
25012         if (node.nodeName == "#text") {
25013             // clean up silly Windows -- stuff?
25014             return; 
25015         }
25016         if (node.nodeName == "#comment") {
25017             node.parentNode.removeChild(node);
25018             // clean up silly Windows -- stuff?
25019             return; 
25020         }
25021         
25022         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25023             node.parentNode.removeChild(node);
25024             return;
25025         }
25026         //Roo.log(node.tagName);
25027         // remove - but keep children..
25028         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25029             //Roo.log('-- removed');
25030             while (node.childNodes.length) {
25031                 var cn = node.childNodes[0];
25032                 node.removeChild(cn);
25033                 node.parentNode.insertBefore(cn, node);
25034                 // move node to parent - and clean it..
25035                 this.cleanWord(cn);
25036             }
25037             node.parentNode.removeChild(node);
25038             /// no need to iterate chidlren = it's got none..
25039             //this.iterateChildren(node, this.cleanWord);
25040             return;
25041         }
25042         // clean styles
25043         if (node.className.length) {
25044             
25045             var cn = node.className.split(/\W+/);
25046             var cna = [];
25047             Roo.each(cn, function(cls) {
25048                 if (cls.match(/Mso[a-zA-Z]+/)) {
25049                     return;
25050                 }
25051                 cna.push(cls);
25052             });
25053             node.className = cna.length ? cna.join(' ') : '';
25054             if (!cna.length) {
25055                 node.removeAttribute("class");
25056             }
25057         }
25058         
25059         if (node.hasAttribute("lang")) {
25060             node.removeAttribute("lang");
25061         }
25062         
25063         if (node.hasAttribute("style")) {
25064             
25065             var styles = node.getAttribute("style").split(";");
25066             var nstyle = [];
25067             Roo.each(styles, function(s) {
25068                 if (!s.match(/:/)) {
25069                     return;
25070                 }
25071                 var kv = s.split(":");
25072                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25073                     return;
25074                 }
25075                 // what ever is left... we allow.
25076                 nstyle.push(s);
25077             });
25078             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25079             if (!nstyle.length) {
25080                 node.removeAttribute('style');
25081             }
25082         }
25083         this.iterateChildren(node, this.cleanWord);
25084         
25085         
25086         
25087     },
25088     /**
25089      * iterateChildren of a Node, calling fn each time, using this as the scole..
25090      * @param {DomNode} node node to iterate children of.
25091      * @param {Function} fn method of this class to call on each item.
25092      */
25093     iterateChildren : function(node, fn)
25094     {
25095         if (!node.childNodes.length) {
25096                 return;
25097         }
25098         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25099            fn.call(this, node.childNodes[i])
25100         }
25101     },
25102     
25103     
25104     /**
25105      * cleanTableWidths.
25106      *
25107      * Quite often pasting from word etc.. results in tables with column and widths.
25108      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25109      *
25110      */
25111     cleanTableWidths : function(node)
25112     {
25113          
25114          
25115         if (!node) {
25116             this.cleanTableWidths(this.doc.body);
25117             return;
25118         }
25119         
25120         // ignore list...
25121         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25122             return; 
25123         }
25124         Roo.log(node.tagName);
25125         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25126             this.iterateChildren(node, this.cleanTableWidths);
25127             return;
25128         }
25129         if (node.hasAttribute('width')) {
25130             node.removeAttribute('width');
25131         }
25132         
25133          
25134         if (node.hasAttribute("style")) {
25135             // pretty basic...
25136             
25137             var styles = node.getAttribute("style").split(";");
25138             var nstyle = [];
25139             Roo.each(styles, function(s) {
25140                 if (!s.match(/:/)) {
25141                     return;
25142                 }
25143                 var kv = s.split(":");
25144                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25145                     return;
25146                 }
25147                 // what ever is left... we allow.
25148                 nstyle.push(s);
25149             });
25150             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25151             if (!nstyle.length) {
25152                 node.removeAttribute('style');
25153             }
25154         }
25155         
25156         this.iterateChildren(node, this.cleanTableWidths);
25157         
25158         
25159     },
25160     
25161     
25162     
25163     
25164     domToHTML : function(currentElement, depth, nopadtext) {
25165         
25166         depth = depth || 0;
25167         nopadtext = nopadtext || false;
25168     
25169         if (!currentElement) {
25170             return this.domToHTML(this.doc.body);
25171         }
25172         
25173         //Roo.log(currentElement);
25174         var j;
25175         var allText = false;
25176         var nodeName = currentElement.nodeName;
25177         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25178         
25179         if  (nodeName == '#text') {
25180             
25181             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25182         }
25183         
25184         
25185         var ret = '';
25186         if (nodeName != 'BODY') {
25187              
25188             var i = 0;
25189             // Prints the node tagName, such as <A>, <IMG>, etc
25190             if (tagName) {
25191                 var attr = [];
25192                 for(i = 0; i < currentElement.attributes.length;i++) {
25193                     // quoting?
25194                     var aname = currentElement.attributes.item(i).name;
25195                     if (!currentElement.attributes.item(i).value.length) {
25196                         continue;
25197                     }
25198                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25199                 }
25200                 
25201                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25202             } 
25203             else {
25204                 
25205                 // eack
25206             }
25207         } else {
25208             tagName = false;
25209         }
25210         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25211             return ret;
25212         }
25213         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25214             nopadtext = true;
25215         }
25216         
25217         
25218         // Traverse the tree
25219         i = 0;
25220         var currentElementChild = currentElement.childNodes.item(i);
25221         var allText = true;
25222         var innerHTML  = '';
25223         lastnode = '';
25224         while (currentElementChild) {
25225             // Formatting code (indent the tree so it looks nice on the screen)
25226             var nopad = nopadtext;
25227             if (lastnode == 'SPAN') {
25228                 nopad  = true;
25229             }
25230             // text
25231             if  (currentElementChild.nodeName == '#text') {
25232                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25233                 toadd = nopadtext ? toadd : toadd.trim();
25234                 if (!nopad && toadd.length > 80) {
25235                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25236                 }
25237                 innerHTML  += toadd;
25238                 
25239                 i++;
25240                 currentElementChild = currentElement.childNodes.item(i);
25241                 lastNode = '';
25242                 continue;
25243             }
25244             allText = false;
25245             
25246             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25247                 
25248             // Recursively traverse the tree structure of the child node
25249             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25250             lastnode = currentElementChild.nodeName;
25251             i++;
25252             currentElementChild=currentElement.childNodes.item(i);
25253         }
25254         
25255         ret += innerHTML;
25256         
25257         if (!allText) {
25258                 // The remaining code is mostly for formatting the tree
25259             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25260         }
25261         
25262         
25263         if (tagName) {
25264             ret+= "</"+tagName+">";
25265         }
25266         return ret;
25267         
25268     },
25269         
25270     applyBlacklists : function()
25271     {
25272         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25273         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25274         
25275         this.white = [];
25276         this.black = [];
25277         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25278             if (b.indexOf(tag) > -1) {
25279                 return;
25280             }
25281             this.white.push(tag);
25282             
25283         }, this);
25284         
25285         Roo.each(w, function(tag) {
25286             if (b.indexOf(tag) > -1) {
25287                 return;
25288             }
25289             if (this.white.indexOf(tag) > -1) {
25290                 return;
25291             }
25292             this.white.push(tag);
25293             
25294         }, this);
25295         
25296         
25297         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25298             if (w.indexOf(tag) > -1) {
25299                 return;
25300             }
25301             this.black.push(tag);
25302             
25303         }, this);
25304         
25305         Roo.each(b, function(tag) {
25306             if (w.indexOf(tag) > -1) {
25307                 return;
25308             }
25309             if (this.black.indexOf(tag) > -1) {
25310                 return;
25311             }
25312             this.black.push(tag);
25313             
25314         }, this);
25315         
25316         
25317         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25318         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25319         
25320         this.cwhite = [];
25321         this.cblack = [];
25322         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25323             if (b.indexOf(tag) > -1) {
25324                 return;
25325             }
25326             this.cwhite.push(tag);
25327             
25328         }, this);
25329         
25330         Roo.each(w, function(tag) {
25331             if (b.indexOf(tag) > -1) {
25332                 return;
25333             }
25334             if (this.cwhite.indexOf(tag) > -1) {
25335                 return;
25336             }
25337             this.cwhite.push(tag);
25338             
25339         }, this);
25340         
25341         
25342         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25343             if (w.indexOf(tag) > -1) {
25344                 return;
25345             }
25346             this.cblack.push(tag);
25347             
25348         }, this);
25349         
25350         Roo.each(b, function(tag) {
25351             if (w.indexOf(tag) > -1) {
25352                 return;
25353             }
25354             if (this.cblack.indexOf(tag) > -1) {
25355                 return;
25356             }
25357             this.cblack.push(tag);
25358             
25359         }, this);
25360     },
25361     
25362     setStylesheets : function(stylesheets)
25363     {
25364         if(typeof(stylesheets) == 'string'){
25365             Roo.get(this.iframe.contentDocument.head).createChild({
25366                 tag : 'link',
25367                 rel : 'stylesheet',
25368                 type : 'text/css',
25369                 href : stylesheets
25370             });
25371             
25372             return;
25373         }
25374         var _this = this;
25375      
25376         Roo.each(stylesheets, function(s) {
25377             if(!s.length){
25378                 return;
25379             }
25380             
25381             Roo.get(_this.iframe.contentDocument.head).createChild({
25382                 tag : 'link',
25383                 rel : 'stylesheet',
25384                 type : 'text/css',
25385                 href : s
25386             });
25387         });
25388
25389         
25390     },
25391     
25392     removeStylesheets : function()
25393     {
25394         var _this = this;
25395         
25396         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25397             s.remove();
25398         });
25399     },
25400     
25401     setStyle : function(style)
25402     {
25403         Roo.get(this.iframe.contentDocument.head).createChild({
25404             tag : 'style',
25405             type : 'text/css',
25406             html : style
25407         });
25408
25409         return;
25410     }
25411     
25412     // hide stuff that is not compatible
25413     /**
25414      * @event blur
25415      * @hide
25416      */
25417     /**
25418      * @event change
25419      * @hide
25420      */
25421     /**
25422      * @event focus
25423      * @hide
25424      */
25425     /**
25426      * @event specialkey
25427      * @hide
25428      */
25429     /**
25430      * @cfg {String} fieldClass @hide
25431      */
25432     /**
25433      * @cfg {String} focusClass @hide
25434      */
25435     /**
25436      * @cfg {String} autoCreate @hide
25437      */
25438     /**
25439      * @cfg {String} inputType @hide
25440      */
25441     /**
25442      * @cfg {String} invalidClass @hide
25443      */
25444     /**
25445      * @cfg {String} invalidText @hide
25446      */
25447     /**
25448      * @cfg {String} msgFx @hide
25449      */
25450     /**
25451      * @cfg {String} validateOnBlur @hide
25452      */
25453 });
25454
25455 Roo.HtmlEditorCore.white = [
25456         'area', 'br', 'img', 'input', 'hr', 'wbr',
25457         
25458        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25459        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25460        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25461        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25462        'table',   'ul',         'xmp', 
25463        
25464        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25465       'thead',   'tr', 
25466      
25467       'dir', 'menu', 'ol', 'ul', 'dl',
25468        
25469       'embed',  'object'
25470 ];
25471
25472
25473 Roo.HtmlEditorCore.black = [
25474     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25475         'applet', // 
25476         'base',   'basefont', 'bgsound', 'blink',  'body', 
25477         'frame',  'frameset', 'head',    'html',   'ilayer', 
25478         'iframe', 'layer',  'link',     'meta',    'object',   
25479         'script', 'style' ,'title',  'xml' // clean later..
25480 ];
25481 Roo.HtmlEditorCore.clean = [
25482     'script', 'style', 'title', 'xml'
25483 ];
25484 Roo.HtmlEditorCore.remove = [
25485     'font'
25486 ];
25487 // attributes..
25488
25489 Roo.HtmlEditorCore.ablack = [
25490     'on'
25491 ];
25492     
25493 Roo.HtmlEditorCore.aclean = [ 
25494     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25495 ];
25496
25497 // protocols..
25498 Roo.HtmlEditorCore.pwhite= [
25499         'http',  'https',  'mailto'
25500 ];
25501
25502 // white listed style attributes.
25503 Roo.HtmlEditorCore.cwhite= [
25504       //  'text-align', /// default is to allow most things..
25505       
25506          
25507 //        'font-size'//??
25508 ];
25509
25510 // black listed style attributes.
25511 Roo.HtmlEditorCore.cblack= [
25512       //  'font-size' -- this can be set by the project 
25513 ];
25514
25515
25516 Roo.HtmlEditorCore.swapCodes   =[ 
25517     [    8211, "--" ], 
25518     [    8212, "--" ], 
25519     [    8216,  "'" ],  
25520     [    8217, "'" ],  
25521     [    8220, '"' ],  
25522     [    8221, '"' ],  
25523     [    8226, "*" ],  
25524     [    8230, "..." ]
25525 ]; 
25526
25527     /*
25528  * - LGPL
25529  *
25530  * HtmlEditor
25531  * 
25532  */
25533
25534 /**
25535  * @class Roo.bootstrap.HtmlEditor
25536  * @extends Roo.bootstrap.TextArea
25537  * Bootstrap HtmlEditor class
25538
25539  * @constructor
25540  * Create a new HtmlEditor
25541  * @param {Object} config The config object
25542  */
25543
25544 Roo.bootstrap.HtmlEditor = function(config){
25545     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25546     if (!this.toolbars) {
25547         this.toolbars = [];
25548     }
25549     
25550     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25551     this.addEvents({
25552             /**
25553              * @event initialize
25554              * Fires when the editor is fully initialized (including the iframe)
25555              * @param {HtmlEditor} this
25556              */
25557             initialize: true,
25558             /**
25559              * @event activate
25560              * Fires when the editor is first receives the focus. Any insertion must wait
25561              * until after this event.
25562              * @param {HtmlEditor} this
25563              */
25564             activate: true,
25565              /**
25566              * @event beforesync
25567              * Fires before the textarea is updated with content from the editor iframe. Return false
25568              * to cancel the sync.
25569              * @param {HtmlEditor} this
25570              * @param {String} html
25571              */
25572             beforesync: true,
25573              /**
25574              * @event beforepush
25575              * Fires before the iframe editor is updated with content from the textarea. Return false
25576              * to cancel the push.
25577              * @param {HtmlEditor} this
25578              * @param {String} html
25579              */
25580             beforepush: true,
25581              /**
25582              * @event sync
25583              * Fires when the textarea is updated with content from the editor iframe.
25584              * @param {HtmlEditor} this
25585              * @param {String} html
25586              */
25587             sync: true,
25588              /**
25589              * @event push
25590              * Fires when the iframe editor is updated with content from the textarea.
25591              * @param {HtmlEditor} this
25592              * @param {String} html
25593              */
25594             push: true,
25595              /**
25596              * @event editmodechange
25597              * Fires when the editor switches edit modes
25598              * @param {HtmlEditor} this
25599              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25600              */
25601             editmodechange: true,
25602             /**
25603              * @event editorevent
25604              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25605              * @param {HtmlEditor} this
25606              */
25607             editorevent: true,
25608             /**
25609              * @event firstfocus
25610              * Fires when on first focus - needed by toolbars..
25611              * @param {HtmlEditor} this
25612              */
25613             firstfocus: true,
25614             /**
25615              * @event autosave
25616              * Auto save the htmlEditor value as a file into Events
25617              * @param {HtmlEditor} this
25618              */
25619             autosave: true,
25620             /**
25621              * @event savedpreview
25622              * preview the saved version of htmlEditor
25623              * @param {HtmlEditor} this
25624              */
25625             savedpreview: true
25626         });
25627 };
25628
25629
25630 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25631     
25632     
25633       /**
25634      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25635      */
25636     toolbars : false,
25637     
25638      /**
25639     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25640     */
25641     btns : [],
25642    
25643      /**
25644      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25645      *                        Roo.resizable.
25646      */
25647     resizable : false,
25648      /**
25649      * @cfg {Number} height (in pixels)
25650      */   
25651     height: 300,
25652    /**
25653      * @cfg {Number} width (in pixels)
25654      */   
25655     width: false,
25656     
25657     /**
25658      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25659      * 
25660      */
25661     stylesheets: false,
25662     
25663     // id of frame..
25664     frameId: false,
25665     
25666     // private properties
25667     validationEvent : false,
25668     deferHeight: true,
25669     initialized : false,
25670     activated : false,
25671     
25672     onFocus : Roo.emptyFn,
25673     iframePad:3,
25674     hideMode:'offsets',
25675     
25676     tbContainer : false,
25677     
25678     bodyCls : '',
25679     
25680     toolbarContainer :function() {
25681         return this.wrap.select('.x-html-editor-tb',true).first();
25682     },
25683
25684     /**
25685      * Protected method that will not generally be called directly. It
25686      * is called when the editor creates its toolbar. Override this method if you need to
25687      * add custom toolbar buttons.
25688      * @param {HtmlEditor} editor
25689      */
25690     createToolbar : function(){
25691         Roo.log('renewing');
25692         Roo.log("create toolbars");
25693         
25694         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25695         this.toolbars[0].render(this.toolbarContainer());
25696         
25697         return;
25698         
25699 //        if (!editor.toolbars || !editor.toolbars.length) {
25700 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25701 //        }
25702 //        
25703 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25704 //            editor.toolbars[i] = Roo.factory(
25705 //                    typeof(editor.toolbars[i]) == 'string' ?
25706 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25707 //                Roo.bootstrap.HtmlEditor);
25708 //            editor.toolbars[i].init(editor);
25709 //        }
25710     },
25711
25712      
25713     // private
25714     onRender : function(ct, position)
25715     {
25716        // Roo.log("Call onRender: " + this.xtype);
25717         var _t = this;
25718         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25719       
25720         this.wrap = this.inputEl().wrap({
25721             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25722         });
25723         
25724         this.editorcore.onRender(ct, position);
25725          
25726         if (this.resizable) {
25727             this.resizeEl = new Roo.Resizable(this.wrap, {
25728                 pinned : true,
25729                 wrap: true,
25730                 dynamic : true,
25731                 minHeight : this.height,
25732                 height: this.height,
25733                 handles : this.resizable,
25734                 width: this.width,
25735                 listeners : {
25736                     resize : function(r, w, h) {
25737                         _t.onResize(w,h); // -something
25738                     }
25739                 }
25740             });
25741             
25742         }
25743         this.createToolbar(this);
25744        
25745         
25746         if(!this.width && this.resizable){
25747             this.setSize(this.wrap.getSize());
25748         }
25749         if (this.resizeEl) {
25750             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25751             // should trigger onReize..
25752         }
25753         
25754     },
25755
25756     // private
25757     onResize : function(w, h)
25758     {
25759         Roo.log('resize: ' +w + ',' + h );
25760         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25761         var ew = false;
25762         var eh = false;
25763         
25764         if(this.inputEl() ){
25765             if(typeof w == 'number'){
25766                 var aw = w - this.wrap.getFrameWidth('lr');
25767                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25768                 ew = aw;
25769             }
25770             if(typeof h == 'number'){
25771                  var tbh = -11;  // fixme it needs to tool bar size!
25772                 for (var i =0; i < this.toolbars.length;i++) {
25773                     // fixme - ask toolbars for heights?
25774                     tbh += this.toolbars[i].el.getHeight();
25775                     //if (this.toolbars[i].footer) {
25776                     //    tbh += this.toolbars[i].footer.el.getHeight();
25777                     //}
25778                 }
25779               
25780                 
25781                 
25782                 
25783                 
25784                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25785                 ah -= 5; // knock a few pixes off for look..
25786                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25787                 var eh = ah;
25788             }
25789         }
25790         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25791         this.editorcore.onResize(ew,eh);
25792         
25793     },
25794
25795     /**
25796      * Toggles the editor between standard and source edit mode.
25797      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25798      */
25799     toggleSourceEdit : function(sourceEditMode)
25800     {
25801         this.editorcore.toggleSourceEdit(sourceEditMode);
25802         
25803         if(this.editorcore.sourceEditMode){
25804             Roo.log('editor - showing textarea');
25805             
25806 //            Roo.log('in');
25807 //            Roo.log(this.syncValue());
25808             this.syncValue();
25809             this.inputEl().removeClass(['hide', 'x-hidden']);
25810             this.inputEl().dom.removeAttribute('tabIndex');
25811             this.inputEl().focus();
25812         }else{
25813             Roo.log('editor - hiding textarea');
25814 //            Roo.log('out')
25815 //            Roo.log(this.pushValue()); 
25816             this.pushValue();
25817             
25818             this.inputEl().addClass(['hide', 'x-hidden']);
25819             this.inputEl().dom.setAttribute('tabIndex', -1);
25820             //this.deferFocus();
25821         }
25822          
25823         if(this.resizable){
25824             this.setSize(this.wrap.getSize());
25825         }
25826         
25827         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25828     },
25829  
25830     // private (for BoxComponent)
25831     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25832
25833     // private (for BoxComponent)
25834     getResizeEl : function(){
25835         return this.wrap;
25836     },
25837
25838     // private (for BoxComponent)
25839     getPositionEl : function(){
25840         return this.wrap;
25841     },
25842
25843     // private
25844     initEvents : function(){
25845         this.originalValue = this.getValue();
25846     },
25847
25848 //    /**
25849 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25850 //     * @method
25851 //     */
25852 //    markInvalid : Roo.emptyFn,
25853 //    /**
25854 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25855 //     * @method
25856 //     */
25857 //    clearInvalid : Roo.emptyFn,
25858
25859     setValue : function(v){
25860         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25861         this.editorcore.pushValue();
25862     },
25863
25864      
25865     // private
25866     deferFocus : function(){
25867         this.focus.defer(10, this);
25868     },
25869
25870     // doc'ed in Field
25871     focus : function(){
25872         this.editorcore.focus();
25873         
25874     },
25875       
25876
25877     // private
25878     onDestroy : function(){
25879         
25880         
25881         
25882         if(this.rendered){
25883             
25884             for (var i =0; i < this.toolbars.length;i++) {
25885                 // fixme - ask toolbars for heights?
25886                 this.toolbars[i].onDestroy();
25887             }
25888             
25889             this.wrap.dom.innerHTML = '';
25890             this.wrap.remove();
25891         }
25892     },
25893
25894     // private
25895     onFirstFocus : function(){
25896         //Roo.log("onFirstFocus");
25897         this.editorcore.onFirstFocus();
25898          for (var i =0; i < this.toolbars.length;i++) {
25899             this.toolbars[i].onFirstFocus();
25900         }
25901         
25902     },
25903     
25904     // private
25905     syncValue : function()
25906     {   
25907         this.editorcore.syncValue();
25908     },
25909     
25910     pushValue : function()
25911     {   
25912         this.editorcore.pushValue();
25913     }
25914      
25915     
25916     // hide stuff that is not compatible
25917     /**
25918      * @event blur
25919      * @hide
25920      */
25921     /**
25922      * @event change
25923      * @hide
25924      */
25925     /**
25926      * @event focus
25927      * @hide
25928      */
25929     /**
25930      * @event specialkey
25931      * @hide
25932      */
25933     /**
25934      * @cfg {String} fieldClass @hide
25935      */
25936     /**
25937      * @cfg {String} focusClass @hide
25938      */
25939     /**
25940      * @cfg {String} autoCreate @hide
25941      */
25942     /**
25943      * @cfg {String} inputType @hide
25944      */
25945      
25946     /**
25947      * @cfg {String} invalidText @hide
25948      */
25949     /**
25950      * @cfg {String} msgFx @hide
25951      */
25952     /**
25953      * @cfg {String} validateOnBlur @hide
25954      */
25955 });
25956  
25957     
25958    
25959    
25960    
25961       
25962 Roo.namespace('Roo.bootstrap.htmleditor');
25963 /**
25964  * @class Roo.bootstrap.HtmlEditorToolbar1
25965  * Basic Toolbar
25966  * 
25967  * @example
25968  * Usage:
25969  *
25970  new Roo.bootstrap.HtmlEditor({
25971     ....
25972     toolbars : [
25973         new Roo.bootstrap.HtmlEditorToolbar1({
25974             disable : { fonts: 1 , format: 1, ..., ... , ...],
25975             btns : [ .... ]
25976         })
25977     }
25978      
25979  * 
25980  * @cfg {Object} disable List of elements to disable..
25981  * @cfg {Array} btns List of additional buttons.
25982  * 
25983  * 
25984  * NEEDS Extra CSS? 
25985  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25986  */
25987  
25988 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25989 {
25990     
25991     Roo.apply(this, config);
25992     
25993     // default disabled, based on 'good practice'..
25994     this.disable = this.disable || {};
25995     Roo.applyIf(this.disable, {
25996         fontSize : true,
25997         colors : true,
25998         specialElements : true
25999     });
26000     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26001     
26002     this.editor = config.editor;
26003     this.editorcore = config.editor.editorcore;
26004     
26005     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26006     
26007     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26008     // dont call parent... till later.
26009 }
26010 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26011      
26012     bar : true,
26013     
26014     editor : false,
26015     editorcore : false,
26016     
26017     
26018     formats : [
26019         "p" ,  
26020         "h1","h2","h3","h4","h5","h6", 
26021         "pre", "code", 
26022         "abbr", "acronym", "address", "cite", "samp", "var",
26023         'div','span'
26024     ],
26025     
26026     onRender : function(ct, position)
26027     {
26028        // Roo.log("Call onRender: " + this.xtype);
26029         
26030        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26031        Roo.log(this.el);
26032        this.el.dom.style.marginBottom = '0';
26033        var _this = this;
26034        var editorcore = this.editorcore;
26035        var editor= this.editor;
26036        
26037        var children = [];
26038        var btn = function(id,cmd , toggle, handler, html){
26039        
26040             var  event = toggle ? 'toggle' : 'click';
26041        
26042             var a = {
26043                 size : 'sm',
26044                 xtype: 'Button',
26045                 xns: Roo.bootstrap,
26046                 //glyphicon : id,
26047                 fa: id,
26048                 cmd : id || cmd,
26049                 enableToggle:toggle !== false,
26050                 html : html || '',
26051                 pressed : toggle ? false : null,
26052                 listeners : {}
26053             };
26054             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26055                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26056             };
26057             children.push(a);
26058             return a;
26059        }
26060        
26061     //    var cb_box = function...
26062         
26063         var style = {
26064                 xtype: 'Button',
26065                 size : 'sm',
26066                 xns: Roo.bootstrap,
26067                 fa : 'font',
26068                 //html : 'submit'
26069                 menu : {
26070                     xtype: 'Menu',
26071                     xns: Roo.bootstrap,
26072                     items:  []
26073                 }
26074         };
26075         Roo.each(this.formats, function(f) {
26076             style.menu.items.push({
26077                 xtype :'MenuItem',
26078                 xns: Roo.bootstrap,
26079                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26080                 tagname : f,
26081                 listeners : {
26082                     click : function()
26083                     {
26084                         editorcore.insertTag(this.tagname);
26085                         editor.focus();
26086                     }
26087                 }
26088                 
26089             });
26090         });
26091         children.push(style);   
26092         
26093         btn('bold',false,true);
26094         btn('italic',false,true);
26095         btn('align-left', 'justifyleft',true);
26096         btn('align-center', 'justifycenter',true);
26097         btn('align-right' , 'justifyright',true);
26098         btn('link', false, false, function(btn) {
26099             //Roo.log("create link?");
26100             var url = prompt(this.createLinkText, this.defaultLinkValue);
26101             if(url && url != 'http:/'+'/'){
26102                 this.editorcore.relayCmd('createlink', url);
26103             }
26104         }),
26105         btn('list','insertunorderedlist',true);
26106         btn('pencil', false,true, function(btn){
26107                 Roo.log(this);
26108                 this.toggleSourceEdit(btn.pressed);
26109         });
26110         
26111         if (this.editor.btns.length > 0) {
26112             for (var i = 0; i<this.editor.btns.length; i++) {
26113                 children.push(this.editor.btns[i]);
26114             }
26115         }
26116         
26117         /*
26118         var cog = {
26119                 xtype: 'Button',
26120                 size : 'sm',
26121                 xns: Roo.bootstrap,
26122                 glyphicon : 'cog',
26123                 //html : 'submit'
26124                 menu : {
26125                     xtype: 'Menu',
26126                     xns: Roo.bootstrap,
26127                     items:  []
26128                 }
26129         };
26130         
26131         cog.menu.items.push({
26132             xtype :'MenuItem',
26133             xns: Roo.bootstrap,
26134             html : Clean styles,
26135             tagname : f,
26136             listeners : {
26137                 click : function()
26138                 {
26139                     editorcore.insertTag(this.tagname);
26140                     editor.focus();
26141                 }
26142             }
26143             
26144         });
26145        */
26146         
26147          
26148        this.xtype = 'NavSimplebar';
26149         
26150         for(var i=0;i< children.length;i++) {
26151             
26152             this.buttons.add(this.addxtypeChild(children[i]));
26153             
26154         }
26155         
26156         editor.on('editorevent', this.updateToolbar, this);
26157     },
26158     onBtnClick : function(id)
26159     {
26160        this.editorcore.relayCmd(id);
26161        this.editorcore.focus();
26162     },
26163     
26164     /**
26165      * Protected method that will not generally be called directly. It triggers
26166      * a toolbar update by reading the markup state of the current selection in the editor.
26167      */
26168     updateToolbar: function(){
26169
26170         if(!this.editorcore.activated){
26171             this.editor.onFirstFocus(); // is this neeed?
26172             return;
26173         }
26174
26175         var btns = this.buttons; 
26176         var doc = this.editorcore.doc;
26177         btns.get('bold').setActive(doc.queryCommandState('bold'));
26178         btns.get('italic').setActive(doc.queryCommandState('italic'));
26179         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26180         
26181         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26182         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26183         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26184         
26185         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26186         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26187          /*
26188         
26189         var ans = this.editorcore.getAllAncestors();
26190         if (this.formatCombo) {
26191             
26192             
26193             var store = this.formatCombo.store;
26194             this.formatCombo.setValue("");
26195             for (var i =0; i < ans.length;i++) {
26196                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26197                     // select it..
26198                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26199                     break;
26200                 }
26201             }
26202         }
26203         
26204         
26205         
26206         // hides menus... - so this cant be on a menu...
26207         Roo.bootstrap.MenuMgr.hideAll();
26208         */
26209         Roo.bootstrap.MenuMgr.hideAll();
26210         //this.editorsyncValue();
26211     },
26212     onFirstFocus: function() {
26213         this.buttons.each(function(item){
26214            item.enable();
26215         });
26216     },
26217     toggleSourceEdit : function(sourceEditMode){
26218         
26219           
26220         if(sourceEditMode){
26221             Roo.log("disabling buttons");
26222            this.buttons.each( function(item){
26223                 if(item.cmd != 'pencil'){
26224                     item.disable();
26225                 }
26226             });
26227           
26228         }else{
26229             Roo.log("enabling buttons");
26230             if(this.editorcore.initialized){
26231                 this.buttons.each( function(item){
26232                     item.enable();
26233                 });
26234             }
26235             
26236         }
26237         Roo.log("calling toggole on editor");
26238         // tell the editor that it's been pressed..
26239         this.editor.toggleSourceEdit(sourceEditMode);
26240        
26241     }
26242 });
26243
26244
26245
26246
26247  
26248 /*
26249  * - LGPL
26250  */
26251
26252 /**
26253  * @class Roo.bootstrap.Markdown
26254  * @extends Roo.bootstrap.TextArea
26255  * Bootstrap Showdown editable area
26256  * @cfg {string} content
26257  * 
26258  * @constructor
26259  * Create a new Showdown
26260  */
26261
26262 Roo.bootstrap.Markdown = function(config){
26263     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26264    
26265 };
26266
26267 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26268     
26269     editing :false,
26270     
26271     initEvents : function()
26272     {
26273         
26274         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26275         this.markdownEl = this.el.createChild({
26276             cls : 'roo-markdown-area'
26277         });
26278         this.inputEl().addClass('d-none');
26279         if (this.getValue() == '') {
26280             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26281             
26282         } else {
26283             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26284         }
26285         this.markdownEl.on('click', this.toggleTextEdit, this);
26286         this.on('blur', this.toggleTextEdit, this);
26287         this.on('specialkey', this.resizeTextArea, this);
26288     },
26289     
26290     toggleTextEdit : function()
26291     {
26292         var sh = this.markdownEl.getHeight();
26293         this.inputEl().addClass('d-none');
26294         this.markdownEl.addClass('d-none');
26295         if (!this.editing) {
26296             // show editor?
26297             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26298             this.inputEl().removeClass('d-none');
26299             this.inputEl().focus();
26300             this.editing = true;
26301             return;
26302         }
26303         // show showdown...
26304         this.updateMarkdown();
26305         this.markdownEl.removeClass('d-none');
26306         this.editing = false;
26307         return;
26308     },
26309     updateMarkdown : function()
26310     {
26311         if (this.getValue() == '') {
26312             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26313             return;
26314         }
26315  
26316         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26317     },
26318     
26319     resizeTextArea: function () {
26320         
26321         var sh = 100;
26322         Roo.log([sh, this.getValue().split("\n").length * 30]);
26323         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26324     },
26325     setValue : function(val)
26326     {
26327         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26328         if (!this.editing) {
26329             this.updateMarkdown();
26330         }
26331         
26332     },
26333     focus : function()
26334     {
26335         if (!this.editing) {
26336             this.toggleTextEdit();
26337         }
26338         
26339     }
26340
26341
26342 });
26343 /**
26344  * @class Roo.bootstrap.Table.AbstractSelectionModel
26345  * @extends Roo.util.Observable
26346  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26347  * implemented by descendant classes.  This class should not be directly instantiated.
26348  * @constructor
26349  */
26350 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26351     this.locked = false;
26352     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26353 };
26354
26355
26356 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26357     /** @ignore Called by the grid automatically. Do not call directly. */
26358     init : function(grid){
26359         this.grid = grid;
26360         this.initEvents();
26361     },
26362
26363     /**
26364      * Locks the selections.
26365      */
26366     lock : function(){
26367         this.locked = true;
26368     },
26369
26370     /**
26371      * Unlocks the selections.
26372      */
26373     unlock : function(){
26374         this.locked = false;
26375     },
26376
26377     /**
26378      * Returns true if the selections are locked.
26379      * @return {Boolean}
26380      */
26381     isLocked : function(){
26382         return this.locked;
26383     },
26384     
26385     
26386     initEvents : function ()
26387     {
26388         
26389     }
26390 });
26391 /**
26392  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26393  * @class Roo.bootstrap.Table.RowSelectionModel
26394  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26395  * It supports multiple selections and keyboard selection/navigation. 
26396  * @constructor
26397  * @param {Object} config
26398  */
26399
26400 Roo.bootstrap.Table.RowSelectionModel = function(config){
26401     Roo.apply(this, config);
26402     this.selections = new Roo.util.MixedCollection(false, function(o){
26403         return o.id;
26404     });
26405
26406     this.last = false;
26407     this.lastActive = false;
26408
26409     this.addEvents({
26410         /**
26411              * @event selectionchange
26412              * Fires when the selection changes
26413              * @param {SelectionModel} this
26414              */
26415             "selectionchange" : true,
26416         /**
26417              * @event afterselectionchange
26418              * Fires after the selection changes (eg. by key press or clicking)
26419              * @param {SelectionModel} this
26420              */
26421             "afterselectionchange" : true,
26422         /**
26423              * @event beforerowselect
26424              * Fires when a row is selected being selected, return false to cancel.
26425              * @param {SelectionModel} this
26426              * @param {Number} rowIndex The selected index
26427              * @param {Boolean} keepExisting False if other selections will be cleared
26428              */
26429             "beforerowselect" : true,
26430         /**
26431              * @event rowselect
26432              * Fires when a row is selected.
26433              * @param {SelectionModel} this
26434              * @param {Number} rowIndex The selected index
26435              * @param {Roo.data.Record} r The record
26436              */
26437             "rowselect" : true,
26438         /**
26439              * @event rowdeselect
26440              * Fires when a row is deselected.
26441              * @param {SelectionModel} this
26442              * @param {Number} rowIndex The selected index
26443              */
26444         "rowdeselect" : true
26445     });
26446     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26447     this.locked = false;
26448  };
26449
26450 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26451     /**
26452      * @cfg {Boolean} singleSelect
26453      * True to allow selection of only one row at a time (defaults to false)
26454      */
26455     singleSelect : false,
26456
26457     // private
26458     initEvents : function()
26459     {
26460
26461         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26462         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26463         //}else{ // allow click to work like normal
26464          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26465         //}
26466         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26467         this.grid.on("rowclick", this.handleMouseDown, this);
26468         
26469         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26470             "up" : function(e){
26471                 if(!e.shiftKey){
26472                     this.selectPrevious(e.shiftKey);
26473                 }else if(this.last !== false && this.lastActive !== false){
26474                     var last = this.last;
26475                     this.selectRange(this.last,  this.lastActive-1);
26476                     this.grid.getView().focusRow(this.lastActive);
26477                     if(last !== false){
26478                         this.last = last;
26479                     }
26480                 }else{
26481                     this.selectFirstRow();
26482                 }
26483                 this.fireEvent("afterselectionchange", this);
26484             },
26485             "down" : function(e){
26486                 if(!e.shiftKey){
26487                     this.selectNext(e.shiftKey);
26488                 }else if(this.last !== false && this.lastActive !== false){
26489                     var last = this.last;
26490                     this.selectRange(this.last,  this.lastActive+1);
26491                     this.grid.getView().focusRow(this.lastActive);
26492                     if(last !== false){
26493                         this.last = last;
26494                     }
26495                 }else{
26496                     this.selectFirstRow();
26497                 }
26498                 this.fireEvent("afterselectionchange", this);
26499             },
26500             scope: this
26501         });
26502         this.grid.store.on('load', function(){
26503             this.selections.clear();
26504         },this);
26505         /*
26506         var view = this.grid.view;
26507         view.on("refresh", this.onRefresh, this);
26508         view.on("rowupdated", this.onRowUpdated, this);
26509         view.on("rowremoved", this.onRemove, this);
26510         */
26511     },
26512
26513     // private
26514     onRefresh : function()
26515     {
26516         var ds = this.grid.store, i, v = this.grid.view;
26517         var s = this.selections;
26518         s.each(function(r){
26519             if((i = ds.indexOfId(r.id)) != -1){
26520                 v.onRowSelect(i);
26521             }else{
26522                 s.remove(r);
26523             }
26524         });
26525     },
26526
26527     // private
26528     onRemove : function(v, index, r){
26529         this.selections.remove(r);
26530     },
26531
26532     // private
26533     onRowUpdated : function(v, index, r){
26534         if(this.isSelected(r)){
26535             v.onRowSelect(index);
26536         }
26537     },
26538
26539     /**
26540      * Select records.
26541      * @param {Array} records The records to select
26542      * @param {Boolean} keepExisting (optional) True to keep existing selections
26543      */
26544     selectRecords : function(records, keepExisting)
26545     {
26546         if(!keepExisting){
26547             this.clearSelections();
26548         }
26549             var ds = this.grid.store;
26550         for(var i = 0, len = records.length; i < len; i++){
26551             this.selectRow(ds.indexOf(records[i]), true);
26552         }
26553     },
26554
26555     /**
26556      * Gets the number of selected rows.
26557      * @return {Number}
26558      */
26559     getCount : function(){
26560         return this.selections.length;
26561     },
26562
26563     /**
26564      * Selects the first row in the grid.
26565      */
26566     selectFirstRow : function(){
26567         this.selectRow(0);
26568     },
26569
26570     /**
26571      * Select the last row.
26572      * @param {Boolean} keepExisting (optional) True to keep existing selections
26573      */
26574     selectLastRow : function(keepExisting){
26575         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26576         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26577     },
26578
26579     /**
26580      * Selects the row immediately following the last selected row.
26581      * @param {Boolean} keepExisting (optional) True to keep existing selections
26582      */
26583     selectNext : function(keepExisting)
26584     {
26585             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26586             this.selectRow(this.last+1, keepExisting);
26587             this.grid.getView().focusRow(this.last);
26588         }
26589     },
26590
26591     /**
26592      * Selects the row that precedes the last selected row.
26593      * @param {Boolean} keepExisting (optional) True to keep existing selections
26594      */
26595     selectPrevious : function(keepExisting){
26596         if(this.last){
26597             this.selectRow(this.last-1, keepExisting);
26598             this.grid.getView().focusRow(this.last);
26599         }
26600     },
26601
26602     /**
26603      * Returns the selected records
26604      * @return {Array} Array of selected records
26605      */
26606     getSelections : function(){
26607         return [].concat(this.selections.items);
26608     },
26609
26610     /**
26611      * Returns the first selected record.
26612      * @return {Record}
26613      */
26614     getSelected : function(){
26615         return this.selections.itemAt(0);
26616     },
26617
26618
26619     /**
26620      * Clears all selections.
26621      */
26622     clearSelections : function(fast)
26623     {
26624         if(this.locked) {
26625             return;
26626         }
26627         if(fast !== true){
26628                 var ds = this.grid.store;
26629             var s = this.selections;
26630             s.each(function(r){
26631                 this.deselectRow(ds.indexOfId(r.id));
26632             }, this);
26633             s.clear();
26634         }else{
26635             this.selections.clear();
26636         }
26637         this.last = false;
26638     },
26639
26640
26641     /**
26642      * Selects all rows.
26643      */
26644     selectAll : function(){
26645         if(this.locked) {
26646             return;
26647         }
26648         this.selections.clear();
26649         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26650             this.selectRow(i, true);
26651         }
26652     },
26653
26654     /**
26655      * Returns True if there is a selection.
26656      * @return {Boolean}
26657      */
26658     hasSelection : function(){
26659         return this.selections.length > 0;
26660     },
26661
26662     /**
26663      * Returns True if the specified row is selected.
26664      * @param {Number/Record} record The record or index of the record to check
26665      * @return {Boolean}
26666      */
26667     isSelected : function(index){
26668             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26669         return (r && this.selections.key(r.id) ? true : false);
26670     },
26671
26672     /**
26673      * Returns True if the specified record id is selected.
26674      * @param {String} id The id of record to check
26675      * @return {Boolean}
26676      */
26677     isIdSelected : function(id){
26678         return (this.selections.key(id) ? true : false);
26679     },
26680
26681
26682     // private
26683     handleMouseDBClick : function(e, t){
26684         
26685     },
26686     // private
26687     handleMouseDown : function(e, t)
26688     {
26689             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26690         if(this.isLocked() || rowIndex < 0 ){
26691             return;
26692         };
26693         if(e.shiftKey && this.last !== false){
26694             var last = this.last;
26695             this.selectRange(last, rowIndex, e.ctrlKey);
26696             this.last = last; // reset the last
26697             t.focus();
26698     
26699         }else{
26700             var isSelected = this.isSelected(rowIndex);
26701             //Roo.log("select row:" + rowIndex);
26702             if(isSelected){
26703                 this.deselectRow(rowIndex);
26704             } else {
26705                         this.selectRow(rowIndex, true);
26706             }
26707     
26708             /*
26709                 if(e.button !== 0 && isSelected){
26710                 alert('rowIndex 2: ' + rowIndex);
26711                     view.focusRow(rowIndex);
26712                 }else if(e.ctrlKey && isSelected){
26713                     this.deselectRow(rowIndex);
26714                 }else if(!isSelected){
26715                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26716                     view.focusRow(rowIndex);
26717                 }
26718             */
26719         }
26720         this.fireEvent("afterselectionchange", this);
26721     },
26722     // private
26723     handleDragableRowClick :  function(grid, rowIndex, e) 
26724     {
26725         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26726             this.selectRow(rowIndex, false);
26727             grid.view.focusRow(rowIndex);
26728              this.fireEvent("afterselectionchange", this);
26729         }
26730     },
26731     
26732     /**
26733      * Selects multiple rows.
26734      * @param {Array} rows Array of the indexes of the row to select
26735      * @param {Boolean} keepExisting (optional) True to keep existing selections
26736      */
26737     selectRows : function(rows, keepExisting){
26738         if(!keepExisting){
26739             this.clearSelections();
26740         }
26741         for(var i = 0, len = rows.length; i < len; i++){
26742             this.selectRow(rows[i], true);
26743         }
26744     },
26745
26746     /**
26747      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26748      * @param {Number} startRow The index of the first row in the range
26749      * @param {Number} endRow The index of the last row in the range
26750      * @param {Boolean} keepExisting (optional) True to retain existing selections
26751      */
26752     selectRange : function(startRow, endRow, keepExisting){
26753         if(this.locked) {
26754             return;
26755         }
26756         if(!keepExisting){
26757             this.clearSelections();
26758         }
26759         if(startRow <= endRow){
26760             for(var i = startRow; i <= endRow; i++){
26761                 this.selectRow(i, true);
26762             }
26763         }else{
26764             for(var i = startRow; i >= endRow; i--){
26765                 this.selectRow(i, true);
26766             }
26767         }
26768     },
26769
26770     /**
26771      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26772      * @param {Number} startRow The index of the first row in the range
26773      * @param {Number} endRow The index of the last row in the range
26774      */
26775     deselectRange : function(startRow, endRow, preventViewNotify){
26776         if(this.locked) {
26777             return;
26778         }
26779         for(var i = startRow; i <= endRow; i++){
26780             this.deselectRow(i, preventViewNotify);
26781         }
26782     },
26783
26784     /**
26785      * Selects a row.
26786      * @param {Number} row The index of the row to select
26787      * @param {Boolean} keepExisting (optional) True to keep existing selections
26788      */
26789     selectRow : function(index, keepExisting, preventViewNotify)
26790     {
26791             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26792             return;
26793         }
26794         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26795             if(!keepExisting || this.singleSelect){
26796                 this.clearSelections();
26797             }
26798             
26799             var r = this.grid.store.getAt(index);
26800             //console.log('selectRow - record id :' + r.id);
26801             
26802             this.selections.add(r);
26803             this.last = this.lastActive = index;
26804             if(!preventViewNotify){
26805                 var proxy = new Roo.Element(
26806                                 this.grid.getRowDom(index)
26807                 );
26808                 proxy.addClass('bg-info info');
26809             }
26810             this.fireEvent("rowselect", this, index, r);
26811             this.fireEvent("selectionchange", this);
26812         }
26813     },
26814
26815     /**
26816      * Deselects a row.
26817      * @param {Number} row The index of the row to deselect
26818      */
26819     deselectRow : function(index, preventViewNotify)
26820     {
26821         if(this.locked) {
26822             return;
26823         }
26824         if(this.last == index){
26825             this.last = false;
26826         }
26827         if(this.lastActive == index){
26828             this.lastActive = false;
26829         }
26830         
26831         var r = this.grid.store.getAt(index);
26832         if (!r) {
26833             return;
26834         }
26835         
26836         this.selections.remove(r);
26837         //.console.log('deselectRow - record id :' + r.id);
26838         if(!preventViewNotify){
26839         
26840             var proxy = new Roo.Element(
26841                 this.grid.getRowDom(index)
26842             );
26843             proxy.removeClass('bg-info info');
26844         }
26845         this.fireEvent("rowdeselect", this, index);
26846         this.fireEvent("selectionchange", this);
26847     },
26848
26849     // private
26850     restoreLast : function(){
26851         if(this._last){
26852             this.last = this._last;
26853         }
26854     },
26855
26856     // private
26857     acceptsNav : function(row, col, cm){
26858         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26859     },
26860
26861     // private
26862     onEditorKey : function(field, e){
26863         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26864         if(k == e.TAB){
26865             e.stopEvent();
26866             ed.completeEdit();
26867             if(e.shiftKey){
26868                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26869             }else{
26870                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26871             }
26872         }else if(k == e.ENTER && !e.ctrlKey){
26873             e.stopEvent();
26874             ed.completeEdit();
26875             if(e.shiftKey){
26876                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26877             }else{
26878                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26879             }
26880         }else if(k == e.ESC){
26881             ed.cancelEdit();
26882         }
26883         if(newCell){
26884             g.startEditing(newCell[0], newCell[1]);
26885         }
26886     }
26887 });
26888 /*
26889  * Based on:
26890  * Ext JS Library 1.1.1
26891  * Copyright(c) 2006-2007, Ext JS, LLC.
26892  *
26893  * Originally Released Under LGPL - original licence link has changed is not relivant.
26894  *
26895  * Fork - LGPL
26896  * <script type="text/javascript">
26897  */
26898  
26899 /**
26900  * @class Roo.bootstrap.PagingToolbar
26901  * @extends Roo.bootstrap.NavSimplebar
26902  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26903  * @constructor
26904  * Create a new PagingToolbar
26905  * @param {Object} config The config object
26906  * @param {Roo.data.Store} store
26907  */
26908 Roo.bootstrap.PagingToolbar = function(config)
26909 {
26910     // old args format still supported... - xtype is prefered..
26911         // created from xtype...
26912     
26913     this.ds = config.dataSource;
26914     
26915     if (config.store && !this.ds) {
26916         this.store= Roo.factory(config.store, Roo.data);
26917         this.ds = this.store;
26918         this.ds.xmodule = this.xmodule || false;
26919     }
26920     
26921     this.toolbarItems = [];
26922     if (config.items) {
26923         this.toolbarItems = config.items;
26924     }
26925     
26926     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26927     
26928     this.cursor = 0;
26929     
26930     if (this.ds) { 
26931         this.bind(this.ds);
26932     }
26933     
26934     if (Roo.bootstrap.version == 4) {
26935         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26936     } else {
26937         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26938     }
26939     
26940 };
26941
26942 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26943     /**
26944      * @cfg {Roo.data.Store} dataSource
26945      * The underlying data store providing the paged data
26946      */
26947     /**
26948      * @cfg {String/HTMLElement/Element} container
26949      * container The id or element that will contain the toolbar
26950      */
26951     /**
26952      * @cfg {Boolean} displayInfo
26953      * True to display the displayMsg (defaults to false)
26954      */
26955     /**
26956      * @cfg {Number} pageSize
26957      * The number of records to display per page (defaults to 20)
26958      */
26959     pageSize: 20,
26960     /**
26961      * @cfg {String} displayMsg
26962      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26963      */
26964     displayMsg : 'Displaying {0} - {1} of {2}',
26965     /**
26966      * @cfg {String} emptyMsg
26967      * The message to display when no records are found (defaults to "No data to display")
26968      */
26969     emptyMsg : 'No data to display',
26970     /**
26971      * Customizable piece of the default paging text (defaults to "Page")
26972      * @type String
26973      */
26974     beforePageText : "Page",
26975     /**
26976      * Customizable piece of the default paging text (defaults to "of %0")
26977      * @type String
26978      */
26979     afterPageText : "of {0}",
26980     /**
26981      * Customizable piece of the default paging text (defaults to "First Page")
26982      * @type String
26983      */
26984     firstText : "First Page",
26985     /**
26986      * Customizable piece of the default paging text (defaults to "Previous Page")
26987      * @type String
26988      */
26989     prevText : "Previous Page",
26990     /**
26991      * Customizable piece of the default paging text (defaults to "Next Page")
26992      * @type String
26993      */
26994     nextText : "Next Page",
26995     /**
26996      * Customizable piece of the default paging text (defaults to "Last Page")
26997      * @type String
26998      */
26999     lastText : "Last Page",
27000     /**
27001      * Customizable piece of the default paging text (defaults to "Refresh")
27002      * @type String
27003      */
27004     refreshText : "Refresh",
27005
27006     buttons : false,
27007     // private
27008     onRender : function(ct, position) 
27009     {
27010         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27011         this.navgroup.parentId = this.id;
27012         this.navgroup.onRender(this.el, null);
27013         // add the buttons to the navgroup
27014         
27015         if(this.displayInfo){
27016             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27017             this.displayEl = this.el.select('.x-paging-info', true).first();
27018 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27019 //            this.displayEl = navel.el.select('span',true).first();
27020         }
27021         
27022         var _this = this;
27023         
27024         if(this.buttons){
27025             Roo.each(_this.buttons, function(e){ // this might need to use render????
27026                Roo.factory(e).render(_this.el);
27027             });
27028         }
27029             
27030         Roo.each(_this.toolbarItems, function(e) {
27031             _this.navgroup.addItem(e);
27032         });
27033         
27034         
27035         this.first = this.navgroup.addItem({
27036             tooltip: this.firstText,
27037             cls: "prev btn-outline-secondary",
27038             html : ' <i class="fa fa-step-backward"></i>',
27039             disabled: true,
27040             preventDefault: true,
27041             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27042         });
27043         
27044         this.prev =  this.navgroup.addItem({
27045             tooltip: this.prevText,
27046             cls: "prev btn-outline-secondary",
27047             html : ' <i class="fa fa-backward"></i>',
27048             disabled: true,
27049             preventDefault: true,
27050             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27051         });
27052     //this.addSeparator();
27053         
27054         
27055         var field = this.navgroup.addItem( {
27056             tagtype : 'span',
27057             cls : 'x-paging-position  btn-outline-secondary',
27058              disabled: true,
27059             html : this.beforePageText  +
27060                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27061                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27062          } ); //?? escaped?
27063         
27064         this.field = field.el.select('input', true).first();
27065         this.field.on("keydown", this.onPagingKeydown, this);
27066         this.field.on("focus", function(){this.dom.select();});
27067     
27068     
27069         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27070         //this.field.setHeight(18);
27071         //this.addSeparator();
27072         this.next = this.navgroup.addItem({
27073             tooltip: this.nextText,
27074             cls: "next btn-outline-secondary",
27075             html : ' <i class="fa fa-forward"></i>',
27076             disabled: true,
27077             preventDefault: true,
27078             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27079         });
27080         this.last = this.navgroup.addItem({
27081             tooltip: this.lastText,
27082             html : ' <i class="fa fa-step-forward"></i>',
27083             cls: "next btn-outline-secondary",
27084             disabled: true,
27085             preventDefault: true,
27086             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27087         });
27088     //this.addSeparator();
27089         this.loading = this.navgroup.addItem({
27090             tooltip: this.refreshText,
27091             cls: "btn-outline-secondary",
27092             html : ' <i class="fa fa-refresh"></i>',
27093             preventDefault: true,
27094             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27095         });
27096         
27097     },
27098
27099     // private
27100     updateInfo : function(){
27101         if(this.displayEl){
27102             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27103             var msg = count == 0 ?
27104                 this.emptyMsg :
27105                 String.format(
27106                     this.displayMsg,
27107                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27108                 );
27109             this.displayEl.update(msg);
27110         }
27111     },
27112
27113     // private
27114     onLoad : function(ds, r, o)
27115     {
27116         this.cursor = o.params.start ? o.params.start : 0;
27117         
27118         var d = this.getPageData(),
27119             ap = d.activePage,
27120             ps = d.pages;
27121         
27122         
27123         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27124         this.field.dom.value = ap;
27125         this.first.setDisabled(ap == 1);
27126         this.prev.setDisabled(ap == 1);
27127         this.next.setDisabled(ap == ps);
27128         this.last.setDisabled(ap == ps);
27129         this.loading.enable();
27130         this.updateInfo();
27131     },
27132
27133     // private
27134     getPageData : function(){
27135         var total = this.ds.getTotalCount();
27136         return {
27137             total : total,
27138             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27139             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27140         };
27141     },
27142
27143     // private
27144     onLoadError : function(){
27145         this.loading.enable();
27146     },
27147
27148     // private
27149     onPagingKeydown : function(e){
27150         var k = e.getKey();
27151         var d = this.getPageData();
27152         if(k == e.RETURN){
27153             var v = this.field.dom.value, pageNum;
27154             if(!v || isNaN(pageNum = parseInt(v, 10))){
27155                 this.field.dom.value = d.activePage;
27156                 return;
27157             }
27158             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27160             e.stopEvent();
27161         }
27162         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))
27163         {
27164           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27165           this.field.dom.value = pageNum;
27166           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27167           e.stopEvent();
27168         }
27169         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27170         {
27171           var v = this.field.dom.value, pageNum; 
27172           var increment = (e.shiftKey) ? 10 : 1;
27173           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27174                 increment *= -1;
27175           }
27176           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27177             this.field.dom.value = d.activePage;
27178             return;
27179           }
27180           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27181           {
27182             this.field.dom.value = parseInt(v, 10) + increment;
27183             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27184             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27185           }
27186           e.stopEvent();
27187         }
27188     },
27189
27190     // private
27191     beforeLoad : function(){
27192         if(this.loading){
27193             this.loading.disable();
27194         }
27195     },
27196
27197     // private
27198     onClick : function(which){
27199         
27200         var ds = this.ds;
27201         if (!ds) {
27202             return;
27203         }
27204         
27205         switch(which){
27206             case "first":
27207                 ds.load({params:{start: 0, limit: this.pageSize}});
27208             break;
27209             case "prev":
27210                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27211             break;
27212             case "next":
27213                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27214             break;
27215             case "last":
27216                 var total = ds.getTotalCount();
27217                 var extra = total % this.pageSize;
27218                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27219                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27220             break;
27221             case "refresh":
27222                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27223             break;
27224         }
27225     },
27226
27227     /**
27228      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27229      * @param {Roo.data.Store} store The data store to unbind
27230      */
27231     unbind : function(ds){
27232         ds.un("beforeload", this.beforeLoad, this);
27233         ds.un("load", this.onLoad, this);
27234         ds.un("loadexception", this.onLoadError, this);
27235         ds.un("remove", this.updateInfo, this);
27236         ds.un("add", this.updateInfo, this);
27237         this.ds = undefined;
27238     },
27239
27240     /**
27241      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27242      * @param {Roo.data.Store} store The data store to bind
27243      */
27244     bind : function(ds){
27245         ds.on("beforeload", this.beforeLoad, this);
27246         ds.on("load", this.onLoad, this);
27247         ds.on("loadexception", this.onLoadError, this);
27248         ds.on("remove", this.updateInfo, this);
27249         ds.on("add", this.updateInfo, this);
27250         this.ds = ds;
27251     }
27252 });/*
27253  * - LGPL
27254  *
27255  * element
27256  * 
27257  */
27258
27259 /**
27260  * @class Roo.bootstrap.MessageBar
27261  * @extends Roo.bootstrap.Component
27262  * Bootstrap MessageBar class
27263  * @cfg {String} html contents of the MessageBar
27264  * @cfg {String} weight (info | success | warning | danger) default info
27265  * @cfg {String} beforeClass insert the bar before the given class
27266  * @cfg {Boolean} closable (true | false) default false
27267  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27268  * 
27269  * @constructor
27270  * Create a new Element
27271  * @param {Object} config The config object
27272  */
27273
27274 Roo.bootstrap.MessageBar = function(config){
27275     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27276 };
27277
27278 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27279     
27280     html: '',
27281     weight: 'info',
27282     closable: false,
27283     fixed: false,
27284     beforeClass: 'bootstrap-sticky-wrap',
27285     
27286     getAutoCreate : function(){
27287         
27288         var cfg = {
27289             tag: 'div',
27290             cls: 'alert alert-dismissable alert-' + this.weight,
27291             cn: [
27292                 {
27293                     tag: 'span',
27294                     cls: 'message',
27295                     html: this.html || ''
27296                 }
27297             ]
27298         };
27299         
27300         if(this.fixed){
27301             cfg.cls += ' alert-messages-fixed';
27302         }
27303         
27304         if(this.closable){
27305             cfg.cn.push({
27306                 tag: 'button',
27307                 cls: 'close',
27308                 html: 'x'
27309             });
27310         }
27311         
27312         return cfg;
27313     },
27314     
27315     onRender : function(ct, position)
27316     {
27317         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27318         
27319         if(!this.el){
27320             var cfg = Roo.apply({},  this.getAutoCreate());
27321             cfg.id = Roo.id();
27322             
27323             if (this.cls) {
27324                 cfg.cls += ' ' + this.cls;
27325             }
27326             if (this.style) {
27327                 cfg.style = this.style;
27328             }
27329             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27330             
27331             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27332         }
27333         
27334         this.el.select('>button.close').on('click', this.hide, this);
27335         
27336     },
27337     
27338     show : function()
27339     {
27340         if (!this.rendered) {
27341             this.render();
27342         }
27343         
27344         this.el.show();
27345         
27346         this.fireEvent('show', this);
27347         
27348     },
27349     
27350     hide : function()
27351     {
27352         if (!this.rendered) {
27353             this.render();
27354         }
27355         
27356         this.el.hide();
27357         
27358         this.fireEvent('hide', this);
27359     },
27360     
27361     update : function()
27362     {
27363 //        var e = this.el.dom.firstChild;
27364 //        
27365 //        if(this.closable){
27366 //            e = e.nextSibling;
27367 //        }
27368 //        
27369 //        e.data = this.html || '';
27370
27371         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27372     }
27373    
27374 });
27375
27376  
27377
27378      /*
27379  * - LGPL
27380  *
27381  * Graph
27382  * 
27383  */
27384
27385
27386 /**
27387  * @class Roo.bootstrap.Graph
27388  * @extends Roo.bootstrap.Component
27389  * Bootstrap Graph class
27390 > Prameters
27391  -sm {number} sm 4
27392  -md {number} md 5
27393  @cfg {String} graphtype  bar | vbar | pie
27394  @cfg {number} g_x coodinator | centre x (pie)
27395  @cfg {number} g_y coodinator | centre y (pie)
27396  @cfg {number} g_r radius (pie)
27397  @cfg {number} g_height height of the chart (respected by all elements in the set)
27398  @cfg {number} g_width width of the chart (respected by all elements in the set)
27399  @cfg {Object} title The title of the chart
27400     
27401  -{Array}  values
27402  -opts (object) options for the chart 
27403      o {
27404      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27405      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27406      o vgutter (number)
27407      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.
27408      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27409      o to
27410      o stretch (boolean)
27411      o }
27412  -opts (object) options for the pie
27413      o{
27414      o cut
27415      o startAngle (number)
27416      o endAngle (number)
27417      } 
27418  *
27419  * @constructor
27420  * Create a new Input
27421  * @param {Object} config The config object
27422  */
27423
27424 Roo.bootstrap.Graph = function(config){
27425     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27426     
27427     this.addEvents({
27428         // img events
27429         /**
27430          * @event click
27431          * The img click event for the img.
27432          * @param {Roo.EventObject} e
27433          */
27434         "click" : true
27435     });
27436 };
27437
27438 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27439     
27440     sm: 4,
27441     md: 5,
27442     graphtype: 'bar',
27443     g_height: 250,
27444     g_width: 400,
27445     g_x: 50,
27446     g_y: 50,
27447     g_r: 30,
27448     opts:{
27449         //g_colors: this.colors,
27450         g_type: 'soft',
27451         g_gutter: '20%'
27452
27453     },
27454     title : false,
27455
27456     getAutoCreate : function(){
27457         
27458         var cfg = {
27459             tag: 'div',
27460             html : null
27461         };
27462         
27463         
27464         return  cfg;
27465     },
27466
27467     onRender : function(ct,position){
27468         
27469         
27470         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27471         
27472         if (typeof(Raphael) == 'undefined') {
27473             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27474             return;
27475         }
27476         
27477         this.raphael = Raphael(this.el.dom);
27478         
27479                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27480                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27481                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27482                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27483                 /*
27484                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27485                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27486                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27487                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27488                 
27489                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27490                 r.barchart(330, 10, 300, 220, data1);
27491                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27492                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27493                 */
27494                 
27495                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27496                 // r.barchart(30, 30, 560, 250,  xdata, {
27497                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27498                 //     axis : "0 0 1 1",
27499                 //     axisxlabels :  xdata
27500                 //     //yvalues : cols,
27501                    
27502                 // });
27503 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27504 //        
27505 //        this.load(null,xdata,{
27506 //                axis : "0 0 1 1",
27507 //                axisxlabels :  xdata
27508 //                });
27509
27510     },
27511
27512     load : function(graphtype,xdata,opts)
27513     {
27514         this.raphael.clear();
27515         if(!graphtype) {
27516             graphtype = this.graphtype;
27517         }
27518         if(!opts){
27519             opts = this.opts;
27520         }
27521         var r = this.raphael,
27522             fin = function () {
27523                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27524             },
27525             fout = function () {
27526                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27527             },
27528             pfin = function() {
27529                 this.sector.stop();
27530                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27531
27532                 if (this.label) {
27533                     this.label[0].stop();
27534                     this.label[0].attr({ r: 7.5 });
27535                     this.label[1].attr({ "font-weight": 800 });
27536                 }
27537             },
27538             pfout = function() {
27539                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27540
27541                 if (this.label) {
27542                     this.label[0].animate({ r: 5 }, 500, "bounce");
27543                     this.label[1].attr({ "font-weight": 400 });
27544                 }
27545             };
27546
27547         switch(graphtype){
27548             case 'bar':
27549                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27550                 break;
27551             case 'hbar':
27552                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27553                 break;
27554             case 'pie':
27555 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27556 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27557 //            
27558                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27559                 
27560                 break;
27561
27562         }
27563         
27564         if(this.title){
27565             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27566         }
27567         
27568     },
27569     
27570     setTitle: function(o)
27571     {
27572         this.title = o;
27573     },
27574     
27575     initEvents: function() {
27576         
27577         if(!this.href){
27578             this.el.on('click', this.onClick, this);
27579         }
27580     },
27581     
27582     onClick : function(e)
27583     {
27584         Roo.log('img onclick');
27585         this.fireEvent('click', this, e);
27586     }
27587    
27588 });
27589
27590  
27591 /*
27592  * - LGPL
27593  *
27594  * numberBox
27595  * 
27596  */
27597 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27598
27599 /**
27600  * @class Roo.bootstrap.dash.NumberBox
27601  * @extends Roo.bootstrap.Component
27602  * Bootstrap NumberBox class
27603  * @cfg {String} headline Box headline
27604  * @cfg {String} content Box content
27605  * @cfg {String} icon Box icon
27606  * @cfg {String} footer Footer text
27607  * @cfg {String} fhref Footer href
27608  * 
27609  * @constructor
27610  * Create a new NumberBox
27611  * @param {Object} config The config object
27612  */
27613
27614
27615 Roo.bootstrap.dash.NumberBox = function(config){
27616     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27617     
27618 };
27619
27620 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27621     
27622     headline : '',
27623     content : '',
27624     icon : '',
27625     footer : '',
27626     fhref : '',
27627     ficon : '',
27628     
27629     getAutoCreate : function(){
27630         
27631         var cfg = {
27632             tag : 'div',
27633             cls : 'small-box ',
27634             cn : [
27635                 {
27636                     tag : 'div',
27637                     cls : 'inner',
27638                     cn :[
27639                         {
27640                             tag : 'h3',
27641                             cls : 'roo-headline',
27642                             html : this.headline
27643                         },
27644                         {
27645                             tag : 'p',
27646                             cls : 'roo-content',
27647                             html : this.content
27648                         }
27649                     ]
27650                 }
27651             ]
27652         };
27653         
27654         if(this.icon){
27655             cfg.cn.push({
27656                 tag : 'div',
27657                 cls : 'icon',
27658                 cn :[
27659                     {
27660                         tag : 'i',
27661                         cls : 'ion ' + this.icon
27662                     }
27663                 ]
27664             });
27665         }
27666         
27667         if(this.footer){
27668             var footer = {
27669                 tag : 'a',
27670                 cls : 'small-box-footer',
27671                 href : this.fhref || '#',
27672                 html : this.footer
27673             };
27674             
27675             cfg.cn.push(footer);
27676             
27677         }
27678         
27679         return  cfg;
27680     },
27681
27682     onRender : function(ct,position){
27683         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27684
27685
27686        
27687                 
27688     },
27689
27690     setHeadline: function (value)
27691     {
27692         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27693     },
27694     
27695     setFooter: function (value, href)
27696     {
27697         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27698         
27699         if(href){
27700             this.el.select('a.small-box-footer',true).first().attr('href', href);
27701         }
27702         
27703     },
27704
27705     setContent: function (value)
27706     {
27707         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27708     },
27709
27710     initEvents: function() 
27711     {   
27712         
27713     }
27714     
27715 });
27716
27717  
27718 /*
27719  * - LGPL
27720  *
27721  * TabBox
27722  * 
27723  */
27724 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27725
27726 /**
27727  * @class Roo.bootstrap.dash.TabBox
27728  * @extends Roo.bootstrap.Component
27729  * Bootstrap TabBox class
27730  * @cfg {String} title Title of the TabBox
27731  * @cfg {String} icon Icon of the TabBox
27732  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27733  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27734  * 
27735  * @constructor
27736  * Create a new TabBox
27737  * @param {Object} config The config object
27738  */
27739
27740
27741 Roo.bootstrap.dash.TabBox = function(config){
27742     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27743     this.addEvents({
27744         // raw events
27745         /**
27746          * @event addpane
27747          * When a pane is added
27748          * @param {Roo.bootstrap.dash.TabPane} pane
27749          */
27750         "addpane" : true,
27751         /**
27752          * @event activatepane
27753          * When a pane is activated
27754          * @param {Roo.bootstrap.dash.TabPane} pane
27755          */
27756         "activatepane" : true
27757         
27758          
27759     });
27760     
27761     this.panes = [];
27762 };
27763
27764 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27765
27766     title : '',
27767     icon : false,
27768     showtabs : true,
27769     tabScrollable : false,
27770     
27771     getChildContainer : function()
27772     {
27773         return this.el.select('.tab-content', true).first();
27774     },
27775     
27776     getAutoCreate : function(){
27777         
27778         var header = {
27779             tag: 'li',
27780             cls: 'pull-left header',
27781             html: this.title,
27782             cn : []
27783         };
27784         
27785         if(this.icon){
27786             header.cn.push({
27787                 tag: 'i',
27788                 cls: 'fa ' + this.icon
27789             });
27790         }
27791         
27792         var h = {
27793             tag: 'ul',
27794             cls: 'nav nav-tabs pull-right',
27795             cn: [
27796                 header
27797             ]
27798         };
27799         
27800         if(this.tabScrollable){
27801             h = {
27802                 tag: 'div',
27803                 cls: 'tab-header',
27804                 cn: [
27805                     {
27806                         tag: 'ul',
27807                         cls: 'nav nav-tabs pull-right',
27808                         cn: [
27809                             header
27810                         ]
27811                     }
27812                 ]
27813             };
27814         }
27815         
27816         var cfg = {
27817             tag: 'div',
27818             cls: 'nav-tabs-custom',
27819             cn: [
27820                 h,
27821                 {
27822                     tag: 'div',
27823                     cls: 'tab-content no-padding',
27824                     cn: []
27825                 }
27826             ]
27827         };
27828
27829         return  cfg;
27830     },
27831     initEvents : function()
27832     {
27833         //Roo.log('add add pane handler');
27834         this.on('addpane', this.onAddPane, this);
27835     },
27836      /**
27837      * Updates the box title
27838      * @param {String} html to set the title to.
27839      */
27840     setTitle : function(value)
27841     {
27842         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27843     },
27844     onAddPane : function(pane)
27845     {
27846         this.panes.push(pane);
27847         //Roo.log('addpane');
27848         //Roo.log(pane);
27849         // tabs are rendere left to right..
27850         if(!this.showtabs){
27851             return;
27852         }
27853         
27854         var ctr = this.el.select('.nav-tabs', true).first();
27855          
27856          
27857         var existing = ctr.select('.nav-tab',true);
27858         var qty = existing.getCount();;
27859         
27860         
27861         var tab = ctr.createChild({
27862             tag : 'li',
27863             cls : 'nav-tab' + (qty ? '' : ' active'),
27864             cn : [
27865                 {
27866                     tag : 'a',
27867                     href:'#',
27868                     html : pane.title
27869                 }
27870             ]
27871         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27872         pane.tab = tab;
27873         
27874         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27875         if (!qty) {
27876             pane.el.addClass('active');
27877         }
27878         
27879                 
27880     },
27881     onTabClick : function(ev,un,ob,pane)
27882     {
27883         //Roo.log('tab - prev default');
27884         ev.preventDefault();
27885         
27886         
27887         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27888         pane.tab.addClass('active');
27889         //Roo.log(pane.title);
27890         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27891         // technically we should have a deactivate event.. but maybe add later.
27892         // and it should not de-activate the selected tab...
27893         this.fireEvent('activatepane', pane);
27894         pane.el.addClass('active');
27895         pane.fireEvent('activate');
27896         
27897         
27898     },
27899     
27900     getActivePane : function()
27901     {
27902         var r = false;
27903         Roo.each(this.panes, function(p) {
27904             if(p.el.hasClass('active')){
27905                 r = p;
27906                 return false;
27907             }
27908             
27909             return;
27910         });
27911         
27912         return r;
27913     }
27914     
27915     
27916 });
27917
27918  
27919 /*
27920  * - LGPL
27921  *
27922  * Tab pane
27923  * 
27924  */
27925 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27926 /**
27927  * @class Roo.bootstrap.TabPane
27928  * @extends Roo.bootstrap.Component
27929  * Bootstrap TabPane class
27930  * @cfg {Boolean} active (false | true) Default false
27931  * @cfg {String} title title of panel
27932
27933  * 
27934  * @constructor
27935  * Create a new TabPane
27936  * @param {Object} config The config object
27937  */
27938
27939 Roo.bootstrap.dash.TabPane = function(config){
27940     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27941     
27942     this.addEvents({
27943         // raw events
27944         /**
27945          * @event activate
27946          * When a pane is activated
27947          * @param {Roo.bootstrap.dash.TabPane} pane
27948          */
27949         "activate" : true
27950          
27951     });
27952 };
27953
27954 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27955     
27956     active : false,
27957     title : '',
27958     
27959     // the tabBox that this is attached to.
27960     tab : false,
27961      
27962     getAutoCreate : function() 
27963     {
27964         var cfg = {
27965             tag: 'div',
27966             cls: 'tab-pane'
27967         };
27968         
27969         if(this.active){
27970             cfg.cls += ' active';
27971         }
27972         
27973         return cfg;
27974     },
27975     initEvents  : function()
27976     {
27977         //Roo.log('trigger add pane handler');
27978         this.parent().fireEvent('addpane', this)
27979     },
27980     
27981      /**
27982      * Updates the tab title 
27983      * @param {String} html to set the title to.
27984      */
27985     setTitle: function(str)
27986     {
27987         if (!this.tab) {
27988             return;
27989         }
27990         this.title = str;
27991         this.tab.select('a', true).first().dom.innerHTML = str;
27992         
27993     }
27994     
27995     
27996     
27997 });
27998
27999  
28000
28001
28002  /*
28003  * - LGPL
28004  *
28005  * menu
28006  * 
28007  */
28008 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28009
28010 /**
28011  * @class Roo.bootstrap.menu.Menu
28012  * @extends Roo.bootstrap.Component
28013  * Bootstrap Menu class - container for Menu
28014  * @cfg {String} html Text of the menu
28015  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28016  * @cfg {String} icon Font awesome icon
28017  * @cfg {String} pos Menu align to (top | bottom) default bottom
28018  * 
28019  * 
28020  * @constructor
28021  * Create a new Menu
28022  * @param {Object} config The config object
28023  */
28024
28025
28026 Roo.bootstrap.menu.Menu = function(config){
28027     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28028     
28029     this.addEvents({
28030         /**
28031          * @event beforeshow
28032          * Fires before this menu is displayed
28033          * @param {Roo.bootstrap.menu.Menu} this
28034          */
28035         beforeshow : true,
28036         /**
28037          * @event beforehide
28038          * Fires before this menu is hidden
28039          * @param {Roo.bootstrap.menu.Menu} this
28040          */
28041         beforehide : true,
28042         /**
28043          * @event show
28044          * Fires after this menu is displayed
28045          * @param {Roo.bootstrap.menu.Menu} this
28046          */
28047         show : true,
28048         /**
28049          * @event hide
28050          * Fires after this menu is hidden
28051          * @param {Roo.bootstrap.menu.Menu} this
28052          */
28053         hide : true,
28054         /**
28055          * @event click
28056          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28057          * @param {Roo.bootstrap.menu.Menu} this
28058          * @param {Roo.EventObject} e
28059          */
28060         click : true
28061     });
28062     
28063 };
28064
28065 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28066     
28067     submenu : false,
28068     html : '',
28069     weight : 'default',
28070     icon : false,
28071     pos : 'bottom',
28072     
28073     
28074     getChildContainer : function() {
28075         if(this.isSubMenu){
28076             return this.el;
28077         }
28078         
28079         return this.el.select('ul.dropdown-menu', true).first();  
28080     },
28081     
28082     getAutoCreate : function()
28083     {
28084         var text = [
28085             {
28086                 tag : 'span',
28087                 cls : 'roo-menu-text',
28088                 html : this.html
28089             }
28090         ];
28091         
28092         if(this.icon){
28093             text.unshift({
28094                 tag : 'i',
28095                 cls : 'fa ' + this.icon
28096             })
28097         }
28098         
28099         
28100         var cfg = {
28101             tag : 'div',
28102             cls : 'btn-group',
28103             cn : [
28104                 {
28105                     tag : 'button',
28106                     cls : 'dropdown-button btn btn-' + this.weight,
28107                     cn : text
28108                 },
28109                 {
28110                     tag : 'button',
28111                     cls : 'dropdown-toggle btn btn-' + this.weight,
28112                     cn : [
28113                         {
28114                             tag : 'span',
28115                             cls : 'caret'
28116                         }
28117                     ]
28118                 },
28119                 {
28120                     tag : 'ul',
28121                     cls : 'dropdown-menu'
28122                 }
28123             ]
28124             
28125         };
28126         
28127         if(this.pos == 'top'){
28128             cfg.cls += ' dropup';
28129         }
28130         
28131         if(this.isSubMenu){
28132             cfg = {
28133                 tag : 'ul',
28134                 cls : 'dropdown-menu'
28135             }
28136         }
28137         
28138         return cfg;
28139     },
28140     
28141     onRender : function(ct, position)
28142     {
28143         this.isSubMenu = ct.hasClass('dropdown-submenu');
28144         
28145         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28146     },
28147     
28148     initEvents : function() 
28149     {
28150         if(this.isSubMenu){
28151             return;
28152         }
28153         
28154         this.hidden = true;
28155         
28156         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28157         this.triggerEl.on('click', this.onTriggerPress, this);
28158         
28159         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28160         this.buttonEl.on('click', this.onClick, this);
28161         
28162     },
28163     
28164     list : function()
28165     {
28166         if(this.isSubMenu){
28167             return this.el;
28168         }
28169         
28170         return this.el.select('ul.dropdown-menu', true).first();
28171     },
28172     
28173     onClick : function(e)
28174     {
28175         this.fireEvent("click", this, e);
28176     },
28177     
28178     onTriggerPress  : function(e)
28179     {   
28180         if (this.isVisible()) {
28181             this.hide();
28182         } else {
28183             this.show();
28184         }
28185     },
28186     
28187     isVisible : function(){
28188         return !this.hidden;
28189     },
28190     
28191     show : function()
28192     {
28193         this.fireEvent("beforeshow", this);
28194         
28195         this.hidden = false;
28196         this.el.addClass('open');
28197         
28198         Roo.get(document).on("mouseup", this.onMouseUp, this);
28199         
28200         this.fireEvent("show", this);
28201         
28202         
28203     },
28204     
28205     hide : function()
28206     {
28207         this.fireEvent("beforehide", this);
28208         
28209         this.hidden = true;
28210         this.el.removeClass('open');
28211         
28212         Roo.get(document).un("mouseup", this.onMouseUp);
28213         
28214         this.fireEvent("hide", this);
28215     },
28216     
28217     onMouseUp : function()
28218     {
28219         this.hide();
28220     }
28221     
28222 });
28223
28224  
28225  /*
28226  * - LGPL
28227  *
28228  * menu item
28229  * 
28230  */
28231 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28232
28233 /**
28234  * @class Roo.bootstrap.menu.Item
28235  * @extends Roo.bootstrap.Component
28236  * Bootstrap MenuItem class
28237  * @cfg {Boolean} submenu (true | false) default false
28238  * @cfg {String} html text of the item
28239  * @cfg {String} href the link
28240  * @cfg {Boolean} disable (true | false) default false
28241  * @cfg {Boolean} preventDefault (true | false) default true
28242  * @cfg {String} icon Font awesome icon
28243  * @cfg {String} pos Submenu align to (left | right) default right 
28244  * 
28245  * 
28246  * @constructor
28247  * Create a new Item
28248  * @param {Object} config The config object
28249  */
28250
28251
28252 Roo.bootstrap.menu.Item = function(config){
28253     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28254     this.addEvents({
28255         /**
28256          * @event mouseover
28257          * Fires when the mouse is hovering over this menu
28258          * @param {Roo.bootstrap.menu.Item} this
28259          * @param {Roo.EventObject} e
28260          */
28261         mouseover : true,
28262         /**
28263          * @event mouseout
28264          * Fires when the mouse exits this menu
28265          * @param {Roo.bootstrap.menu.Item} this
28266          * @param {Roo.EventObject} e
28267          */
28268         mouseout : true,
28269         // raw events
28270         /**
28271          * @event click
28272          * The raw click event for the entire grid.
28273          * @param {Roo.EventObject} e
28274          */
28275         click : true
28276     });
28277 };
28278
28279 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28280     
28281     submenu : false,
28282     href : '',
28283     html : '',
28284     preventDefault: true,
28285     disable : false,
28286     icon : false,
28287     pos : 'right',
28288     
28289     getAutoCreate : function()
28290     {
28291         var text = [
28292             {
28293                 tag : 'span',
28294                 cls : 'roo-menu-item-text',
28295                 html : this.html
28296             }
28297         ];
28298         
28299         if(this.icon){
28300             text.unshift({
28301                 tag : 'i',
28302                 cls : 'fa ' + this.icon
28303             })
28304         }
28305         
28306         var cfg = {
28307             tag : 'li',
28308             cn : [
28309                 {
28310                     tag : 'a',
28311                     href : this.href || '#',
28312                     cn : text
28313                 }
28314             ]
28315         };
28316         
28317         if(this.disable){
28318             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28319         }
28320         
28321         if(this.submenu){
28322             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28323             
28324             if(this.pos == 'left'){
28325                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28326             }
28327         }
28328         
28329         return cfg;
28330     },
28331     
28332     initEvents : function() 
28333     {
28334         this.el.on('mouseover', this.onMouseOver, this);
28335         this.el.on('mouseout', this.onMouseOut, this);
28336         
28337         this.el.select('a', true).first().on('click', this.onClick, this);
28338         
28339     },
28340     
28341     onClick : function(e)
28342     {
28343         if(this.preventDefault){
28344             e.preventDefault();
28345         }
28346         
28347         this.fireEvent("click", this, e);
28348     },
28349     
28350     onMouseOver : function(e)
28351     {
28352         if(this.submenu && this.pos == 'left'){
28353             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28354         }
28355         
28356         this.fireEvent("mouseover", this, e);
28357     },
28358     
28359     onMouseOut : function(e)
28360     {
28361         this.fireEvent("mouseout", this, e);
28362     }
28363 });
28364
28365  
28366
28367  /*
28368  * - LGPL
28369  *
28370  * menu separator
28371  * 
28372  */
28373 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28374
28375 /**
28376  * @class Roo.bootstrap.menu.Separator
28377  * @extends Roo.bootstrap.Component
28378  * Bootstrap Separator class
28379  * 
28380  * @constructor
28381  * Create a new Separator
28382  * @param {Object} config The config object
28383  */
28384
28385
28386 Roo.bootstrap.menu.Separator = function(config){
28387     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28388 };
28389
28390 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28391     
28392     getAutoCreate : function(){
28393         var cfg = {
28394             tag : 'li',
28395             cls: 'divider'
28396         };
28397         
28398         return cfg;
28399     }
28400    
28401 });
28402
28403  
28404
28405  /*
28406  * - LGPL
28407  *
28408  * Tooltip
28409  * 
28410  */
28411
28412 /**
28413  * @class Roo.bootstrap.Tooltip
28414  * Bootstrap Tooltip class
28415  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28416  * to determine which dom element triggers the tooltip.
28417  * 
28418  * It needs to add support for additional attributes like tooltip-position
28419  * 
28420  * @constructor
28421  * Create a new Toolti
28422  * @param {Object} config The config object
28423  */
28424
28425 Roo.bootstrap.Tooltip = function(config){
28426     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28427     
28428     this.alignment = Roo.bootstrap.Tooltip.alignment;
28429     
28430     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28431         this.alignment = config.alignment;
28432     }
28433     
28434 };
28435
28436 Roo.apply(Roo.bootstrap.Tooltip, {
28437     /**
28438      * @function init initialize tooltip monitoring.
28439      * @static
28440      */
28441     currentEl : false,
28442     currentTip : false,
28443     currentRegion : false,
28444     
28445     //  init : delay?
28446     
28447     init : function()
28448     {
28449         Roo.get(document).on('mouseover', this.enter ,this);
28450         Roo.get(document).on('mouseout', this.leave, this);
28451          
28452         
28453         this.currentTip = new Roo.bootstrap.Tooltip();
28454     },
28455     
28456     enter : function(ev)
28457     {
28458         var dom = ev.getTarget();
28459         
28460         //Roo.log(['enter',dom]);
28461         var el = Roo.fly(dom);
28462         if (this.currentEl) {
28463             //Roo.log(dom);
28464             //Roo.log(this.currentEl);
28465             //Roo.log(this.currentEl.contains(dom));
28466             if (this.currentEl == el) {
28467                 return;
28468             }
28469             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28470                 return;
28471             }
28472
28473         }
28474         
28475         if (this.currentTip.el) {
28476             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28477         }    
28478         //Roo.log(ev);
28479         
28480         if(!el || el.dom == document){
28481             return;
28482         }
28483         
28484         var bindEl = el;
28485         
28486         // you can not look for children, as if el is the body.. then everythign is the child..
28487         if (!el.attr('tooltip')) { //
28488             if (!el.select("[tooltip]").elements.length) {
28489                 return;
28490             }
28491             // is the mouse over this child...?
28492             bindEl = el.select("[tooltip]").first();
28493             var xy = ev.getXY();
28494             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28495                 //Roo.log("not in region.");
28496                 return;
28497             }
28498             //Roo.log("child element over..");
28499             
28500         }
28501         this.currentEl = bindEl;
28502         this.currentTip.bind(bindEl);
28503         this.currentRegion = Roo.lib.Region.getRegion(dom);
28504         this.currentTip.enter();
28505         
28506     },
28507     leave : function(ev)
28508     {
28509         var dom = ev.getTarget();
28510         //Roo.log(['leave',dom]);
28511         if (!this.currentEl) {
28512             return;
28513         }
28514         
28515         
28516         if (dom != this.currentEl.dom) {
28517             return;
28518         }
28519         var xy = ev.getXY();
28520         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28521             return;
28522         }
28523         // only activate leave if mouse cursor is outside... bounding box..
28524         
28525         
28526         
28527         
28528         if (this.currentTip) {
28529             this.currentTip.leave();
28530         }
28531         //Roo.log('clear currentEl');
28532         this.currentEl = false;
28533         
28534         
28535     },
28536     alignment : {
28537         'left' : ['r-l', [-2,0], 'right'],
28538         'right' : ['l-r', [2,0], 'left'],
28539         'bottom' : ['t-b', [0,2], 'top'],
28540         'top' : [ 'b-t', [0,-2], 'bottom']
28541     }
28542     
28543 });
28544
28545
28546 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28547     
28548     
28549     bindEl : false,
28550     
28551     delay : null, // can be { show : 300 , hide: 500}
28552     
28553     timeout : null,
28554     
28555     hoverState : null, //???
28556     
28557     placement : 'bottom', 
28558     
28559     alignment : false,
28560     
28561     getAutoCreate : function(){
28562     
28563         var cfg = {
28564            cls : 'tooltip',   
28565            role : 'tooltip',
28566            cn : [
28567                 {
28568                     cls : 'tooltip-arrow arrow'
28569                 },
28570                 {
28571                     cls : 'tooltip-inner'
28572                 }
28573            ]
28574         };
28575         
28576         return cfg;
28577     },
28578     bind : function(el)
28579     {
28580         this.bindEl = el;
28581     },
28582     
28583     initEvents : function()
28584     {
28585         this.arrowEl = this.el.select('.arrow', true).first();
28586         this.innerEl = this.el.select('.tooltip-inner', true).first();
28587     },
28588     
28589     enter : function () {
28590        
28591         if (this.timeout != null) {
28592             clearTimeout(this.timeout);
28593         }
28594         
28595         this.hoverState = 'in';
28596          //Roo.log("enter - show");
28597         if (!this.delay || !this.delay.show) {
28598             this.show();
28599             return;
28600         }
28601         var _t = this;
28602         this.timeout = setTimeout(function () {
28603             if (_t.hoverState == 'in') {
28604                 _t.show();
28605             }
28606         }, this.delay.show);
28607     },
28608     leave : function()
28609     {
28610         clearTimeout(this.timeout);
28611     
28612         this.hoverState = 'out';
28613          if (!this.delay || !this.delay.hide) {
28614             this.hide();
28615             return;
28616         }
28617        
28618         var _t = this;
28619         this.timeout = setTimeout(function () {
28620             //Roo.log("leave - timeout");
28621             
28622             if (_t.hoverState == 'out') {
28623                 _t.hide();
28624                 Roo.bootstrap.Tooltip.currentEl = false;
28625             }
28626         }, delay);
28627     },
28628     
28629     show : function (msg)
28630     {
28631         if (!this.el) {
28632             this.render(document.body);
28633         }
28634         // set content.
28635         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28636         
28637         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28638         
28639         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28640         
28641         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28642                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28643         
28644         var placement = typeof this.placement == 'function' ?
28645             this.placement.call(this, this.el, on_el) :
28646             this.placement;
28647             
28648         var autoToken = /\s?auto?\s?/i;
28649         var autoPlace = autoToken.test(placement);
28650         if (autoPlace) {
28651             placement = placement.replace(autoToken, '') || 'top';
28652         }
28653         
28654         //this.el.detach()
28655         //this.el.setXY([0,0]);
28656         this.el.show();
28657         //this.el.dom.style.display='block';
28658         
28659         //this.el.appendTo(on_el);
28660         
28661         var p = this.getPosition();
28662         var box = this.el.getBox();
28663         
28664         if (autoPlace) {
28665             // fixme..
28666         }
28667         
28668         var align = this.alignment[placement];
28669         
28670         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28671         
28672         if(placement == 'top' || placement == 'bottom'){
28673             if(xy[0] < 0){
28674                 placement = 'right';
28675             }
28676             
28677             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28678                 placement = 'left';
28679             }
28680             
28681             var scroll = Roo.select('body', true).first().getScroll();
28682             
28683             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28684                 placement = 'top';
28685             }
28686             
28687             align = this.alignment[placement];
28688             
28689             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28690             
28691         }
28692         
28693         this.el.alignTo(this.bindEl, align[0],align[1]);
28694         //var arrow = this.el.select('.arrow',true).first();
28695         //arrow.set(align[2], 
28696         
28697         this.el.addClass(placement);
28698         this.el.addClass("bs-tooltip-"+ placement);
28699         
28700         this.el.addClass('in fade show');
28701         
28702         this.hoverState = null;
28703         
28704         if (this.el.hasClass('fade')) {
28705             // fade it?
28706         }
28707         
28708         
28709         
28710         
28711         
28712     },
28713     hide : function()
28714     {
28715          
28716         if (!this.el) {
28717             return;
28718         }
28719         //this.el.setXY([0,0]);
28720         this.el.removeClass(['show', 'in']);
28721         //this.el.hide();
28722         
28723     }
28724     
28725 });
28726  
28727
28728  /*
28729  * - LGPL
28730  *
28731  * Location Picker
28732  * 
28733  */
28734
28735 /**
28736  * @class Roo.bootstrap.LocationPicker
28737  * @extends Roo.bootstrap.Component
28738  * Bootstrap LocationPicker class
28739  * @cfg {Number} latitude Position when init default 0
28740  * @cfg {Number} longitude Position when init default 0
28741  * @cfg {Number} zoom default 15
28742  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28743  * @cfg {Boolean} mapTypeControl default false
28744  * @cfg {Boolean} disableDoubleClickZoom default false
28745  * @cfg {Boolean} scrollwheel default true
28746  * @cfg {Boolean} streetViewControl default false
28747  * @cfg {Number} radius default 0
28748  * @cfg {String} locationName
28749  * @cfg {Boolean} draggable default true
28750  * @cfg {Boolean} enableAutocomplete default false
28751  * @cfg {Boolean} enableReverseGeocode default true
28752  * @cfg {String} markerTitle
28753  * 
28754  * @constructor
28755  * Create a new LocationPicker
28756  * @param {Object} config The config object
28757  */
28758
28759
28760 Roo.bootstrap.LocationPicker = function(config){
28761     
28762     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28763     
28764     this.addEvents({
28765         /**
28766          * @event initial
28767          * Fires when the picker initialized.
28768          * @param {Roo.bootstrap.LocationPicker} this
28769          * @param {Google Location} location
28770          */
28771         initial : true,
28772         /**
28773          * @event positionchanged
28774          * Fires when the picker position changed.
28775          * @param {Roo.bootstrap.LocationPicker} this
28776          * @param {Google Location} location
28777          */
28778         positionchanged : true,
28779         /**
28780          * @event resize
28781          * Fires when the map resize.
28782          * @param {Roo.bootstrap.LocationPicker} this
28783          */
28784         resize : true,
28785         /**
28786          * @event show
28787          * Fires when the map show.
28788          * @param {Roo.bootstrap.LocationPicker} this
28789          */
28790         show : true,
28791         /**
28792          * @event hide
28793          * Fires when the map hide.
28794          * @param {Roo.bootstrap.LocationPicker} this
28795          */
28796         hide : true,
28797         /**
28798          * @event mapClick
28799          * Fires when click the map.
28800          * @param {Roo.bootstrap.LocationPicker} this
28801          * @param {Map event} e
28802          */
28803         mapClick : true,
28804         /**
28805          * @event mapRightClick
28806          * Fires when right click the map.
28807          * @param {Roo.bootstrap.LocationPicker} this
28808          * @param {Map event} e
28809          */
28810         mapRightClick : true,
28811         /**
28812          * @event markerClick
28813          * Fires when click the marker.
28814          * @param {Roo.bootstrap.LocationPicker} this
28815          * @param {Map event} e
28816          */
28817         markerClick : true,
28818         /**
28819          * @event markerRightClick
28820          * Fires when right click the marker.
28821          * @param {Roo.bootstrap.LocationPicker} this
28822          * @param {Map event} e
28823          */
28824         markerRightClick : true,
28825         /**
28826          * @event OverlayViewDraw
28827          * Fires when OverlayView Draw
28828          * @param {Roo.bootstrap.LocationPicker} this
28829          */
28830         OverlayViewDraw : true,
28831         /**
28832          * @event OverlayViewOnAdd
28833          * Fires when OverlayView Draw
28834          * @param {Roo.bootstrap.LocationPicker} this
28835          */
28836         OverlayViewOnAdd : true,
28837         /**
28838          * @event OverlayViewOnRemove
28839          * Fires when OverlayView Draw
28840          * @param {Roo.bootstrap.LocationPicker} this
28841          */
28842         OverlayViewOnRemove : true,
28843         /**
28844          * @event OverlayViewShow
28845          * Fires when OverlayView Draw
28846          * @param {Roo.bootstrap.LocationPicker} this
28847          * @param {Pixel} cpx
28848          */
28849         OverlayViewShow : true,
28850         /**
28851          * @event OverlayViewHide
28852          * Fires when OverlayView Draw
28853          * @param {Roo.bootstrap.LocationPicker} this
28854          */
28855         OverlayViewHide : true,
28856         /**
28857          * @event loadexception
28858          * Fires when load google lib failed.
28859          * @param {Roo.bootstrap.LocationPicker} this
28860          */
28861         loadexception : true
28862     });
28863         
28864 };
28865
28866 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28867     
28868     gMapContext: false,
28869     
28870     latitude: 0,
28871     longitude: 0,
28872     zoom: 15,
28873     mapTypeId: false,
28874     mapTypeControl: false,
28875     disableDoubleClickZoom: false,
28876     scrollwheel: true,
28877     streetViewControl: false,
28878     radius: 0,
28879     locationName: '',
28880     draggable: true,
28881     enableAutocomplete: false,
28882     enableReverseGeocode: true,
28883     markerTitle: '',
28884     
28885     getAutoCreate: function()
28886     {
28887
28888         var cfg = {
28889             tag: 'div',
28890             cls: 'roo-location-picker'
28891         };
28892         
28893         return cfg
28894     },
28895     
28896     initEvents: function(ct, position)
28897     {       
28898         if(!this.el.getWidth() || this.isApplied()){
28899             return;
28900         }
28901         
28902         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28903         
28904         this.initial();
28905     },
28906     
28907     initial: function()
28908     {
28909         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28910             this.fireEvent('loadexception', this);
28911             return;
28912         }
28913         
28914         if(!this.mapTypeId){
28915             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28916         }
28917         
28918         this.gMapContext = this.GMapContext();
28919         
28920         this.initOverlayView();
28921         
28922         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28923         
28924         var _this = this;
28925                 
28926         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28927             _this.setPosition(_this.gMapContext.marker.position);
28928         });
28929         
28930         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28931             _this.fireEvent('mapClick', this, event);
28932             
28933         });
28934
28935         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28936             _this.fireEvent('mapRightClick', this, event);
28937             
28938         });
28939         
28940         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28941             _this.fireEvent('markerClick', this, event);
28942             
28943         });
28944
28945         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28946             _this.fireEvent('markerRightClick', this, event);
28947             
28948         });
28949         
28950         this.setPosition(this.gMapContext.location);
28951         
28952         this.fireEvent('initial', this, this.gMapContext.location);
28953     },
28954     
28955     initOverlayView: function()
28956     {
28957         var _this = this;
28958         
28959         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28960             
28961             draw: function()
28962             {
28963                 _this.fireEvent('OverlayViewDraw', _this);
28964             },
28965             
28966             onAdd: function()
28967             {
28968                 _this.fireEvent('OverlayViewOnAdd', _this);
28969             },
28970             
28971             onRemove: function()
28972             {
28973                 _this.fireEvent('OverlayViewOnRemove', _this);
28974             },
28975             
28976             show: function(cpx)
28977             {
28978                 _this.fireEvent('OverlayViewShow', _this, cpx);
28979             },
28980             
28981             hide: function()
28982             {
28983                 _this.fireEvent('OverlayViewHide', _this);
28984             }
28985             
28986         });
28987     },
28988     
28989     fromLatLngToContainerPixel: function(event)
28990     {
28991         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28992     },
28993     
28994     isApplied: function() 
28995     {
28996         return this.getGmapContext() == false ? false : true;
28997     },
28998     
28999     getGmapContext: function() 
29000     {
29001         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29002     },
29003     
29004     GMapContext: function() 
29005     {
29006         var position = new google.maps.LatLng(this.latitude, this.longitude);
29007         
29008         var _map = new google.maps.Map(this.el.dom, {
29009             center: position,
29010             zoom: this.zoom,
29011             mapTypeId: this.mapTypeId,
29012             mapTypeControl: this.mapTypeControl,
29013             disableDoubleClickZoom: this.disableDoubleClickZoom,
29014             scrollwheel: this.scrollwheel,
29015             streetViewControl: this.streetViewControl,
29016             locationName: this.locationName,
29017             draggable: this.draggable,
29018             enableAutocomplete: this.enableAutocomplete,
29019             enableReverseGeocode: this.enableReverseGeocode
29020         });
29021         
29022         var _marker = new google.maps.Marker({
29023             position: position,
29024             map: _map,
29025             title: this.markerTitle,
29026             draggable: this.draggable
29027         });
29028         
29029         return {
29030             map: _map,
29031             marker: _marker,
29032             circle: null,
29033             location: position,
29034             radius: this.radius,
29035             locationName: this.locationName,
29036             addressComponents: {
29037                 formatted_address: null,
29038                 addressLine1: null,
29039                 addressLine2: null,
29040                 streetName: null,
29041                 streetNumber: null,
29042                 city: null,
29043                 district: null,
29044                 state: null,
29045                 stateOrProvince: null
29046             },
29047             settings: this,
29048             domContainer: this.el.dom,
29049             geodecoder: new google.maps.Geocoder()
29050         };
29051     },
29052     
29053     drawCircle: function(center, radius, options) 
29054     {
29055         if (this.gMapContext.circle != null) {
29056             this.gMapContext.circle.setMap(null);
29057         }
29058         if (radius > 0) {
29059             radius *= 1;
29060             options = Roo.apply({}, options, {
29061                 strokeColor: "#0000FF",
29062                 strokeOpacity: .35,
29063                 strokeWeight: 2,
29064                 fillColor: "#0000FF",
29065                 fillOpacity: .2
29066             });
29067             
29068             options.map = this.gMapContext.map;
29069             options.radius = radius;
29070             options.center = center;
29071             this.gMapContext.circle = new google.maps.Circle(options);
29072             return this.gMapContext.circle;
29073         }
29074         
29075         return null;
29076     },
29077     
29078     setPosition: function(location) 
29079     {
29080         this.gMapContext.location = location;
29081         this.gMapContext.marker.setPosition(location);
29082         this.gMapContext.map.panTo(location);
29083         this.drawCircle(location, this.gMapContext.radius, {});
29084         
29085         var _this = this;
29086         
29087         if (this.gMapContext.settings.enableReverseGeocode) {
29088             this.gMapContext.geodecoder.geocode({
29089                 latLng: this.gMapContext.location
29090             }, function(results, status) {
29091                 
29092                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29093                     _this.gMapContext.locationName = results[0].formatted_address;
29094                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29095                     
29096                     _this.fireEvent('positionchanged', this, location);
29097                 }
29098             });
29099             
29100             return;
29101         }
29102         
29103         this.fireEvent('positionchanged', this, location);
29104     },
29105     
29106     resize: function()
29107     {
29108         google.maps.event.trigger(this.gMapContext.map, "resize");
29109         
29110         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29111         
29112         this.fireEvent('resize', this);
29113     },
29114     
29115     setPositionByLatLng: function(latitude, longitude)
29116     {
29117         this.setPosition(new google.maps.LatLng(latitude, longitude));
29118     },
29119     
29120     getCurrentPosition: function() 
29121     {
29122         return {
29123             latitude: this.gMapContext.location.lat(),
29124             longitude: this.gMapContext.location.lng()
29125         };
29126     },
29127     
29128     getAddressName: function() 
29129     {
29130         return this.gMapContext.locationName;
29131     },
29132     
29133     getAddressComponents: function() 
29134     {
29135         return this.gMapContext.addressComponents;
29136     },
29137     
29138     address_component_from_google_geocode: function(address_components) 
29139     {
29140         var result = {};
29141         
29142         for (var i = 0; i < address_components.length; i++) {
29143             var component = address_components[i];
29144             if (component.types.indexOf("postal_code") >= 0) {
29145                 result.postalCode = component.short_name;
29146             } else if (component.types.indexOf("street_number") >= 0) {
29147                 result.streetNumber = component.short_name;
29148             } else if (component.types.indexOf("route") >= 0) {
29149                 result.streetName = component.short_name;
29150             } else if (component.types.indexOf("neighborhood") >= 0) {
29151                 result.city = component.short_name;
29152             } else if (component.types.indexOf("locality") >= 0) {
29153                 result.city = component.short_name;
29154             } else if (component.types.indexOf("sublocality") >= 0) {
29155                 result.district = component.short_name;
29156             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29157                 result.stateOrProvince = component.short_name;
29158             } else if (component.types.indexOf("country") >= 0) {
29159                 result.country = component.short_name;
29160             }
29161         }
29162         
29163         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29164         result.addressLine2 = "";
29165         return result;
29166     },
29167     
29168     setZoomLevel: function(zoom)
29169     {
29170         this.gMapContext.map.setZoom(zoom);
29171     },
29172     
29173     show: function()
29174     {
29175         if(!this.el){
29176             return;
29177         }
29178         
29179         this.el.show();
29180         
29181         this.resize();
29182         
29183         this.fireEvent('show', this);
29184     },
29185     
29186     hide: function()
29187     {
29188         if(!this.el){
29189             return;
29190         }
29191         
29192         this.el.hide();
29193         
29194         this.fireEvent('hide', this);
29195     }
29196     
29197 });
29198
29199 Roo.apply(Roo.bootstrap.LocationPicker, {
29200     
29201     OverlayView : function(map, options)
29202     {
29203         options = options || {};
29204         
29205         this.setMap(map);
29206     }
29207     
29208     
29209 });/**
29210  * @class Roo.bootstrap.Alert
29211  * @extends Roo.bootstrap.Component
29212  * Bootstrap Alert class - shows an alert area box
29213  * eg
29214  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29215   Enter a valid email address
29216 </div>
29217  * @licence LGPL
29218  * @cfg {String} title The title of alert
29219  * @cfg {String} html The content of alert
29220  * @cfg {String} weight (  success | info | warning | danger )
29221  * @cfg {String} faicon font-awesomeicon
29222  * 
29223  * @constructor
29224  * Create a new alert
29225  * @param {Object} config The config object
29226  */
29227
29228
29229 Roo.bootstrap.Alert = function(config){
29230     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29231     
29232 };
29233
29234 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29235     
29236     title: '',
29237     html: '',
29238     weight: false,
29239     faicon: false,
29240     
29241     getAutoCreate : function()
29242     {
29243         
29244         var cfg = {
29245             tag : 'div',
29246             cls : 'alert',
29247             cn : [
29248                 {
29249                     tag : 'i',
29250                     cls : 'roo-alert-icon'
29251                     
29252                 },
29253                 {
29254                     tag : 'b',
29255                     cls : 'roo-alert-title',
29256                     html : this.title
29257                 },
29258                 {
29259                     tag : 'span',
29260                     cls : 'roo-alert-text',
29261                     html : this.html
29262                 }
29263             ]
29264         };
29265         
29266         if(this.faicon){
29267             cfg.cn[0].cls += ' fa ' + this.faicon;
29268         }
29269         
29270         if(this.weight){
29271             cfg.cls += ' alert-' + this.weight;
29272         }
29273         
29274         return cfg;
29275     },
29276     
29277     initEvents: function() 
29278     {
29279         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29280     },
29281     
29282     setTitle : function(str)
29283     {
29284         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29285     },
29286     
29287     setText : function(str)
29288     {
29289         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29290     },
29291     
29292     setWeight : function(weight)
29293     {
29294         if(this.weight){
29295             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29296         }
29297         
29298         this.weight = weight;
29299         
29300         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29301     },
29302     
29303     setIcon : function(icon)
29304     {
29305         if(this.faicon){
29306             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29307         }
29308         
29309         this.faicon = icon;
29310         
29311         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29312     },
29313     
29314     hide: function() 
29315     {
29316         this.el.hide();   
29317     },
29318     
29319     show: function() 
29320     {  
29321         this.el.show();   
29322     }
29323     
29324 });
29325
29326  
29327 /*
29328 * Licence: LGPL
29329 */
29330
29331 /**
29332  * @class Roo.bootstrap.UploadCropbox
29333  * @extends Roo.bootstrap.Component
29334  * Bootstrap UploadCropbox class
29335  * @cfg {String} emptyText show when image has been loaded
29336  * @cfg {String} rotateNotify show when image too small to rotate
29337  * @cfg {Number} errorTimeout default 3000
29338  * @cfg {Number} minWidth default 300
29339  * @cfg {Number} minHeight default 300
29340  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29341  * @cfg {Boolean} isDocument (true|false) default false
29342  * @cfg {String} url action url
29343  * @cfg {String} paramName default 'imageUpload'
29344  * @cfg {String} method default POST
29345  * @cfg {Boolean} loadMask (true|false) default true
29346  * @cfg {Boolean} loadingText default 'Loading...'
29347  * 
29348  * @constructor
29349  * Create a new UploadCropbox
29350  * @param {Object} config The config object
29351  */
29352
29353 Roo.bootstrap.UploadCropbox = function(config){
29354     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29355     
29356     this.addEvents({
29357         /**
29358          * @event beforeselectfile
29359          * Fire before select file
29360          * @param {Roo.bootstrap.UploadCropbox} this
29361          */
29362         "beforeselectfile" : true,
29363         /**
29364          * @event initial
29365          * Fire after initEvent
29366          * @param {Roo.bootstrap.UploadCropbox} this
29367          */
29368         "initial" : true,
29369         /**
29370          * @event crop
29371          * Fire after initEvent
29372          * @param {Roo.bootstrap.UploadCropbox} this
29373          * @param {String} data
29374          */
29375         "crop" : true,
29376         /**
29377          * @event prepare
29378          * Fire when preparing the file data
29379          * @param {Roo.bootstrap.UploadCropbox} this
29380          * @param {Object} file
29381          */
29382         "prepare" : true,
29383         /**
29384          * @event exception
29385          * Fire when get exception
29386          * @param {Roo.bootstrap.UploadCropbox} this
29387          * @param {XMLHttpRequest} xhr
29388          */
29389         "exception" : true,
29390         /**
29391          * @event beforeloadcanvas
29392          * Fire before load the canvas
29393          * @param {Roo.bootstrap.UploadCropbox} this
29394          * @param {String} src
29395          */
29396         "beforeloadcanvas" : true,
29397         /**
29398          * @event trash
29399          * Fire when trash image
29400          * @param {Roo.bootstrap.UploadCropbox} this
29401          */
29402         "trash" : true,
29403         /**
29404          * @event download
29405          * Fire when download the image
29406          * @param {Roo.bootstrap.UploadCropbox} this
29407          */
29408         "download" : true,
29409         /**
29410          * @event footerbuttonclick
29411          * Fire when footerbuttonclick
29412          * @param {Roo.bootstrap.UploadCropbox} this
29413          * @param {String} type
29414          */
29415         "footerbuttonclick" : true,
29416         /**
29417          * @event resize
29418          * Fire when resize
29419          * @param {Roo.bootstrap.UploadCropbox} this
29420          */
29421         "resize" : true,
29422         /**
29423          * @event rotate
29424          * Fire when rotate the image
29425          * @param {Roo.bootstrap.UploadCropbox} this
29426          * @param {String} pos
29427          */
29428         "rotate" : true,
29429         /**
29430          * @event inspect
29431          * Fire when inspect the file
29432          * @param {Roo.bootstrap.UploadCropbox} this
29433          * @param {Object} file
29434          */
29435         "inspect" : true,
29436         /**
29437          * @event upload
29438          * Fire when xhr upload the file
29439          * @param {Roo.bootstrap.UploadCropbox} this
29440          * @param {Object} data
29441          */
29442         "upload" : true,
29443         /**
29444          * @event arrange
29445          * Fire when arrange the file data
29446          * @param {Roo.bootstrap.UploadCropbox} this
29447          * @param {Object} formData
29448          */
29449         "arrange" : true
29450     });
29451     
29452     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29453 };
29454
29455 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29456     
29457     emptyText : 'Click to upload image',
29458     rotateNotify : 'Image is too small to rotate',
29459     errorTimeout : 3000,
29460     scale : 0,
29461     baseScale : 1,
29462     rotate : 0,
29463     dragable : false,
29464     pinching : false,
29465     mouseX : 0,
29466     mouseY : 0,
29467     cropData : false,
29468     minWidth : 300,
29469     minHeight : 300,
29470     file : false,
29471     exif : {},
29472     baseRotate : 1,
29473     cropType : 'image/jpeg',
29474     buttons : false,
29475     canvasLoaded : false,
29476     isDocument : false,
29477     method : 'POST',
29478     paramName : 'imageUpload',
29479     loadMask : true,
29480     loadingText : 'Loading...',
29481     maskEl : false,
29482     
29483     getAutoCreate : function()
29484     {
29485         var cfg = {
29486             tag : 'div',
29487             cls : 'roo-upload-cropbox',
29488             cn : [
29489                 {
29490                     tag : 'input',
29491                     cls : 'roo-upload-cropbox-selector',
29492                     type : 'file'
29493                 },
29494                 {
29495                     tag : 'div',
29496                     cls : 'roo-upload-cropbox-body',
29497                     style : 'cursor:pointer',
29498                     cn : [
29499                         {
29500                             tag : 'div',
29501                             cls : 'roo-upload-cropbox-preview'
29502                         },
29503                         {
29504                             tag : 'div',
29505                             cls : 'roo-upload-cropbox-thumb'
29506                         },
29507                         {
29508                             tag : 'div',
29509                             cls : 'roo-upload-cropbox-empty-notify',
29510                             html : this.emptyText
29511                         },
29512                         {
29513                             tag : 'div',
29514                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29515                             html : this.rotateNotify
29516                         }
29517                     ]
29518                 },
29519                 {
29520                     tag : 'div',
29521                     cls : 'roo-upload-cropbox-footer',
29522                     cn : {
29523                         tag : 'div',
29524                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29525                         cn : []
29526                     }
29527                 }
29528             ]
29529         };
29530         
29531         return cfg;
29532     },
29533     
29534     onRender : function(ct, position)
29535     {
29536         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29537         
29538         if (this.buttons.length) {
29539             
29540             Roo.each(this.buttons, function(bb) {
29541                 
29542                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29543                 
29544                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29545                 
29546             }, this);
29547         }
29548         
29549         if(this.loadMask){
29550             this.maskEl = this.el;
29551         }
29552     },
29553     
29554     initEvents : function()
29555     {
29556         this.urlAPI = (window.createObjectURL && window) || 
29557                                 (window.URL && URL.revokeObjectURL && URL) || 
29558                                 (window.webkitURL && webkitURL);
29559                         
29560         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29561         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29562         
29563         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29564         this.selectorEl.hide();
29565         
29566         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29567         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29568         
29569         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29570         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29571         this.thumbEl.hide();
29572         
29573         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29574         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29575         
29576         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29577         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29578         this.errorEl.hide();
29579         
29580         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29581         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29582         this.footerEl.hide();
29583         
29584         this.setThumbBoxSize();
29585         
29586         this.bind();
29587         
29588         this.resize();
29589         
29590         this.fireEvent('initial', this);
29591     },
29592
29593     bind : function()
29594     {
29595         var _this = this;
29596         
29597         window.addEventListener("resize", function() { _this.resize(); } );
29598         
29599         this.bodyEl.on('click', this.beforeSelectFile, this);
29600         
29601         if(Roo.isTouch){
29602             this.bodyEl.on('touchstart', this.onTouchStart, this);
29603             this.bodyEl.on('touchmove', this.onTouchMove, this);
29604             this.bodyEl.on('touchend', this.onTouchEnd, this);
29605         }
29606         
29607         if(!Roo.isTouch){
29608             this.bodyEl.on('mousedown', this.onMouseDown, this);
29609             this.bodyEl.on('mousemove', this.onMouseMove, this);
29610             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29611             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29612             Roo.get(document).on('mouseup', this.onMouseUp, this);
29613         }
29614         
29615         this.selectorEl.on('change', this.onFileSelected, this);
29616     },
29617     
29618     reset : function()
29619     {    
29620         this.scale = 0;
29621         this.baseScale = 1;
29622         this.rotate = 0;
29623         this.baseRotate = 1;
29624         this.dragable = false;
29625         this.pinching = false;
29626         this.mouseX = 0;
29627         this.mouseY = 0;
29628         this.cropData = false;
29629         this.notifyEl.dom.innerHTML = this.emptyText;
29630         
29631         this.selectorEl.dom.value = '';
29632         
29633     },
29634     
29635     resize : function()
29636     {
29637         if(this.fireEvent('resize', this) != false){
29638             this.setThumbBoxPosition();
29639             this.setCanvasPosition();
29640         }
29641     },
29642     
29643     onFooterButtonClick : function(e, el, o, type)
29644     {
29645         switch (type) {
29646             case 'rotate-left' :
29647                 this.onRotateLeft(e);
29648                 break;
29649             case 'rotate-right' :
29650                 this.onRotateRight(e);
29651                 break;
29652             case 'picture' :
29653                 this.beforeSelectFile(e);
29654                 break;
29655             case 'trash' :
29656                 this.trash(e);
29657                 break;
29658             case 'crop' :
29659                 this.crop(e);
29660                 break;
29661             case 'download' :
29662                 this.download(e);
29663                 break;
29664             default :
29665                 break;
29666         }
29667         
29668         this.fireEvent('footerbuttonclick', this, type);
29669     },
29670     
29671     beforeSelectFile : function(e)
29672     {
29673         e.preventDefault();
29674         
29675         if(this.fireEvent('beforeselectfile', this) != false){
29676             this.selectorEl.dom.click();
29677         }
29678     },
29679     
29680     onFileSelected : function(e)
29681     {
29682         e.preventDefault();
29683         
29684         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29685             return;
29686         }
29687         
29688         var file = this.selectorEl.dom.files[0];
29689         
29690         if(this.fireEvent('inspect', this, file) != false){
29691             this.prepare(file);
29692         }
29693         
29694     },
29695     
29696     trash : function(e)
29697     {
29698         this.fireEvent('trash', this);
29699     },
29700     
29701     download : function(e)
29702     {
29703         this.fireEvent('download', this);
29704     },
29705     
29706     loadCanvas : function(src)
29707     {   
29708         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29709             
29710             this.reset();
29711             
29712             this.imageEl = document.createElement('img');
29713             
29714             var _this = this;
29715             
29716             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29717             
29718             this.imageEl.src = src;
29719         }
29720     },
29721     
29722     onLoadCanvas : function()
29723     {   
29724         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29725         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29726         
29727         this.bodyEl.un('click', this.beforeSelectFile, this);
29728         
29729         this.notifyEl.hide();
29730         this.thumbEl.show();
29731         this.footerEl.show();
29732         
29733         this.baseRotateLevel();
29734         
29735         if(this.isDocument){
29736             this.setThumbBoxSize();
29737         }
29738         
29739         this.setThumbBoxPosition();
29740         
29741         this.baseScaleLevel();
29742         
29743         this.draw();
29744         
29745         this.resize();
29746         
29747         this.canvasLoaded = true;
29748         
29749         if(this.loadMask){
29750             this.maskEl.unmask();
29751         }
29752         
29753     },
29754     
29755     setCanvasPosition : function()
29756     {   
29757         if(!this.canvasEl){
29758             return;
29759         }
29760         
29761         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29762         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29763         
29764         this.previewEl.setLeft(pw);
29765         this.previewEl.setTop(ph);
29766         
29767     },
29768     
29769     onMouseDown : function(e)
29770     {   
29771         e.stopEvent();
29772         
29773         this.dragable = true;
29774         this.pinching = false;
29775         
29776         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29777             this.dragable = false;
29778             return;
29779         }
29780         
29781         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29782         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29783         
29784     },
29785     
29786     onMouseMove : function(e)
29787     {   
29788         e.stopEvent();
29789         
29790         if(!this.canvasLoaded){
29791             return;
29792         }
29793         
29794         if (!this.dragable){
29795             return;
29796         }
29797         
29798         var minX = Math.ceil(this.thumbEl.getLeft(true));
29799         var minY = Math.ceil(this.thumbEl.getTop(true));
29800         
29801         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29802         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29803         
29804         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29805         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29806         
29807         x = x - this.mouseX;
29808         y = y - this.mouseY;
29809         
29810         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29811         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29812         
29813         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29814         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29815         
29816         this.previewEl.setLeft(bgX);
29817         this.previewEl.setTop(bgY);
29818         
29819         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29820         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29821     },
29822     
29823     onMouseUp : function(e)
29824     {   
29825         e.stopEvent();
29826         
29827         this.dragable = false;
29828     },
29829     
29830     onMouseWheel : function(e)
29831     {   
29832         e.stopEvent();
29833         
29834         this.startScale = this.scale;
29835         
29836         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29837         
29838         if(!this.zoomable()){
29839             this.scale = this.startScale;
29840             return;
29841         }
29842         
29843         this.draw();
29844         
29845         return;
29846     },
29847     
29848     zoomable : function()
29849     {
29850         var minScale = this.thumbEl.getWidth() / this.minWidth;
29851         
29852         if(this.minWidth < this.minHeight){
29853             minScale = this.thumbEl.getHeight() / this.minHeight;
29854         }
29855         
29856         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29857         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29858         
29859         if(
29860                 this.isDocument &&
29861                 (this.rotate == 0 || this.rotate == 180) && 
29862                 (
29863                     width > this.imageEl.OriginWidth || 
29864                     height > this.imageEl.OriginHeight ||
29865                     (width < this.minWidth && height < this.minHeight)
29866                 )
29867         ){
29868             return false;
29869         }
29870         
29871         if(
29872                 this.isDocument &&
29873                 (this.rotate == 90 || this.rotate == 270) && 
29874                 (
29875                     width > this.imageEl.OriginWidth || 
29876                     height > this.imageEl.OriginHeight ||
29877                     (width < this.minHeight && height < this.minWidth)
29878                 )
29879         ){
29880             return false;
29881         }
29882         
29883         if(
29884                 !this.isDocument &&
29885                 (this.rotate == 0 || this.rotate == 180) && 
29886                 (
29887                     width < this.minWidth || 
29888                     width > this.imageEl.OriginWidth || 
29889                     height < this.minHeight || 
29890                     height > this.imageEl.OriginHeight
29891                 )
29892         ){
29893             return false;
29894         }
29895         
29896         if(
29897                 !this.isDocument &&
29898                 (this.rotate == 90 || this.rotate == 270) && 
29899                 (
29900                     width < this.minHeight || 
29901                     width > this.imageEl.OriginWidth || 
29902                     height < this.minWidth || 
29903                     height > this.imageEl.OriginHeight
29904                 )
29905         ){
29906             return false;
29907         }
29908         
29909         return true;
29910         
29911     },
29912     
29913     onRotateLeft : function(e)
29914     {   
29915         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29916             
29917             var minScale = this.thumbEl.getWidth() / this.minWidth;
29918             
29919             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29920             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29921             
29922             this.startScale = this.scale;
29923             
29924             while (this.getScaleLevel() < minScale){
29925             
29926                 this.scale = this.scale + 1;
29927                 
29928                 if(!this.zoomable()){
29929                     break;
29930                 }
29931                 
29932                 if(
29933                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29934                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29935                 ){
29936                     continue;
29937                 }
29938                 
29939                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29940
29941                 this.draw();
29942                 
29943                 return;
29944             }
29945             
29946             this.scale = this.startScale;
29947             
29948             this.onRotateFail();
29949             
29950             return false;
29951         }
29952         
29953         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29954
29955         if(this.isDocument){
29956             this.setThumbBoxSize();
29957             this.setThumbBoxPosition();
29958             this.setCanvasPosition();
29959         }
29960         
29961         this.draw();
29962         
29963         this.fireEvent('rotate', this, 'left');
29964         
29965     },
29966     
29967     onRotateRight : function(e)
29968     {
29969         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29970             
29971             var minScale = this.thumbEl.getWidth() / this.minWidth;
29972         
29973             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29974             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29975             
29976             this.startScale = this.scale;
29977             
29978             while (this.getScaleLevel() < minScale){
29979             
29980                 this.scale = this.scale + 1;
29981                 
29982                 if(!this.zoomable()){
29983                     break;
29984                 }
29985                 
29986                 if(
29987                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29988                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29989                 ){
29990                     continue;
29991                 }
29992                 
29993                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29994
29995                 this.draw();
29996                 
29997                 return;
29998             }
29999             
30000             this.scale = this.startScale;
30001             
30002             this.onRotateFail();
30003             
30004             return false;
30005         }
30006         
30007         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30008
30009         if(this.isDocument){
30010             this.setThumbBoxSize();
30011             this.setThumbBoxPosition();
30012             this.setCanvasPosition();
30013         }
30014         
30015         this.draw();
30016         
30017         this.fireEvent('rotate', this, 'right');
30018     },
30019     
30020     onRotateFail : function()
30021     {
30022         this.errorEl.show(true);
30023         
30024         var _this = this;
30025         
30026         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30027     },
30028     
30029     draw : function()
30030     {
30031         this.previewEl.dom.innerHTML = '';
30032         
30033         var canvasEl = document.createElement("canvas");
30034         
30035         var contextEl = canvasEl.getContext("2d");
30036         
30037         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30038         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30039         var center = this.imageEl.OriginWidth / 2;
30040         
30041         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30042             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30043             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30044             center = this.imageEl.OriginHeight / 2;
30045         }
30046         
30047         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30048         
30049         contextEl.translate(center, center);
30050         contextEl.rotate(this.rotate * Math.PI / 180);
30051
30052         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30053         
30054         this.canvasEl = document.createElement("canvas");
30055         
30056         this.contextEl = this.canvasEl.getContext("2d");
30057         
30058         switch (this.rotate) {
30059             case 0 :
30060                 
30061                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30062                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30063                 
30064                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30065                 
30066                 break;
30067             case 90 : 
30068                 
30069                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30070                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30071                 
30072                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30073                     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);
30074                     break;
30075                 }
30076                 
30077                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30078                 
30079                 break;
30080             case 180 :
30081                 
30082                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30083                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30084                 
30085                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30086                     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);
30087                     break;
30088                 }
30089                 
30090                 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);
30091                 
30092                 break;
30093             case 270 :
30094                 
30095                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30096                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30097         
30098                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30099                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30100                     break;
30101                 }
30102                 
30103                 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);
30104                 
30105                 break;
30106             default : 
30107                 break;
30108         }
30109         
30110         this.previewEl.appendChild(this.canvasEl);
30111         
30112         this.setCanvasPosition();
30113     },
30114     
30115     crop : function()
30116     {
30117         if(!this.canvasLoaded){
30118             return;
30119         }
30120         
30121         var imageCanvas = document.createElement("canvas");
30122         
30123         var imageContext = imageCanvas.getContext("2d");
30124         
30125         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30126         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30127         
30128         var center = imageCanvas.width / 2;
30129         
30130         imageContext.translate(center, center);
30131         
30132         imageContext.rotate(this.rotate * Math.PI / 180);
30133         
30134         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30135         
30136         var canvas = document.createElement("canvas");
30137         
30138         var context = canvas.getContext("2d");
30139                 
30140         canvas.width = this.minWidth;
30141         canvas.height = this.minHeight;
30142
30143         switch (this.rotate) {
30144             case 0 :
30145                 
30146                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30147                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30148                 
30149                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30150                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30151                 
30152                 var targetWidth = this.minWidth - 2 * x;
30153                 var targetHeight = this.minHeight - 2 * y;
30154                 
30155                 var scale = 1;
30156                 
30157                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30158                     scale = targetWidth / width;
30159                 }
30160                 
30161                 if(x > 0 && y == 0){
30162                     scale = targetHeight / height;
30163                 }
30164                 
30165                 if(x > 0 && y > 0){
30166                     scale = targetWidth / width;
30167                     
30168                     if(width < height){
30169                         scale = targetHeight / height;
30170                     }
30171                 }
30172                 
30173                 context.scale(scale, scale);
30174                 
30175                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30176                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30177
30178                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30179                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30180
30181                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30182                 
30183                 break;
30184             case 90 : 
30185                 
30186                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30187                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30188                 
30189                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30190                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30191                 
30192                 var targetWidth = this.minWidth - 2 * x;
30193                 var targetHeight = this.minHeight - 2 * y;
30194                 
30195                 var scale = 1;
30196                 
30197                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30198                     scale = targetWidth / width;
30199                 }
30200                 
30201                 if(x > 0 && y == 0){
30202                     scale = targetHeight / height;
30203                 }
30204                 
30205                 if(x > 0 && y > 0){
30206                     scale = targetWidth / width;
30207                     
30208                     if(width < height){
30209                         scale = targetHeight / height;
30210                     }
30211                 }
30212                 
30213                 context.scale(scale, scale);
30214                 
30215                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30216                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30217
30218                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30219                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30220                 
30221                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30222                 
30223                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30224                 
30225                 break;
30226             case 180 :
30227                 
30228                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30229                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30230                 
30231                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30232                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30233                 
30234                 var targetWidth = this.minWidth - 2 * x;
30235                 var targetHeight = this.minHeight - 2 * y;
30236                 
30237                 var scale = 1;
30238                 
30239                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30240                     scale = targetWidth / width;
30241                 }
30242                 
30243                 if(x > 0 && y == 0){
30244                     scale = targetHeight / height;
30245                 }
30246                 
30247                 if(x > 0 && y > 0){
30248                     scale = targetWidth / width;
30249                     
30250                     if(width < height){
30251                         scale = targetHeight / height;
30252                     }
30253                 }
30254                 
30255                 context.scale(scale, scale);
30256                 
30257                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30258                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30259
30260                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30261                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30262
30263                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30264                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30265                 
30266                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30267                 
30268                 break;
30269             case 270 :
30270                 
30271                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30272                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30273                 
30274                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30275                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30276                 
30277                 var targetWidth = this.minWidth - 2 * x;
30278                 var targetHeight = this.minHeight - 2 * y;
30279                 
30280                 var scale = 1;
30281                 
30282                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30283                     scale = targetWidth / width;
30284                 }
30285                 
30286                 if(x > 0 && y == 0){
30287                     scale = targetHeight / height;
30288                 }
30289                 
30290                 if(x > 0 && y > 0){
30291                     scale = targetWidth / width;
30292                     
30293                     if(width < height){
30294                         scale = targetHeight / height;
30295                     }
30296                 }
30297                 
30298                 context.scale(scale, scale);
30299                 
30300                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30301                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30302
30303                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30304                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30305                 
30306                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30307                 
30308                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30309                 
30310                 break;
30311             default : 
30312                 break;
30313         }
30314         
30315         this.cropData = canvas.toDataURL(this.cropType);
30316         
30317         if(this.fireEvent('crop', this, this.cropData) !== false){
30318             this.process(this.file, this.cropData);
30319         }
30320         
30321         return;
30322         
30323     },
30324     
30325     setThumbBoxSize : function()
30326     {
30327         var width, height;
30328         
30329         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30330             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30331             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30332             
30333             this.minWidth = width;
30334             this.minHeight = height;
30335             
30336             if(this.rotate == 90 || this.rotate == 270){
30337                 this.minWidth = height;
30338                 this.minHeight = width;
30339             }
30340         }
30341         
30342         height = 300;
30343         width = Math.ceil(this.minWidth * height / this.minHeight);
30344         
30345         if(this.minWidth > this.minHeight){
30346             width = 300;
30347             height = Math.ceil(this.minHeight * width / this.minWidth);
30348         }
30349         
30350         this.thumbEl.setStyle({
30351             width : width + 'px',
30352             height : height + 'px'
30353         });
30354
30355         return;
30356             
30357     },
30358     
30359     setThumbBoxPosition : function()
30360     {
30361         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30362         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30363         
30364         this.thumbEl.setLeft(x);
30365         this.thumbEl.setTop(y);
30366         
30367     },
30368     
30369     baseRotateLevel : function()
30370     {
30371         this.baseRotate = 1;
30372         
30373         if(
30374                 typeof(this.exif) != 'undefined' &&
30375                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30376                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30377         ){
30378             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30379         }
30380         
30381         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30382         
30383     },
30384     
30385     baseScaleLevel : function()
30386     {
30387         var width, height;
30388         
30389         if(this.isDocument){
30390             
30391             if(this.baseRotate == 6 || this.baseRotate == 8){
30392             
30393                 height = this.thumbEl.getHeight();
30394                 this.baseScale = height / this.imageEl.OriginWidth;
30395
30396                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30397                     width = this.thumbEl.getWidth();
30398                     this.baseScale = width / this.imageEl.OriginHeight;
30399                 }
30400
30401                 return;
30402             }
30403
30404             height = this.thumbEl.getHeight();
30405             this.baseScale = height / this.imageEl.OriginHeight;
30406
30407             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30408                 width = this.thumbEl.getWidth();
30409                 this.baseScale = width / this.imageEl.OriginWidth;
30410             }
30411
30412             return;
30413         }
30414         
30415         if(this.baseRotate == 6 || this.baseRotate == 8){
30416             
30417             width = this.thumbEl.getHeight();
30418             this.baseScale = width / this.imageEl.OriginHeight;
30419             
30420             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30421                 height = this.thumbEl.getWidth();
30422                 this.baseScale = height / this.imageEl.OriginHeight;
30423             }
30424             
30425             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30426                 height = this.thumbEl.getWidth();
30427                 this.baseScale = height / this.imageEl.OriginHeight;
30428                 
30429                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30430                     width = this.thumbEl.getHeight();
30431                     this.baseScale = width / this.imageEl.OriginWidth;
30432                 }
30433             }
30434             
30435             return;
30436         }
30437         
30438         width = this.thumbEl.getWidth();
30439         this.baseScale = width / this.imageEl.OriginWidth;
30440         
30441         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30442             height = this.thumbEl.getHeight();
30443             this.baseScale = height / this.imageEl.OriginHeight;
30444         }
30445         
30446         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30447             
30448             height = this.thumbEl.getHeight();
30449             this.baseScale = height / this.imageEl.OriginHeight;
30450             
30451             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30452                 width = this.thumbEl.getWidth();
30453                 this.baseScale = width / this.imageEl.OriginWidth;
30454             }
30455             
30456         }
30457         
30458         return;
30459     },
30460     
30461     getScaleLevel : function()
30462     {
30463         return this.baseScale * Math.pow(1.1, this.scale);
30464     },
30465     
30466     onTouchStart : function(e)
30467     {
30468         if(!this.canvasLoaded){
30469             this.beforeSelectFile(e);
30470             return;
30471         }
30472         
30473         var touches = e.browserEvent.touches;
30474         
30475         if(!touches){
30476             return;
30477         }
30478         
30479         if(touches.length == 1){
30480             this.onMouseDown(e);
30481             return;
30482         }
30483         
30484         if(touches.length != 2){
30485             return;
30486         }
30487         
30488         var coords = [];
30489         
30490         for(var i = 0, finger; finger = touches[i]; i++){
30491             coords.push(finger.pageX, finger.pageY);
30492         }
30493         
30494         var x = Math.pow(coords[0] - coords[2], 2);
30495         var y = Math.pow(coords[1] - coords[3], 2);
30496         
30497         this.startDistance = Math.sqrt(x + y);
30498         
30499         this.startScale = this.scale;
30500         
30501         this.pinching = true;
30502         this.dragable = false;
30503         
30504     },
30505     
30506     onTouchMove : function(e)
30507     {
30508         if(!this.pinching && !this.dragable){
30509             return;
30510         }
30511         
30512         var touches = e.browserEvent.touches;
30513         
30514         if(!touches){
30515             return;
30516         }
30517         
30518         if(this.dragable){
30519             this.onMouseMove(e);
30520             return;
30521         }
30522         
30523         var coords = [];
30524         
30525         for(var i = 0, finger; finger = touches[i]; i++){
30526             coords.push(finger.pageX, finger.pageY);
30527         }
30528         
30529         var x = Math.pow(coords[0] - coords[2], 2);
30530         var y = Math.pow(coords[1] - coords[3], 2);
30531         
30532         this.endDistance = Math.sqrt(x + y);
30533         
30534         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30535         
30536         if(!this.zoomable()){
30537             this.scale = this.startScale;
30538             return;
30539         }
30540         
30541         this.draw();
30542         
30543     },
30544     
30545     onTouchEnd : function(e)
30546     {
30547         this.pinching = false;
30548         this.dragable = false;
30549         
30550     },
30551     
30552     process : function(file, crop)
30553     {
30554         if(this.loadMask){
30555             this.maskEl.mask(this.loadingText);
30556         }
30557         
30558         this.xhr = new XMLHttpRequest();
30559         
30560         file.xhr = this.xhr;
30561
30562         this.xhr.open(this.method, this.url, true);
30563         
30564         var headers = {
30565             "Accept": "application/json",
30566             "Cache-Control": "no-cache",
30567             "X-Requested-With": "XMLHttpRequest"
30568         };
30569         
30570         for (var headerName in headers) {
30571             var headerValue = headers[headerName];
30572             if (headerValue) {
30573                 this.xhr.setRequestHeader(headerName, headerValue);
30574             }
30575         }
30576         
30577         var _this = this;
30578         
30579         this.xhr.onload = function()
30580         {
30581             _this.xhrOnLoad(_this.xhr);
30582         }
30583         
30584         this.xhr.onerror = function()
30585         {
30586             _this.xhrOnError(_this.xhr);
30587         }
30588         
30589         var formData = new FormData();
30590
30591         formData.append('returnHTML', 'NO');
30592         
30593         if(crop){
30594             formData.append('crop', crop);
30595         }
30596         
30597         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30598             formData.append(this.paramName, file, file.name);
30599         }
30600         
30601         if(typeof(file.filename) != 'undefined'){
30602             formData.append('filename', file.filename);
30603         }
30604         
30605         if(typeof(file.mimetype) != 'undefined'){
30606             formData.append('mimetype', file.mimetype);
30607         }
30608         
30609         if(this.fireEvent('arrange', this, formData) != false){
30610             this.xhr.send(formData);
30611         };
30612     },
30613     
30614     xhrOnLoad : function(xhr)
30615     {
30616         if(this.loadMask){
30617             this.maskEl.unmask();
30618         }
30619         
30620         if (xhr.readyState !== 4) {
30621             this.fireEvent('exception', this, xhr);
30622             return;
30623         }
30624
30625         var response = Roo.decode(xhr.responseText);
30626         
30627         if(!response.success){
30628             this.fireEvent('exception', this, xhr);
30629             return;
30630         }
30631         
30632         var response = Roo.decode(xhr.responseText);
30633         
30634         this.fireEvent('upload', this, response);
30635         
30636     },
30637     
30638     xhrOnError : function()
30639     {
30640         if(this.loadMask){
30641             this.maskEl.unmask();
30642         }
30643         
30644         Roo.log('xhr on error');
30645         
30646         var response = Roo.decode(xhr.responseText);
30647           
30648         Roo.log(response);
30649         
30650     },
30651     
30652     prepare : function(file)
30653     {   
30654         if(this.loadMask){
30655             this.maskEl.mask(this.loadingText);
30656         }
30657         
30658         this.file = false;
30659         this.exif = {};
30660         
30661         if(typeof(file) === 'string'){
30662             this.loadCanvas(file);
30663             return;
30664         }
30665         
30666         if(!file || !this.urlAPI){
30667             return;
30668         }
30669         
30670         this.file = file;
30671         this.cropType = file.type;
30672         
30673         var _this = this;
30674         
30675         if(this.fireEvent('prepare', this, this.file) != false){
30676             
30677             var reader = new FileReader();
30678             
30679             reader.onload = function (e) {
30680                 if (e.target.error) {
30681                     Roo.log(e.target.error);
30682                     return;
30683                 }
30684                 
30685                 var buffer = e.target.result,
30686                     dataView = new DataView(buffer),
30687                     offset = 2,
30688                     maxOffset = dataView.byteLength - 4,
30689                     markerBytes,
30690                     markerLength;
30691                 
30692                 if (dataView.getUint16(0) === 0xffd8) {
30693                     while (offset < maxOffset) {
30694                         markerBytes = dataView.getUint16(offset);
30695                         
30696                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30697                             markerLength = dataView.getUint16(offset + 2) + 2;
30698                             if (offset + markerLength > dataView.byteLength) {
30699                                 Roo.log('Invalid meta data: Invalid segment size.');
30700                                 break;
30701                             }
30702                             
30703                             if(markerBytes == 0xffe1){
30704                                 _this.parseExifData(
30705                                     dataView,
30706                                     offset,
30707                                     markerLength
30708                                 );
30709                             }
30710                             
30711                             offset += markerLength;
30712                             
30713                             continue;
30714                         }
30715                         
30716                         break;
30717                     }
30718                     
30719                 }
30720                 
30721                 var url = _this.urlAPI.createObjectURL(_this.file);
30722                 
30723                 _this.loadCanvas(url);
30724                 
30725                 return;
30726             }
30727             
30728             reader.readAsArrayBuffer(this.file);
30729             
30730         }
30731         
30732     },
30733     
30734     parseExifData : function(dataView, offset, length)
30735     {
30736         var tiffOffset = offset + 10,
30737             littleEndian,
30738             dirOffset;
30739     
30740         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30741             // No Exif data, might be XMP data instead
30742             return;
30743         }
30744         
30745         // Check for the ASCII code for "Exif" (0x45786966):
30746         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30747             // No Exif data, might be XMP data instead
30748             return;
30749         }
30750         if (tiffOffset + 8 > dataView.byteLength) {
30751             Roo.log('Invalid Exif data: Invalid segment size.');
30752             return;
30753         }
30754         // Check for the two null bytes:
30755         if (dataView.getUint16(offset + 8) !== 0x0000) {
30756             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30757             return;
30758         }
30759         // Check the byte alignment:
30760         switch (dataView.getUint16(tiffOffset)) {
30761         case 0x4949:
30762             littleEndian = true;
30763             break;
30764         case 0x4D4D:
30765             littleEndian = false;
30766             break;
30767         default:
30768             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30769             return;
30770         }
30771         // Check for the TIFF tag marker (0x002A):
30772         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30773             Roo.log('Invalid Exif data: Missing TIFF marker.');
30774             return;
30775         }
30776         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30777         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30778         
30779         this.parseExifTags(
30780             dataView,
30781             tiffOffset,
30782             tiffOffset + dirOffset,
30783             littleEndian
30784         );
30785     },
30786     
30787     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30788     {
30789         var tagsNumber,
30790             dirEndOffset,
30791             i;
30792         if (dirOffset + 6 > dataView.byteLength) {
30793             Roo.log('Invalid Exif data: Invalid directory offset.');
30794             return;
30795         }
30796         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30797         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30798         if (dirEndOffset + 4 > dataView.byteLength) {
30799             Roo.log('Invalid Exif data: Invalid directory size.');
30800             return;
30801         }
30802         for (i = 0; i < tagsNumber; i += 1) {
30803             this.parseExifTag(
30804                 dataView,
30805                 tiffOffset,
30806                 dirOffset + 2 + 12 * i, // tag offset
30807                 littleEndian
30808             );
30809         }
30810         // Return the offset to the next directory:
30811         return dataView.getUint32(dirEndOffset, littleEndian);
30812     },
30813     
30814     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30815     {
30816         var tag = dataView.getUint16(offset, littleEndian);
30817         
30818         this.exif[tag] = this.getExifValue(
30819             dataView,
30820             tiffOffset,
30821             offset,
30822             dataView.getUint16(offset + 2, littleEndian), // tag type
30823             dataView.getUint32(offset + 4, littleEndian), // tag length
30824             littleEndian
30825         );
30826     },
30827     
30828     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30829     {
30830         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30831             tagSize,
30832             dataOffset,
30833             values,
30834             i,
30835             str,
30836             c;
30837     
30838         if (!tagType) {
30839             Roo.log('Invalid Exif data: Invalid tag type.');
30840             return;
30841         }
30842         
30843         tagSize = tagType.size * length;
30844         // Determine if the value is contained in the dataOffset bytes,
30845         // or if the value at the dataOffset is a pointer to the actual data:
30846         dataOffset = tagSize > 4 ?
30847                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30848         if (dataOffset + tagSize > dataView.byteLength) {
30849             Roo.log('Invalid Exif data: Invalid data offset.');
30850             return;
30851         }
30852         if (length === 1) {
30853             return tagType.getValue(dataView, dataOffset, littleEndian);
30854         }
30855         values = [];
30856         for (i = 0; i < length; i += 1) {
30857             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30858         }
30859         
30860         if (tagType.ascii) {
30861             str = '';
30862             // Concatenate the chars:
30863             for (i = 0; i < values.length; i += 1) {
30864                 c = values[i];
30865                 // Ignore the terminating NULL byte(s):
30866                 if (c === '\u0000') {
30867                     break;
30868                 }
30869                 str += c;
30870             }
30871             return str;
30872         }
30873         return values;
30874     }
30875     
30876 });
30877
30878 Roo.apply(Roo.bootstrap.UploadCropbox, {
30879     tags : {
30880         'Orientation': 0x0112
30881     },
30882     
30883     Orientation: {
30884             1: 0, //'top-left',
30885 //            2: 'top-right',
30886             3: 180, //'bottom-right',
30887 //            4: 'bottom-left',
30888 //            5: 'left-top',
30889             6: 90, //'right-top',
30890 //            7: 'right-bottom',
30891             8: 270 //'left-bottom'
30892     },
30893     
30894     exifTagTypes : {
30895         // byte, 8-bit unsigned int:
30896         1: {
30897             getValue: function (dataView, dataOffset) {
30898                 return dataView.getUint8(dataOffset);
30899             },
30900             size: 1
30901         },
30902         // ascii, 8-bit byte:
30903         2: {
30904             getValue: function (dataView, dataOffset) {
30905                 return String.fromCharCode(dataView.getUint8(dataOffset));
30906             },
30907             size: 1,
30908             ascii: true
30909         },
30910         // short, 16 bit int:
30911         3: {
30912             getValue: function (dataView, dataOffset, littleEndian) {
30913                 return dataView.getUint16(dataOffset, littleEndian);
30914             },
30915             size: 2
30916         },
30917         // long, 32 bit int:
30918         4: {
30919             getValue: function (dataView, dataOffset, littleEndian) {
30920                 return dataView.getUint32(dataOffset, littleEndian);
30921             },
30922             size: 4
30923         },
30924         // rational = two long values, first is numerator, second is denominator:
30925         5: {
30926             getValue: function (dataView, dataOffset, littleEndian) {
30927                 return dataView.getUint32(dataOffset, littleEndian) /
30928                     dataView.getUint32(dataOffset + 4, littleEndian);
30929             },
30930             size: 8
30931         },
30932         // slong, 32 bit signed int:
30933         9: {
30934             getValue: function (dataView, dataOffset, littleEndian) {
30935                 return dataView.getInt32(dataOffset, littleEndian);
30936             },
30937             size: 4
30938         },
30939         // srational, two slongs, first is numerator, second is denominator:
30940         10: {
30941             getValue: function (dataView, dataOffset, littleEndian) {
30942                 return dataView.getInt32(dataOffset, littleEndian) /
30943                     dataView.getInt32(dataOffset + 4, littleEndian);
30944             },
30945             size: 8
30946         }
30947     },
30948     
30949     footer : {
30950         STANDARD : [
30951             {
30952                 tag : 'div',
30953                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30954                 action : 'rotate-left',
30955                 cn : [
30956                     {
30957                         tag : 'button',
30958                         cls : 'btn btn-default',
30959                         html : '<i class="fa fa-undo"></i>'
30960                     }
30961                 ]
30962             },
30963             {
30964                 tag : 'div',
30965                 cls : 'btn-group roo-upload-cropbox-picture',
30966                 action : 'picture',
30967                 cn : [
30968                     {
30969                         tag : 'button',
30970                         cls : 'btn btn-default',
30971                         html : '<i class="fa fa-picture-o"></i>'
30972                     }
30973                 ]
30974             },
30975             {
30976                 tag : 'div',
30977                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30978                 action : 'rotate-right',
30979                 cn : [
30980                     {
30981                         tag : 'button',
30982                         cls : 'btn btn-default',
30983                         html : '<i class="fa fa-repeat"></i>'
30984                     }
30985                 ]
30986             }
30987         ],
30988         DOCUMENT : [
30989             {
30990                 tag : 'div',
30991                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30992                 action : 'rotate-left',
30993                 cn : [
30994                     {
30995                         tag : 'button',
30996                         cls : 'btn btn-default',
30997                         html : '<i class="fa fa-undo"></i>'
30998                     }
30999                 ]
31000             },
31001             {
31002                 tag : 'div',
31003                 cls : 'btn-group roo-upload-cropbox-download',
31004                 action : 'download',
31005                 cn : [
31006                     {
31007                         tag : 'button',
31008                         cls : 'btn btn-default',
31009                         html : '<i class="fa fa-download"></i>'
31010                     }
31011                 ]
31012             },
31013             {
31014                 tag : 'div',
31015                 cls : 'btn-group roo-upload-cropbox-crop',
31016                 action : 'crop',
31017                 cn : [
31018                     {
31019                         tag : 'button',
31020                         cls : 'btn btn-default',
31021                         html : '<i class="fa fa-crop"></i>'
31022                     }
31023                 ]
31024             },
31025             {
31026                 tag : 'div',
31027                 cls : 'btn-group roo-upload-cropbox-trash',
31028                 action : 'trash',
31029                 cn : [
31030                     {
31031                         tag : 'button',
31032                         cls : 'btn btn-default',
31033                         html : '<i class="fa fa-trash"></i>'
31034                     }
31035                 ]
31036             },
31037             {
31038                 tag : 'div',
31039                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31040                 action : 'rotate-right',
31041                 cn : [
31042                     {
31043                         tag : 'button',
31044                         cls : 'btn btn-default',
31045                         html : '<i class="fa fa-repeat"></i>'
31046                     }
31047                 ]
31048             }
31049         ],
31050         ROTATOR : [
31051             {
31052                 tag : 'div',
31053                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31054                 action : 'rotate-left',
31055                 cn : [
31056                     {
31057                         tag : 'button',
31058                         cls : 'btn btn-default',
31059                         html : '<i class="fa fa-undo"></i>'
31060                     }
31061                 ]
31062             },
31063             {
31064                 tag : 'div',
31065                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31066                 action : 'rotate-right',
31067                 cn : [
31068                     {
31069                         tag : 'button',
31070                         cls : 'btn btn-default',
31071                         html : '<i class="fa fa-repeat"></i>'
31072                     }
31073                 ]
31074             }
31075         ]
31076     }
31077 });
31078
31079 /*
31080 * Licence: LGPL
31081 */
31082
31083 /**
31084  * @class Roo.bootstrap.DocumentManager
31085  * @extends Roo.bootstrap.Component
31086  * Bootstrap DocumentManager class
31087  * @cfg {String} paramName default 'imageUpload'
31088  * @cfg {String} toolTipName default 'filename'
31089  * @cfg {String} method default POST
31090  * @cfg {String} url action url
31091  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31092  * @cfg {Boolean} multiple multiple upload default true
31093  * @cfg {Number} thumbSize default 300
31094  * @cfg {String} fieldLabel
31095  * @cfg {Number} labelWidth default 4
31096  * @cfg {String} labelAlign (left|top) default left
31097  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31098 * @cfg {Number} labellg set the width of label (1-12)
31099  * @cfg {Number} labelmd set the width of label (1-12)
31100  * @cfg {Number} labelsm set the width of label (1-12)
31101  * @cfg {Number} labelxs set the width of label (1-12)
31102  * 
31103  * @constructor
31104  * Create a new DocumentManager
31105  * @param {Object} config The config object
31106  */
31107
31108 Roo.bootstrap.DocumentManager = function(config){
31109     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31110     
31111     this.files = [];
31112     this.delegates = [];
31113     
31114     this.addEvents({
31115         /**
31116          * @event initial
31117          * Fire when initial the DocumentManager
31118          * @param {Roo.bootstrap.DocumentManager} this
31119          */
31120         "initial" : true,
31121         /**
31122          * @event inspect
31123          * inspect selected file
31124          * @param {Roo.bootstrap.DocumentManager} this
31125          * @param {File} file
31126          */
31127         "inspect" : true,
31128         /**
31129          * @event exception
31130          * Fire when xhr load exception
31131          * @param {Roo.bootstrap.DocumentManager} this
31132          * @param {XMLHttpRequest} xhr
31133          */
31134         "exception" : true,
31135         /**
31136          * @event afterupload
31137          * Fire when xhr load exception
31138          * @param {Roo.bootstrap.DocumentManager} this
31139          * @param {XMLHttpRequest} xhr
31140          */
31141         "afterupload" : true,
31142         /**
31143          * @event prepare
31144          * prepare the form data
31145          * @param {Roo.bootstrap.DocumentManager} this
31146          * @param {Object} formData
31147          */
31148         "prepare" : true,
31149         /**
31150          * @event remove
31151          * Fire when remove the file
31152          * @param {Roo.bootstrap.DocumentManager} this
31153          * @param {Object} file
31154          */
31155         "remove" : true,
31156         /**
31157          * @event refresh
31158          * Fire after refresh the file
31159          * @param {Roo.bootstrap.DocumentManager} this
31160          */
31161         "refresh" : true,
31162         /**
31163          * @event click
31164          * Fire after click the image
31165          * @param {Roo.bootstrap.DocumentManager} this
31166          * @param {Object} file
31167          */
31168         "click" : true,
31169         /**
31170          * @event edit
31171          * Fire when upload a image and editable set to true
31172          * @param {Roo.bootstrap.DocumentManager} this
31173          * @param {Object} file
31174          */
31175         "edit" : true,
31176         /**
31177          * @event beforeselectfile
31178          * Fire before select file
31179          * @param {Roo.bootstrap.DocumentManager} this
31180          */
31181         "beforeselectfile" : true,
31182         /**
31183          * @event process
31184          * Fire before process file
31185          * @param {Roo.bootstrap.DocumentManager} this
31186          * @param {Object} file
31187          */
31188         "process" : true,
31189         /**
31190          * @event previewrendered
31191          * Fire when preview rendered
31192          * @param {Roo.bootstrap.DocumentManager} this
31193          * @param {Object} file
31194          */
31195         "previewrendered" : true,
31196         /**
31197          */
31198         "previewResize" : true
31199         
31200     });
31201 };
31202
31203 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31204     
31205     boxes : 0,
31206     inputName : '',
31207     thumbSize : 300,
31208     multiple : true,
31209     files : false,
31210     method : 'POST',
31211     url : '',
31212     paramName : 'imageUpload',
31213     toolTipName : 'filename',
31214     fieldLabel : '',
31215     labelWidth : 4,
31216     labelAlign : 'left',
31217     editable : true,
31218     delegates : false,
31219     xhr : false, 
31220     
31221     labellg : 0,
31222     labelmd : 0,
31223     labelsm : 0,
31224     labelxs : 0,
31225     
31226     getAutoCreate : function()
31227     {   
31228         var managerWidget = {
31229             tag : 'div',
31230             cls : 'roo-document-manager',
31231             cn : [
31232                 {
31233                     tag : 'input',
31234                     cls : 'roo-document-manager-selector',
31235                     type : 'file'
31236                 },
31237                 {
31238                     tag : 'div',
31239                     cls : 'roo-document-manager-uploader',
31240                     cn : [
31241                         {
31242                             tag : 'div',
31243                             cls : 'roo-document-manager-upload-btn',
31244                             html : '<i class="fa fa-plus"></i>'
31245                         }
31246                     ]
31247                     
31248                 }
31249             ]
31250         };
31251         
31252         var content = [
31253             {
31254                 tag : 'div',
31255                 cls : 'column col-md-12',
31256                 cn : managerWidget
31257             }
31258         ];
31259         
31260         if(this.fieldLabel.length){
31261             
31262             content = [
31263                 {
31264                     tag : 'div',
31265                     cls : 'column col-md-12',
31266                     html : this.fieldLabel
31267                 },
31268                 {
31269                     tag : 'div',
31270                     cls : 'column col-md-12',
31271                     cn : managerWidget
31272                 }
31273             ];
31274
31275             if(this.labelAlign == 'left'){
31276                 content = [
31277                     {
31278                         tag : 'div',
31279                         cls : 'column',
31280                         html : this.fieldLabel
31281                     },
31282                     {
31283                         tag : 'div',
31284                         cls : 'column',
31285                         cn : managerWidget
31286                     }
31287                 ];
31288                 
31289                 if(this.labelWidth > 12){
31290                     content[0].style = "width: " + this.labelWidth + 'px';
31291                 }
31292
31293                 if(this.labelWidth < 13 && this.labelmd == 0){
31294                     this.labelmd = this.labelWidth;
31295                 }
31296
31297                 if(this.labellg > 0){
31298                     content[0].cls += ' col-lg-' + this.labellg;
31299                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31300                 }
31301
31302                 if(this.labelmd > 0){
31303                     content[0].cls += ' col-md-' + this.labelmd;
31304                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31305                 }
31306
31307                 if(this.labelsm > 0){
31308                     content[0].cls += ' col-sm-' + this.labelsm;
31309                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31310                 }
31311
31312                 if(this.labelxs > 0){
31313                     content[0].cls += ' col-xs-' + this.labelxs;
31314                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31315                 }
31316                 
31317             }
31318         }
31319         
31320         var cfg = {
31321             tag : 'div',
31322             cls : 'row clearfix',
31323             cn : content
31324         };
31325         
31326         return cfg;
31327         
31328     },
31329     
31330     initEvents : function()
31331     {
31332         this.managerEl = this.el.select('.roo-document-manager', true).first();
31333         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31334         
31335         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31336         this.selectorEl.hide();
31337         
31338         if(this.multiple){
31339             this.selectorEl.attr('multiple', 'multiple');
31340         }
31341         
31342         this.selectorEl.on('change', this.onFileSelected, this);
31343         
31344         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31345         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31346         
31347         this.uploader.on('click', this.onUploaderClick, this);
31348         
31349         this.renderProgressDialog();
31350         
31351         var _this = this;
31352         
31353         window.addEventListener("resize", function() { _this.refresh(); } );
31354         
31355         this.fireEvent('initial', this);
31356     },
31357     
31358     renderProgressDialog : function()
31359     {
31360         var _this = this;
31361         
31362         this.progressDialog = new Roo.bootstrap.Modal({
31363             cls : 'roo-document-manager-progress-dialog',
31364             allow_close : false,
31365             animate : false,
31366             title : '',
31367             buttons : [
31368                 {
31369                     name  :'cancel',
31370                     weight : 'danger',
31371                     html : 'Cancel'
31372                 }
31373             ], 
31374             listeners : { 
31375                 btnclick : function() {
31376                     _this.uploadCancel();
31377                     this.hide();
31378                 }
31379             }
31380         });
31381          
31382         this.progressDialog.render(Roo.get(document.body));
31383          
31384         this.progress = new Roo.bootstrap.Progress({
31385             cls : 'roo-document-manager-progress',
31386             active : true,
31387             striped : true
31388         });
31389         
31390         this.progress.render(this.progressDialog.getChildContainer());
31391         
31392         this.progressBar = new Roo.bootstrap.ProgressBar({
31393             cls : 'roo-document-manager-progress-bar',
31394             aria_valuenow : 0,
31395             aria_valuemin : 0,
31396             aria_valuemax : 12,
31397             panel : 'success'
31398         });
31399         
31400         this.progressBar.render(this.progress.getChildContainer());
31401     },
31402     
31403     onUploaderClick : function(e)
31404     {
31405         e.preventDefault();
31406      
31407         if(this.fireEvent('beforeselectfile', this) != false){
31408             this.selectorEl.dom.click();
31409         }
31410         
31411     },
31412     
31413     onFileSelected : function(e)
31414     {
31415         e.preventDefault();
31416         
31417         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31418             return;
31419         }
31420         
31421         Roo.each(this.selectorEl.dom.files, function(file){
31422             if(this.fireEvent('inspect', this, file) != false){
31423                 this.files.push(file);
31424             }
31425         }, this);
31426         
31427         this.queue();
31428         
31429     },
31430     
31431     queue : function()
31432     {
31433         this.selectorEl.dom.value = '';
31434         
31435         if(!this.files || !this.files.length){
31436             return;
31437         }
31438         
31439         if(this.boxes > 0 && this.files.length > this.boxes){
31440             this.files = this.files.slice(0, this.boxes);
31441         }
31442         
31443         this.uploader.show();
31444         
31445         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31446             this.uploader.hide();
31447         }
31448         
31449         var _this = this;
31450         
31451         var files = [];
31452         
31453         var docs = [];
31454         
31455         Roo.each(this.files, function(file){
31456             
31457             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31458                 var f = this.renderPreview(file);
31459                 files.push(f);
31460                 return;
31461             }
31462             
31463             if(file.type.indexOf('image') != -1){
31464                 this.delegates.push(
31465                     (function(){
31466                         _this.process(file);
31467                     }).createDelegate(this)
31468                 );
31469         
31470                 return;
31471             }
31472             
31473             docs.push(
31474                 (function(){
31475                     _this.process(file);
31476                 }).createDelegate(this)
31477             );
31478             
31479         }, this);
31480         
31481         this.files = files;
31482         
31483         this.delegates = this.delegates.concat(docs);
31484         
31485         if(!this.delegates.length){
31486             this.refresh();
31487             return;
31488         }
31489         
31490         this.progressBar.aria_valuemax = this.delegates.length;
31491         
31492         this.arrange();
31493         
31494         return;
31495     },
31496     
31497     arrange : function()
31498     {
31499         if(!this.delegates.length){
31500             this.progressDialog.hide();
31501             this.refresh();
31502             return;
31503         }
31504         
31505         var delegate = this.delegates.shift();
31506         
31507         this.progressDialog.show();
31508         
31509         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31510         
31511         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31512         
31513         delegate();
31514     },
31515     
31516     refresh : function()
31517     {
31518         this.uploader.show();
31519         
31520         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31521             this.uploader.hide();
31522         }
31523         
31524         Roo.isTouch ? this.closable(false) : this.closable(true);
31525         
31526         this.fireEvent('refresh', this);
31527     },
31528     
31529     onRemove : function(e, el, o)
31530     {
31531         e.preventDefault();
31532         
31533         this.fireEvent('remove', this, o);
31534         
31535     },
31536     
31537     remove : function(o)
31538     {
31539         var files = [];
31540         
31541         Roo.each(this.files, function(file){
31542             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31543                 files.push(file);
31544                 return;
31545             }
31546
31547             o.target.remove();
31548
31549         }, this);
31550         
31551         this.files = files;
31552         
31553         this.refresh();
31554     },
31555     
31556     clear : function()
31557     {
31558         Roo.each(this.files, function(file){
31559             if(!file.target){
31560                 return;
31561             }
31562             
31563             file.target.remove();
31564
31565         }, this);
31566         
31567         this.files = [];
31568         
31569         this.refresh();
31570     },
31571     
31572     onClick : function(e, el, o)
31573     {
31574         e.preventDefault();
31575         
31576         this.fireEvent('click', this, o);
31577         
31578     },
31579     
31580     closable : function(closable)
31581     {
31582         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31583             
31584             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31585             
31586             if(closable){
31587                 el.show();
31588                 return;
31589             }
31590             
31591             el.hide();
31592             
31593         }, this);
31594     },
31595     
31596     xhrOnLoad : function(xhr)
31597     {
31598         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31599             el.remove();
31600         }, this);
31601         
31602         if (xhr.readyState !== 4) {
31603             this.arrange();
31604             this.fireEvent('exception', this, xhr);
31605             return;
31606         }
31607
31608         var response = Roo.decode(xhr.responseText);
31609         
31610         if(!response.success){
31611             this.arrange();
31612             this.fireEvent('exception', this, xhr);
31613             return;
31614         }
31615         
31616         var file = this.renderPreview(response.data);
31617         
31618         this.files.push(file);
31619         
31620         this.arrange();
31621         
31622         this.fireEvent('afterupload', this, xhr);
31623         
31624     },
31625     
31626     xhrOnError : function(xhr)
31627     {
31628         Roo.log('xhr on error');
31629         
31630         var response = Roo.decode(xhr.responseText);
31631           
31632         Roo.log(response);
31633         
31634         this.arrange();
31635     },
31636     
31637     process : function(file)
31638     {
31639         if(this.fireEvent('process', this, file) !== false){
31640             if(this.editable && file.type.indexOf('image') != -1){
31641                 this.fireEvent('edit', this, file);
31642                 return;
31643             }
31644
31645             this.uploadStart(file, false);
31646
31647             return;
31648         }
31649         
31650     },
31651     
31652     uploadStart : function(file, crop)
31653     {
31654         this.xhr = new XMLHttpRequest();
31655         
31656         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31657             this.arrange();
31658             return;
31659         }
31660         
31661         file.xhr = this.xhr;
31662             
31663         this.managerEl.createChild({
31664             tag : 'div',
31665             cls : 'roo-document-manager-loading',
31666             cn : [
31667                 {
31668                     tag : 'div',
31669                     tooltip : file.name,
31670                     cls : 'roo-document-manager-thumb',
31671                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31672                 }
31673             ]
31674
31675         });
31676
31677         this.xhr.open(this.method, this.url, true);
31678         
31679         var headers = {
31680             "Accept": "application/json",
31681             "Cache-Control": "no-cache",
31682             "X-Requested-With": "XMLHttpRequest"
31683         };
31684         
31685         for (var headerName in headers) {
31686             var headerValue = headers[headerName];
31687             if (headerValue) {
31688                 this.xhr.setRequestHeader(headerName, headerValue);
31689             }
31690         }
31691         
31692         var _this = this;
31693         
31694         this.xhr.onload = function()
31695         {
31696             _this.xhrOnLoad(_this.xhr);
31697         }
31698         
31699         this.xhr.onerror = function()
31700         {
31701             _this.xhrOnError(_this.xhr);
31702         }
31703         
31704         var formData = new FormData();
31705
31706         formData.append('returnHTML', 'NO');
31707         
31708         if(crop){
31709             formData.append('crop', crop);
31710         }
31711         
31712         formData.append(this.paramName, file, file.name);
31713         
31714         var options = {
31715             file : file, 
31716             manually : false
31717         };
31718         
31719         if(this.fireEvent('prepare', this, formData, options) != false){
31720             
31721             if(options.manually){
31722                 return;
31723             }
31724             
31725             this.xhr.send(formData);
31726             return;
31727         };
31728         
31729         this.uploadCancel();
31730     },
31731     
31732     uploadCancel : function()
31733     {
31734         if (this.xhr) {
31735             this.xhr.abort();
31736         }
31737         
31738         this.delegates = [];
31739         
31740         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31741             el.remove();
31742         }, this);
31743         
31744         this.arrange();
31745     },
31746     
31747     renderPreview : function(file)
31748     {
31749         if(typeof(file.target) != 'undefined' && file.target){
31750             return file;
31751         }
31752         
31753         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31754         
31755         var previewEl = this.managerEl.createChild({
31756             tag : 'div',
31757             cls : 'roo-document-manager-preview',
31758             cn : [
31759                 {
31760                     tag : 'div',
31761                     tooltip : file[this.toolTipName],
31762                     cls : 'roo-document-manager-thumb',
31763                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31764                 },
31765                 {
31766                     tag : 'button',
31767                     cls : 'close',
31768                     html : '<i class="fa fa-times-circle"></i>'
31769                 }
31770             ]
31771         });
31772
31773         var close = previewEl.select('button.close', true).first();
31774
31775         close.on('click', this.onRemove, this, file);
31776
31777         file.target = previewEl;
31778
31779         var image = previewEl.select('img', true).first();
31780         
31781         var _this = this;
31782         
31783         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31784         
31785         image.on('click', this.onClick, this, file);
31786         
31787         this.fireEvent('previewrendered', this, file);
31788         
31789         return file;
31790         
31791     },
31792     
31793     onPreviewLoad : function(file, image)
31794     {
31795         if(typeof(file.target) == 'undefined' || !file.target){
31796             return;
31797         }
31798         
31799         var width = image.dom.naturalWidth || image.dom.width;
31800         var height = image.dom.naturalHeight || image.dom.height;
31801         
31802         if(!this.previewResize) {
31803             return;
31804         }
31805         
31806         if(width > height){
31807             file.target.addClass('wide');
31808             return;
31809         }
31810         
31811         file.target.addClass('tall');
31812         return;
31813         
31814     },
31815     
31816     uploadFromSource : function(file, crop)
31817     {
31818         this.xhr = new XMLHttpRequest();
31819         
31820         this.managerEl.createChild({
31821             tag : 'div',
31822             cls : 'roo-document-manager-loading',
31823             cn : [
31824                 {
31825                     tag : 'div',
31826                     tooltip : file.name,
31827                     cls : 'roo-document-manager-thumb',
31828                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31829                 }
31830             ]
31831
31832         });
31833
31834         this.xhr.open(this.method, this.url, true);
31835         
31836         var headers = {
31837             "Accept": "application/json",
31838             "Cache-Control": "no-cache",
31839             "X-Requested-With": "XMLHttpRequest"
31840         };
31841         
31842         for (var headerName in headers) {
31843             var headerValue = headers[headerName];
31844             if (headerValue) {
31845                 this.xhr.setRequestHeader(headerName, headerValue);
31846             }
31847         }
31848         
31849         var _this = this;
31850         
31851         this.xhr.onload = function()
31852         {
31853             _this.xhrOnLoad(_this.xhr);
31854         }
31855         
31856         this.xhr.onerror = function()
31857         {
31858             _this.xhrOnError(_this.xhr);
31859         }
31860         
31861         var formData = new FormData();
31862
31863         formData.append('returnHTML', 'NO');
31864         
31865         formData.append('crop', crop);
31866         
31867         if(typeof(file.filename) != 'undefined'){
31868             formData.append('filename', file.filename);
31869         }
31870         
31871         if(typeof(file.mimetype) != 'undefined'){
31872             formData.append('mimetype', file.mimetype);
31873         }
31874         
31875         Roo.log(formData);
31876         
31877         if(this.fireEvent('prepare', this, formData) != false){
31878             this.xhr.send(formData);
31879         };
31880     }
31881 });
31882
31883 /*
31884 * Licence: LGPL
31885 */
31886
31887 /**
31888  * @class Roo.bootstrap.DocumentViewer
31889  * @extends Roo.bootstrap.Component
31890  * Bootstrap DocumentViewer class
31891  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31892  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31893  * 
31894  * @constructor
31895  * Create a new DocumentViewer
31896  * @param {Object} config The config object
31897  */
31898
31899 Roo.bootstrap.DocumentViewer = function(config){
31900     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31901     
31902     this.addEvents({
31903         /**
31904          * @event initial
31905          * Fire after initEvent
31906          * @param {Roo.bootstrap.DocumentViewer} this
31907          */
31908         "initial" : true,
31909         /**
31910          * @event click
31911          * Fire after click
31912          * @param {Roo.bootstrap.DocumentViewer} this
31913          */
31914         "click" : true,
31915         /**
31916          * @event download
31917          * Fire after download button
31918          * @param {Roo.bootstrap.DocumentViewer} this
31919          */
31920         "download" : true,
31921         /**
31922          * @event trash
31923          * Fire after trash button
31924          * @param {Roo.bootstrap.DocumentViewer} this
31925          */
31926         "trash" : true
31927         
31928     });
31929 };
31930
31931 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31932     
31933     showDownload : true,
31934     
31935     showTrash : true,
31936     
31937     getAutoCreate : function()
31938     {
31939         var cfg = {
31940             tag : 'div',
31941             cls : 'roo-document-viewer',
31942             cn : [
31943                 {
31944                     tag : 'div',
31945                     cls : 'roo-document-viewer-body',
31946                     cn : [
31947                         {
31948                             tag : 'div',
31949                             cls : 'roo-document-viewer-thumb',
31950                             cn : [
31951                                 {
31952                                     tag : 'img',
31953                                     cls : 'roo-document-viewer-image'
31954                                 }
31955                             ]
31956                         }
31957                     ]
31958                 },
31959                 {
31960                     tag : 'div',
31961                     cls : 'roo-document-viewer-footer',
31962                     cn : {
31963                         tag : 'div',
31964                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31965                         cn : [
31966                             {
31967                                 tag : 'div',
31968                                 cls : 'btn-group roo-document-viewer-download',
31969                                 cn : [
31970                                     {
31971                                         tag : 'button',
31972                                         cls : 'btn btn-default',
31973                                         html : '<i class="fa fa-download"></i>'
31974                                     }
31975                                 ]
31976                             },
31977                             {
31978                                 tag : 'div',
31979                                 cls : 'btn-group roo-document-viewer-trash',
31980                                 cn : [
31981                                     {
31982                                         tag : 'button',
31983                                         cls : 'btn btn-default',
31984                                         html : '<i class="fa fa-trash"></i>'
31985                                     }
31986                                 ]
31987                             }
31988                         ]
31989                     }
31990                 }
31991             ]
31992         };
31993         
31994         return cfg;
31995     },
31996     
31997     initEvents : function()
31998     {
31999         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32000         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32001         
32002         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32003         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32004         
32005         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32006         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32007         
32008         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32009         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32010         
32011         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32012         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32013         
32014         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32015         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32016         
32017         this.bodyEl.on('click', this.onClick, this);
32018         this.downloadBtn.on('click', this.onDownload, this);
32019         this.trashBtn.on('click', this.onTrash, this);
32020         
32021         this.downloadBtn.hide();
32022         this.trashBtn.hide();
32023         
32024         if(this.showDownload){
32025             this.downloadBtn.show();
32026         }
32027         
32028         if(this.showTrash){
32029             this.trashBtn.show();
32030         }
32031         
32032         if(!this.showDownload && !this.showTrash) {
32033             this.footerEl.hide();
32034         }
32035         
32036     },
32037     
32038     initial : function()
32039     {
32040         this.fireEvent('initial', this);
32041         
32042     },
32043     
32044     onClick : function(e)
32045     {
32046         e.preventDefault();
32047         
32048         this.fireEvent('click', this);
32049     },
32050     
32051     onDownload : function(e)
32052     {
32053         e.preventDefault();
32054         
32055         this.fireEvent('download', this);
32056     },
32057     
32058     onTrash : function(e)
32059     {
32060         e.preventDefault();
32061         
32062         this.fireEvent('trash', this);
32063     }
32064     
32065 });
32066 /*
32067  * - LGPL
32068  *
32069  * nav progress bar
32070  * 
32071  */
32072
32073 /**
32074  * @class Roo.bootstrap.NavProgressBar
32075  * @extends Roo.bootstrap.Component
32076  * Bootstrap NavProgressBar class
32077  * 
32078  * @constructor
32079  * Create a new nav progress bar
32080  * @param {Object} config The config object
32081  */
32082
32083 Roo.bootstrap.NavProgressBar = function(config){
32084     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32085
32086     this.bullets = this.bullets || [];
32087    
32088 //    Roo.bootstrap.NavProgressBar.register(this);
32089      this.addEvents({
32090         /**
32091              * @event changed
32092              * Fires when the active item changes
32093              * @param {Roo.bootstrap.NavProgressBar} this
32094              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32095              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32096          */
32097         'changed': true
32098      });
32099     
32100 };
32101
32102 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32103     
32104     bullets : [],
32105     barItems : [],
32106     
32107     getAutoCreate : function()
32108     {
32109         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32110         
32111         cfg = {
32112             tag : 'div',
32113             cls : 'roo-navigation-bar-group',
32114             cn : [
32115                 {
32116                     tag : 'div',
32117                     cls : 'roo-navigation-top-bar'
32118                 },
32119                 {
32120                     tag : 'div',
32121                     cls : 'roo-navigation-bullets-bar',
32122                     cn : [
32123                         {
32124                             tag : 'ul',
32125                             cls : 'roo-navigation-bar'
32126                         }
32127                     ]
32128                 },
32129                 
32130                 {
32131                     tag : 'div',
32132                     cls : 'roo-navigation-bottom-bar'
32133                 }
32134             ]
32135             
32136         };
32137         
32138         return cfg;
32139         
32140     },
32141     
32142     initEvents: function() 
32143     {
32144         
32145     },
32146     
32147     onRender : function(ct, position) 
32148     {
32149         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32150         
32151         if(this.bullets.length){
32152             Roo.each(this.bullets, function(b){
32153                this.addItem(b);
32154             }, this);
32155         }
32156         
32157         this.format();
32158         
32159     },
32160     
32161     addItem : function(cfg)
32162     {
32163         var item = new Roo.bootstrap.NavProgressItem(cfg);
32164         
32165         item.parentId = this.id;
32166         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32167         
32168         if(cfg.html){
32169             var top = new Roo.bootstrap.Element({
32170                 tag : 'div',
32171                 cls : 'roo-navigation-bar-text'
32172             });
32173             
32174             var bottom = new Roo.bootstrap.Element({
32175                 tag : 'div',
32176                 cls : 'roo-navigation-bar-text'
32177             });
32178             
32179             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32180             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32181             
32182             var topText = new Roo.bootstrap.Element({
32183                 tag : 'span',
32184                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32185             });
32186             
32187             var bottomText = new Roo.bootstrap.Element({
32188                 tag : 'span',
32189                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32190             });
32191             
32192             topText.onRender(top.el, null);
32193             bottomText.onRender(bottom.el, null);
32194             
32195             item.topEl = top;
32196             item.bottomEl = bottom;
32197         }
32198         
32199         this.barItems.push(item);
32200         
32201         return item;
32202     },
32203     
32204     getActive : function()
32205     {
32206         var active = false;
32207         
32208         Roo.each(this.barItems, function(v){
32209             
32210             if (!v.isActive()) {
32211                 return;
32212             }
32213             
32214             active = v;
32215             return false;
32216             
32217         });
32218         
32219         return active;
32220     },
32221     
32222     setActiveItem : function(item)
32223     {
32224         var prev = false;
32225         
32226         Roo.each(this.barItems, function(v){
32227             if (v.rid == item.rid) {
32228                 return ;
32229             }
32230             
32231             if (v.isActive()) {
32232                 v.setActive(false);
32233                 prev = v;
32234             }
32235         });
32236
32237         item.setActive(true);
32238         
32239         this.fireEvent('changed', this, item, prev);
32240     },
32241     
32242     getBarItem: function(rid)
32243     {
32244         var ret = false;
32245         
32246         Roo.each(this.barItems, function(e) {
32247             if (e.rid != rid) {
32248                 return;
32249             }
32250             
32251             ret =  e;
32252             return false;
32253         });
32254         
32255         return ret;
32256     },
32257     
32258     indexOfItem : function(item)
32259     {
32260         var index = false;
32261         
32262         Roo.each(this.barItems, function(v, i){
32263             
32264             if (v.rid != item.rid) {
32265                 return;
32266             }
32267             
32268             index = i;
32269             return false
32270         });
32271         
32272         return index;
32273     },
32274     
32275     setActiveNext : function()
32276     {
32277         var i = this.indexOfItem(this.getActive());
32278         
32279         if (i > this.barItems.length) {
32280             return;
32281         }
32282         
32283         this.setActiveItem(this.barItems[i+1]);
32284     },
32285     
32286     setActivePrev : function()
32287     {
32288         var i = this.indexOfItem(this.getActive());
32289         
32290         if (i  < 1) {
32291             return;
32292         }
32293         
32294         this.setActiveItem(this.barItems[i-1]);
32295     },
32296     
32297     format : function()
32298     {
32299         if(!this.barItems.length){
32300             return;
32301         }
32302      
32303         var width = 100 / this.barItems.length;
32304         
32305         Roo.each(this.barItems, function(i){
32306             i.el.setStyle('width', width + '%');
32307             i.topEl.el.setStyle('width', width + '%');
32308             i.bottomEl.el.setStyle('width', width + '%');
32309         }, this);
32310         
32311     }
32312     
32313 });
32314 /*
32315  * - LGPL
32316  *
32317  * Nav Progress Item
32318  * 
32319  */
32320
32321 /**
32322  * @class Roo.bootstrap.NavProgressItem
32323  * @extends Roo.bootstrap.Component
32324  * Bootstrap NavProgressItem class
32325  * @cfg {String} rid the reference id
32326  * @cfg {Boolean} active (true|false) Is item active default false
32327  * @cfg {Boolean} disabled (true|false) Is item active default false
32328  * @cfg {String} html
32329  * @cfg {String} position (top|bottom) text position default bottom
32330  * @cfg {String} icon show icon instead of number
32331  * 
32332  * @constructor
32333  * Create a new NavProgressItem
32334  * @param {Object} config The config object
32335  */
32336 Roo.bootstrap.NavProgressItem = function(config){
32337     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32338     this.addEvents({
32339         // raw events
32340         /**
32341          * @event click
32342          * The raw click event for the entire grid.
32343          * @param {Roo.bootstrap.NavProgressItem} this
32344          * @param {Roo.EventObject} e
32345          */
32346         "click" : true
32347     });
32348    
32349 };
32350
32351 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32352     
32353     rid : '',
32354     active : false,
32355     disabled : false,
32356     html : '',
32357     position : 'bottom',
32358     icon : false,
32359     
32360     getAutoCreate : function()
32361     {
32362         var iconCls = 'roo-navigation-bar-item-icon';
32363         
32364         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32365         
32366         var cfg = {
32367             tag: 'li',
32368             cls: 'roo-navigation-bar-item',
32369             cn : [
32370                 {
32371                     tag : 'i',
32372                     cls : iconCls
32373                 }
32374             ]
32375         };
32376         
32377         if(this.active){
32378             cfg.cls += ' active';
32379         }
32380         if(this.disabled){
32381             cfg.cls += ' disabled';
32382         }
32383         
32384         return cfg;
32385     },
32386     
32387     disable : function()
32388     {
32389         this.setDisabled(true);
32390     },
32391     
32392     enable : function()
32393     {
32394         this.setDisabled(false);
32395     },
32396     
32397     initEvents: function() 
32398     {
32399         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32400         
32401         this.iconEl.on('click', this.onClick, this);
32402     },
32403     
32404     onClick : function(e)
32405     {
32406         e.preventDefault();
32407         
32408         if(this.disabled){
32409             return;
32410         }
32411         
32412         if(this.fireEvent('click', this, e) === false){
32413             return;
32414         };
32415         
32416         this.parent().setActiveItem(this);
32417     },
32418     
32419     isActive: function () 
32420     {
32421         return this.active;
32422     },
32423     
32424     setActive : function(state)
32425     {
32426         if(this.active == state){
32427             return;
32428         }
32429         
32430         this.active = state;
32431         
32432         if (state) {
32433             this.el.addClass('active');
32434             return;
32435         }
32436         
32437         this.el.removeClass('active');
32438         
32439         return;
32440     },
32441     
32442     setDisabled : function(state)
32443     {
32444         if(this.disabled == state){
32445             return;
32446         }
32447         
32448         this.disabled = state;
32449         
32450         if (state) {
32451             this.el.addClass('disabled');
32452             return;
32453         }
32454         
32455         this.el.removeClass('disabled');
32456     },
32457     
32458     tooltipEl : function()
32459     {
32460         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32461     }
32462 });
32463  
32464
32465  /*
32466  * - LGPL
32467  *
32468  * FieldLabel
32469  * 
32470  */
32471
32472 /**
32473  * @class Roo.bootstrap.FieldLabel
32474  * @extends Roo.bootstrap.Component
32475  * Bootstrap FieldLabel class
32476  * @cfg {String} html contents of the element
32477  * @cfg {String} tag tag of the element default label
32478  * @cfg {String} cls class of the element
32479  * @cfg {String} target label target 
32480  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32481  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32482  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32483  * @cfg {String} iconTooltip default "This field is required"
32484  * @cfg {String} indicatorpos (left|right) default left
32485  * 
32486  * @constructor
32487  * Create a new FieldLabel
32488  * @param {Object} config The config object
32489  */
32490
32491 Roo.bootstrap.FieldLabel = function(config){
32492     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32493     
32494     this.addEvents({
32495             /**
32496              * @event invalid
32497              * Fires after the field has been marked as invalid.
32498              * @param {Roo.form.FieldLabel} this
32499              * @param {String} msg The validation message
32500              */
32501             invalid : true,
32502             /**
32503              * @event valid
32504              * Fires after the field has been validated with no errors.
32505              * @param {Roo.form.FieldLabel} this
32506              */
32507             valid : true
32508         });
32509 };
32510
32511 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32512     
32513     tag: 'label',
32514     cls: '',
32515     html: '',
32516     target: '',
32517     allowBlank : true,
32518     invalidClass : 'has-warning',
32519     validClass : 'has-success',
32520     iconTooltip : 'This field is required',
32521     indicatorpos : 'left',
32522     
32523     getAutoCreate : function(){
32524         
32525         var cls = "";
32526         if (!this.allowBlank) {
32527             cls  = "visible";
32528         }
32529         
32530         var cfg = {
32531             tag : this.tag,
32532             cls : 'roo-bootstrap-field-label ' + this.cls,
32533             for : this.target,
32534             cn : [
32535                 {
32536                     tag : 'i',
32537                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32538                     tooltip : this.iconTooltip
32539                 },
32540                 {
32541                     tag : 'span',
32542                     html : this.html
32543                 }
32544             ] 
32545         };
32546         
32547         if(this.indicatorpos == 'right'){
32548             var cfg = {
32549                 tag : this.tag,
32550                 cls : 'roo-bootstrap-field-label ' + this.cls,
32551                 for : this.target,
32552                 cn : [
32553                     {
32554                         tag : 'span',
32555                         html : this.html
32556                     },
32557                     {
32558                         tag : 'i',
32559                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32560                         tooltip : this.iconTooltip
32561                     }
32562                 ] 
32563             };
32564         }
32565         
32566         return cfg;
32567     },
32568     
32569     initEvents: function() 
32570     {
32571         Roo.bootstrap.Element.superclass.initEvents.call(this);
32572         
32573         this.indicator = this.indicatorEl();
32574         
32575         if(this.indicator){
32576             this.indicator.removeClass('visible');
32577             this.indicator.addClass('invisible');
32578         }
32579         
32580         Roo.bootstrap.FieldLabel.register(this);
32581     },
32582     
32583     indicatorEl : function()
32584     {
32585         var indicator = this.el.select('i.roo-required-indicator',true).first();
32586         
32587         if(!indicator){
32588             return false;
32589         }
32590         
32591         return indicator;
32592         
32593     },
32594     
32595     /**
32596      * Mark this field as valid
32597      */
32598     markValid : function()
32599     {
32600         if(this.indicator){
32601             this.indicator.removeClass('visible');
32602             this.indicator.addClass('invisible');
32603         }
32604         if (Roo.bootstrap.version == 3) {
32605             this.el.removeClass(this.invalidClass);
32606             this.el.addClass(this.validClass);
32607         } else {
32608             this.el.removeClass('is-invalid');
32609             this.el.addClass('is-valid');
32610         }
32611         
32612         
32613         this.fireEvent('valid', this);
32614     },
32615     
32616     /**
32617      * Mark this field as invalid
32618      * @param {String} msg The validation message
32619      */
32620     markInvalid : function(msg)
32621     {
32622         if(this.indicator){
32623             this.indicator.removeClass('invisible');
32624             this.indicator.addClass('visible');
32625         }
32626           if (Roo.bootstrap.version == 3) {
32627             this.el.removeClass(this.validClass);
32628             this.el.addClass(this.invalidClass);
32629         } else {
32630             this.el.removeClass('is-valid');
32631             this.el.addClass('is-invalid');
32632         }
32633         
32634         
32635         this.fireEvent('invalid', this, msg);
32636     }
32637     
32638    
32639 });
32640
32641 Roo.apply(Roo.bootstrap.FieldLabel, {
32642     
32643     groups: {},
32644     
32645      /**
32646     * register a FieldLabel Group
32647     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32648     */
32649     register : function(label)
32650     {
32651         if(this.groups.hasOwnProperty(label.target)){
32652             return;
32653         }
32654      
32655         this.groups[label.target] = label;
32656         
32657     },
32658     /**
32659     * fetch a FieldLabel Group based on the target
32660     * @param {string} target
32661     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32662     */
32663     get: function(target) {
32664         if (typeof(this.groups[target]) == 'undefined') {
32665             return false;
32666         }
32667         
32668         return this.groups[target] ;
32669     }
32670 });
32671
32672  
32673
32674  /*
32675  * - LGPL
32676  *
32677  * page DateSplitField.
32678  * 
32679  */
32680
32681
32682 /**
32683  * @class Roo.bootstrap.DateSplitField
32684  * @extends Roo.bootstrap.Component
32685  * Bootstrap DateSplitField class
32686  * @cfg {string} fieldLabel - the label associated
32687  * @cfg {Number} labelWidth set the width of label (0-12)
32688  * @cfg {String} labelAlign (top|left)
32689  * @cfg {Boolean} dayAllowBlank (true|false) default false
32690  * @cfg {Boolean} monthAllowBlank (true|false) default false
32691  * @cfg {Boolean} yearAllowBlank (true|false) default false
32692  * @cfg {string} dayPlaceholder 
32693  * @cfg {string} monthPlaceholder
32694  * @cfg {string} yearPlaceholder
32695  * @cfg {string} dayFormat default 'd'
32696  * @cfg {string} monthFormat default 'm'
32697  * @cfg {string} yearFormat default 'Y'
32698  * @cfg {Number} labellg set the width of label (1-12)
32699  * @cfg {Number} labelmd set the width of label (1-12)
32700  * @cfg {Number} labelsm set the width of label (1-12)
32701  * @cfg {Number} labelxs set the width of label (1-12)
32702
32703  *     
32704  * @constructor
32705  * Create a new DateSplitField
32706  * @param {Object} config The config object
32707  */
32708
32709 Roo.bootstrap.DateSplitField = function(config){
32710     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32711     
32712     this.addEvents({
32713         // raw events
32714          /**
32715          * @event years
32716          * getting the data of years
32717          * @param {Roo.bootstrap.DateSplitField} this
32718          * @param {Object} years
32719          */
32720         "years" : true,
32721         /**
32722          * @event days
32723          * getting the data of days
32724          * @param {Roo.bootstrap.DateSplitField} this
32725          * @param {Object} days
32726          */
32727         "days" : true,
32728         /**
32729          * @event invalid
32730          * Fires after the field has been marked as invalid.
32731          * @param {Roo.form.Field} this
32732          * @param {String} msg The validation message
32733          */
32734         invalid : true,
32735        /**
32736          * @event valid
32737          * Fires after the field has been validated with no errors.
32738          * @param {Roo.form.Field} this
32739          */
32740         valid : true
32741     });
32742 };
32743
32744 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32745     
32746     fieldLabel : '',
32747     labelAlign : 'top',
32748     labelWidth : 3,
32749     dayAllowBlank : false,
32750     monthAllowBlank : false,
32751     yearAllowBlank : false,
32752     dayPlaceholder : '',
32753     monthPlaceholder : '',
32754     yearPlaceholder : '',
32755     dayFormat : 'd',
32756     monthFormat : 'm',
32757     yearFormat : 'Y',
32758     isFormField : true,
32759     labellg : 0,
32760     labelmd : 0,
32761     labelsm : 0,
32762     labelxs : 0,
32763     
32764     getAutoCreate : function()
32765     {
32766         var cfg = {
32767             tag : 'div',
32768             cls : 'row roo-date-split-field-group',
32769             cn : [
32770                 {
32771                     tag : 'input',
32772                     type : 'hidden',
32773                     cls : 'form-hidden-field roo-date-split-field-group-value',
32774                     name : this.name
32775                 }
32776             ]
32777         };
32778         
32779         var labelCls = 'col-md-12';
32780         var contentCls = 'col-md-4';
32781         
32782         if(this.fieldLabel){
32783             
32784             var label = {
32785                 tag : 'div',
32786                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32787                 cn : [
32788                     {
32789                         tag : 'label',
32790                         html : this.fieldLabel
32791                     }
32792                 ]
32793             };
32794             
32795             if(this.labelAlign == 'left'){
32796             
32797                 if(this.labelWidth > 12){
32798                     label.style = "width: " + this.labelWidth + 'px';
32799                 }
32800
32801                 if(this.labelWidth < 13 && this.labelmd == 0){
32802                     this.labelmd = this.labelWidth;
32803                 }
32804
32805                 if(this.labellg > 0){
32806                     labelCls = ' col-lg-' + this.labellg;
32807                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32808                 }
32809
32810                 if(this.labelmd > 0){
32811                     labelCls = ' col-md-' + this.labelmd;
32812                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32813                 }
32814
32815                 if(this.labelsm > 0){
32816                     labelCls = ' col-sm-' + this.labelsm;
32817                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32818                 }
32819
32820                 if(this.labelxs > 0){
32821                     labelCls = ' col-xs-' + this.labelxs;
32822                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32823                 }
32824             }
32825             
32826             label.cls += ' ' + labelCls;
32827             
32828             cfg.cn.push(label);
32829         }
32830         
32831         Roo.each(['day', 'month', 'year'], function(t){
32832             cfg.cn.push({
32833                 tag : 'div',
32834                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32835             });
32836         }, this);
32837         
32838         return cfg;
32839     },
32840     
32841     inputEl: function ()
32842     {
32843         return this.el.select('.roo-date-split-field-group-value', true).first();
32844     },
32845     
32846     onRender : function(ct, position) 
32847     {
32848         var _this = this;
32849         
32850         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32851         
32852         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32853         
32854         this.dayField = new Roo.bootstrap.ComboBox({
32855             allowBlank : this.dayAllowBlank,
32856             alwaysQuery : true,
32857             displayField : 'value',
32858             editable : false,
32859             fieldLabel : '',
32860             forceSelection : true,
32861             mode : 'local',
32862             placeholder : this.dayPlaceholder,
32863             selectOnFocus : true,
32864             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32865             triggerAction : 'all',
32866             typeAhead : true,
32867             valueField : 'value',
32868             store : new Roo.data.SimpleStore({
32869                 data : (function() {    
32870                     var days = [];
32871                     _this.fireEvent('days', _this, days);
32872                     return days;
32873                 })(),
32874                 fields : [ 'value' ]
32875             }),
32876             listeners : {
32877                 select : function (_self, record, index)
32878                 {
32879                     _this.setValue(_this.getValue());
32880                 }
32881             }
32882         });
32883
32884         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32885         
32886         this.monthField = new Roo.bootstrap.MonthField({
32887             after : '<i class=\"fa fa-calendar\"></i>',
32888             allowBlank : this.monthAllowBlank,
32889             placeholder : this.monthPlaceholder,
32890             readOnly : true,
32891             listeners : {
32892                 render : function (_self)
32893                 {
32894                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32895                         e.preventDefault();
32896                         _self.focus();
32897                     });
32898                 },
32899                 select : function (_self, oldvalue, newvalue)
32900                 {
32901                     _this.setValue(_this.getValue());
32902                 }
32903             }
32904         });
32905         
32906         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32907         
32908         this.yearField = new Roo.bootstrap.ComboBox({
32909             allowBlank : this.yearAllowBlank,
32910             alwaysQuery : true,
32911             displayField : 'value',
32912             editable : false,
32913             fieldLabel : '',
32914             forceSelection : true,
32915             mode : 'local',
32916             placeholder : this.yearPlaceholder,
32917             selectOnFocus : true,
32918             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32919             triggerAction : 'all',
32920             typeAhead : true,
32921             valueField : 'value',
32922             store : new Roo.data.SimpleStore({
32923                 data : (function() {
32924                     var years = [];
32925                     _this.fireEvent('years', _this, years);
32926                     return years;
32927                 })(),
32928                 fields : [ 'value' ]
32929             }),
32930             listeners : {
32931                 select : function (_self, record, index)
32932                 {
32933                     _this.setValue(_this.getValue());
32934                 }
32935             }
32936         });
32937
32938         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32939     },
32940     
32941     setValue : function(v, format)
32942     {
32943         this.inputEl.dom.value = v;
32944         
32945         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32946         
32947         var d = Date.parseDate(v, f);
32948         
32949         if(!d){
32950             this.validate();
32951             return;
32952         }
32953         
32954         this.setDay(d.format(this.dayFormat));
32955         this.setMonth(d.format(this.monthFormat));
32956         this.setYear(d.format(this.yearFormat));
32957         
32958         this.validate();
32959         
32960         return;
32961     },
32962     
32963     setDay : function(v)
32964     {
32965         this.dayField.setValue(v);
32966         this.inputEl.dom.value = this.getValue();
32967         this.validate();
32968         return;
32969     },
32970     
32971     setMonth : function(v)
32972     {
32973         this.monthField.setValue(v, true);
32974         this.inputEl.dom.value = this.getValue();
32975         this.validate();
32976         return;
32977     },
32978     
32979     setYear : function(v)
32980     {
32981         this.yearField.setValue(v);
32982         this.inputEl.dom.value = this.getValue();
32983         this.validate();
32984         return;
32985     },
32986     
32987     getDay : function()
32988     {
32989         return this.dayField.getValue();
32990     },
32991     
32992     getMonth : function()
32993     {
32994         return this.monthField.getValue();
32995     },
32996     
32997     getYear : function()
32998     {
32999         return this.yearField.getValue();
33000     },
33001     
33002     getValue : function()
33003     {
33004         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33005         
33006         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33007         
33008         return date;
33009     },
33010     
33011     reset : function()
33012     {
33013         this.setDay('');
33014         this.setMonth('');
33015         this.setYear('');
33016         this.inputEl.dom.value = '';
33017         this.validate();
33018         return;
33019     },
33020     
33021     validate : function()
33022     {
33023         var d = this.dayField.validate();
33024         var m = this.monthField.validate();
33025         var y = this.yearField.validate();
33026         
33027         var valid = true;
33028         
33029         if(
33030                 (!this.dayAllowBlank && !d) ||
33031                 (!this.monthAllowBlank && !m) ||
33032                 (!this.yearAllowBlank && !y)
33033         ){
33034             valid = false;
33035         }
33036         
33037         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33038             return valid;
33039         }
33040         
33041         if(valid){
33042             this.markValid();
33043             return valid;
33044         }
33045         
33046         this.markInvalid();
33047         
33048         return valid;
33049     },
33050     
33051     markValid : function()
33052     {
33053         
33054         var label = this.el.select('label', true).first();
33055         var icon = this.el.select('i.fa-star', true).first();
33056
33057         if(label && icon){
33058             icon.remove();
33059         }
33060         
33061         this.fireEvent('valid', this);
33062     },
33063     
33064      /**
33065      * Mark this field as invalid
33066      * @param {String} msg The validation message
33067      */
33068     markInvalid : function(msg)
33069     {
33070         
33071         var label = this.el.select('label', true).first();
33072         var icon = this.el.select('i.fa-star', true).first();
33073
33074         if(label && !icon){
33075             this.el.select('.roo-date-split-field-label', true).createChild({
33076                 tag : 'i',
33077                 cls : 'text-danger fa fa-lg fa-star',
33078                 tooltip : 'This field is required',
33079                 style : 'margin-right:5px;'
33080             }, label, true);
33081         }
33082         
33083         this.fireEvent('invalid', this, msg);
33084     },
33085     
33086     clearInvalid : function()
33087     {
33088         var label = this.el.select('label', true).first();
33089         var icon = this.el.select('i.fa-star', true).first();
33090
33091         if(label && icon){
33092             icon.remove();
33093         }
33094         
33095         this.fireEvent('valid', this);
33096     },
33097     
33098     getName: function()
33099     {
33100         return this.name;
33101     }
33102     
33103 });
33104
33105  /**
33106  *
33107  * This is based on 
33108  * http://masonry.desandro.com
33109  *
33110  * The idea is to render all the bricks based on vertical width...
33111  *
33112  * The original code extends 'outlayer' - we might need to use that....
33113  * 
33114  */
33115
33116
33117 /**
33118  * @class Roo.bootstrap.LayoutMasonry
33119  * @extends Roo.bootstrap.Component
33120  * Bootstrap Layout Masonry class
33121  * 
33122  * @constructor
33123  * Create a new Element
33124  * @param {Object} config The config object
33125  */
33126
33127 Roo.bootstrap.LayoutMasonry = function(config){
33128     
33129     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33130     
33131     this.bricks = [];
33132     
33133     Roo.bootstrap.LayoutMasonry.register(this);
33134     
33135     this.addEvents({
33136         // raw events
33137         /**
33138          * @event layout
33139          * Fire after layout the items
33140          * @param {Roo.bootstrap.LayoutMasonry} this
33141          * @param {Roo.EventObject} e
33142          */
33143         "layout" : true
33144     });
33145     
33146 };
33147
33148 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33149     
33150     /**
33151      * @cfg {Boolean} isLayoutInstant = no animation?
33152      */   
33153     isLayoutInstant : false, // needed?
33154    
33155     /**
33156      * @cfg {Number} boxWidth  width of the columns
33157      */   
33158     boxWidth : 450,
33159     
33160       /**
33161      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33162      */   
33163     boxHeight : 0,
33164     
33165     /**
33166      * @cfg {Number} padWidth padding below box..
33167      */   
33168     padWidth : 10, 
33169     
33170     /**
33171      * @cfg {Number} gutter gutter width..
33172      */   
33173     gutter : 10,
33174     
33175      /**
33176      * @cfg {Number} maxCols maximum number of columns
33177      */   
33178     
33179     maxCols: 0,
33180     
33181     /**
33182      * @cfg {Boolean} isAutoInitial defalut true
33183      */   
33184     isAutoInitial : true, 
33185     
33186     containerWidth: 0,
33187     
33188     /**
33189      * @cfg {Boolean} isHorizontal defalut false
33190      */   
33191     isHorizontal : false, 
33192
33193     currentSize : null,
33194     
33195     tag: 'div',
33196     
33197     cls: '',
33198     
33199     bricks: null, //CompositeElement
33200     
33201     cols : 1,
33202     
33203     _isLayoutInited : false,
33204     
33205 //    isAlternative : false, // only use for vertical layout...
33206     
33207     /**
33208      * @cfg {Number} alternativePadWidth padding below box..
33209      */   
33210     alternativePadWidth : 50,
33211     
33212     selectedBrick : [],
33213     
33214     getAutoCreate : function(){
33215         
33216         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33217         
33218         var cfg = {
33219             tag: this.tag,
33220             cls: 'blog-masonary-wrapper ' + this.cls,
33221             cn : {
33222                 cls : 'mas-boxes masonary'
33223             }
33224         };
33225         
33226         return cfg;
33227     },
33228     
33229     getChildContainer: function( )
33230     {
33231         if (this.boxesEl) {
33232             return this.boxesEl;
33233         }
33234         
33235         this.boxesEl = this.el.select('.mas-boxes').first();
33236         
33237         return this.boxesEl;
33238     },
33239     
33240     
33241     initEvents : function()
33242     {
33243         var _this = this;
33244         
33245         if(this.isAutoInitial){
33246             Roo.log('hook children rendered');
33247             this.on('childrenrendered', function() {
33248                 Roo.log('children rendered');
33249                 _this.initial();
33250             } ,this);
33251         }
33252     },
33253     
33254     initial : function()
33255     {
33256         this.selectedBrick = [];
33257         
33258         this.currentSize = this.el.getBox(true);
33259         
33260         Roo.EventManager.onWindowResize(this.resize, this); 
33261
33262         if(!this.isAutoInitial){
33263             this.layout();
33264             return;
33265         }
33266         
33267         this.layout();
33268         
33269         return;
33270         //this.layout.defer(500,this);
33271         
33272     },
33273     
33274     resize : function()
33275     {
33276         var cs = this.el.getBox(true);
33277         
33278         if (
33279                 this.currentSize.width == cs.width && 
33280                 this.currentSize.x == cs.x && 
33281                 this.currentSize.height == cs.height && 
33282                 this.currentSize.y == cs.y 
33283         ) {
33284             Roo.log("no change in with or X or Y");
33285             return;
33286         }
33287         
33288         this.currentSize = cs;
33289         
33290         this.layout();
33291         
33292     },
33293     
33294     layout : function()
33295     {   
33296         this._resetLayout();
33297         
33298         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33299         
33300         this.layoutItems( isInstant );
33301       
33302         this._isLayoutInited = true;
33303         
33304         this.fireEvent('layout', this);
33305         
33306     },
33307     
33308     _resetLayout : function()
33309     {
33310         if(this.isHorizontal){
33311             this.horizontalMeasureColumns();
33312             return;
33313         }
33314         
33315         this.verticalMeasureColumns();
33316         
33317     },
33318     
33319     verticalMeasureColumns : function()
33320     {
33321         this.getContainerWidth();
33322         
33323 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33324 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33325 //            return;
33326 //        }
33327         
33328         var boxWidth = this.boxWidth + this.padWidth;
33329         
33330         if(this.containerWidth < this.boxWidth){
33331             boxWidth = this.containerWidth
33332         }
33333         
33334         var containerWidth = this.containerWidth;
33335         
33336         var cols = Math.floor(containerWidth / boxWidth);
33337         
33338         this.cols = Math.max( cols, 1 );
33339         
33340         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33341         
33342         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33343         
33344         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33345         
33346         this.colWidth = boxWidth + avail - this.padWidth;
33347         
33348         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33349         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33350     },
33351     
33352     horizontalMeasureColumns : function()
33353     {
33354         this.getContainerWidth();
33355         
33356         var boxWidth = this.boxWidth;
33357         
33358         if(this.containerWidth < boxWidth){
33359             boxWidth = this.containerWidth;
33360         }
33361         
33362         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33363         
33364         this.el.setHeight(boxWidth);
33365         
33366     },
33367     
33368     getContainerWidth : function()
33369     {
33370         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33371     },
33372     
33373     layoutItems : function( isInstant )
33374     {
33375         Roo.log(this.bricks);
33376         
33377         var items = Roo.apply([], this.bricks);
33378         
33379         if(this.isHorizontal){
33380             this._horizontalLayoutItems( items , isInstant );
33381             return;
33382         }
33383         
33384 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33385 //            this._verticalAlternativeLayoutItems( items , isInstant );
33386 //            return;
33387 //        }
33388         
33389         this._verticalLayoutItems( items , isInstant );
33390         
33391     },
33392     
33393     _verticalLayoutItems : function ( items , isInstant)
33394     {
33395         if ( !items || !items.length ) {
33396             return;
33397         }
33398         
33399         var standard = [
33400             ['xs', 'xs', 'xs', 'tall'],
33401             ['xs', 'xs', 'tall'],
33402             ['xs', 'xs', 'sm'],
33403             ['xs', 'xs', 'xs'],
33404             ['xs', 'tall'],
33405             ['xs', 'sm'],
33406             ['xs', 'xs'],
33407             ['xs'],
33408             
33409             ['sm', 'xs', 'xs'],
33410             ['sm', 'xs'],
33411             ['sm'],
33412             
33413             ['tall', 'xs', 'xs', 'xs'],
33414             ['tall', 'xs', 'xs'],
33415             ['tall', 'xs'],
33416             ['tall']
33417             
33418         ];
33419         
33420         var queue = [];
33421         
33422         var boxes = [];
33423         
33424         var box = [];
33425         
33426         Roo.each(items, function(item, k){
33427             
33428             switch (item.size) {
33429                 // these layouts take up a full box,
33430                 case 'md' :
33431                 case 'md-left' :
33432                 case 'md-right' :
33433                 case 'wide' :
33434                     
33435                     if(box.length){
33436                         boxes.push(box);
33437                         box = [];
33438                     }
33439                     
33440                     boxes.push([item]);
33441                     
33442                     break;
33443                     
33444                 case 'xs' :
33445                 case 'sm' :
33446                 case 'tall' :
33447                     
33448                     box.push(item);
33449                     
33450                     break;
33451                 default :
33452                     break;
33453                     
33454             }
33455             
33456         }, this);
33457         
33458         if(box.length){
33459             boxes.push(box);
33460             box = [];
33461         }
33462         
33463         var filterPattern = function(box, length)
33464         {
33465             if(!box.length){
33466                 return;
33467             }
33468             
33469             var match = false;
33470             
33471             var pattern = box.slice(0, length);
33472             
33473             var format = [];
33474             
33475             Roo.each(pattern, function(i){
33476                 format.push(i.size);
33477             }, this);
33478             
33479             Roo.each(standard, function(s){
33480                 
33481                 if(String(s) != String(format)){
33482                     return;
33483                 }
33484                 
33485                 match = true;
33486                 return false;
33487                 
33488             }, this);
33489             
33490             if(!match && length == 1){
33491                 return;
33492             }
33493             
33494             if(!match){
33495                 filterPattern(box, length - 1);
33496                 return;
33497             }
33498                 
33499             queue.push(pattern);
33500
33501             box = box.slice(length, box.length);
33502
33503             filterPattern(box, 4);
33504
33505             return;
33506             
33507         }
33508         
33509         Roo.each(boxes, function(box, k){
33510             
33511             if(!box.length){
33512                 return;
33513             }
33514             
33515             if(box.length == 1){
33516                 queue.push(box);
33517                 return;
33518             }
33519             
33520             filterPattern(box, 4);
33521             
33522         }, this);
33523         
33524         this._processVerticalLayoutQueue( queue, isInstant );
33525         
33526     },
33527     
33528 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33529 //    {
33530 //        if ( !items || !items.length ) {
33531 //            return;
33532 //        }
33533 //
33534 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33535 //        
33536 //    },
33537     
33538     _horizontalLayoutItems : function ( items , isInstant)
33539     {
33540         if ( !items || !items.length || items.length < 3) {
33541             return;
33542         }
33543         
33544         items.reverse();
33545         
33546         var eItems = items.slice(0, 3);
33547         
33548         items = items.slice(3, items.length);
33549         
33550         var standard = [
33551             ['xs', 'xs', 'xs', 'wide'],
33552             ['xs', 'xs', 'wide'],
33553             ['xs', 'xs', 'sm'],
33554             ['xs', 'xs', 'xs'],
33555             ['xs', 'wide'],
33556             ['xs', 'sm'],
33557             ['xs', 'xs'],
33558             ['xs'],
33559             
33560             ['sm', 'xs', 'xs'],
33561             ['sm', 'xs'],
33562             ['sm'],
33563             
33564             ['wide', 'xs', 'xs', 'xs'],
33565             ['wide', 'xs', 'xs'],
33566             ['wide', 'xs'],
33567             ['wide'],
33568             
33569             ['wide-thin']
33570         ];
33571         
33572         var queue = [];
33573         
33574         var boxes = [];
33575         
33576         var box = [];
33577         
33578         Roo.each(items, function(item, k){
33579             
33580             switch (item.size) {
33581                 case 'md' :
33582                 case 'md-left' :
33583                 case 'md-right' :
33584                 case 'tall' :
33585                     
33586                     if(box.length){
33587                         boxes.push(box);
33588                         box = [];
33589                     }
33590                     
33591                     boxes.push([item]);
33592                     
33593                     break;
33594                     
33595                 case 'xs' :
33596                 case 'sm' :
33597                 case 'wide' :
33598                 case 'wide-thin' :
33599                     
33600                     box.push(item);
33601                     
33602                     break;
33603                 default :
33604                     break;
33605                     
33606             }
33607             
33608         }, this);
33609         
33610         if(box.length){
33611             boxes.push(box);
33612             box = [];
33613         }
33614         
33615         var filterPattern = function(box, length)
33616         {
33617             if(!box.length){
33618                 return;
33619             }
33620             
33621             var match = false;
33622             
33623             var pattern = box.slice(0, length);
33624             
33625             var format = [];
33626             
33627             Roo.each(pattern, function(i){
33628                 format.push(i.size);
33629             }, this);
33630             
33631             Roo.each(standard, function(s){
33632                 
33633                 if(String(s) != String(format)){
33634                     return;
33635                 }
33636                 
33637                 match = true;
33638                 return false;
33639                 
33640             }, this);
33641             
33642             if(!match && length == 1){
33643                 return;
33644             }
33645             
33646             if(!match){
33647                 filterPattern(box, length - 1);
33648                 return;
33649             }
33650                 
33651             queue.push(pattern);
33652
33653             box = box.slice(length, box.length);
33654
33655             filterPattern(box, 4);
33656
33657             return;
33658             
33659         }
33660         
33661         Roo.each(boxes, function(box, k){
33662             
33663             if(!box.length){
33664                 return;
33665             }
33666             
33667             if(box.length == 1){
33668                 queue.push(box);
33669                 return;
33670             }
33671             
33672             filterPattern(box, 4);
33673             
33674         }, this);
33675         
33676         
33677         var prune = [];
33678         
33679         var pos = this.el.getBox(true);
33680         
33681         var minX = pos.x;
33682         
33683         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33684         
33685         var hit_end = false;
33686         
33687         Roo.each(queue, function(box){
33688             
33689             if(hit_end){
33690                 
33691                 Roo.each(box, function(b){
33692                 
33693                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33694                     b.el.hide();
33695
33696                 }, this);
33697
33698                 return;
33699             }
33700             
33701             var mx = 0;
33702             
33703             Roo.each(box, function(b){
33704                 
33705                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33706                 b.el.show();
33707
33708                 mx = Math.max(mx, b.x);
33709                 
33710             }, this);
33711             
33712             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33713             
33714             if(maxX < minX){
33715                 
33716                 Roo.each(box, function(b){
33717                 
33718                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33719                     b.el.hide();
33720                     
33721                 }, this);
33722                 
33723                 hit_end = true;
33724                 
33725                 return;
33726             }
33727             
33728             prune.push(box);
33729             
33730         }, this);
33731         
33732         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33733     },
33734     
33735     /** Sets position of item in DOM
33736     * @param {Element} item
33737     * @param {Number} x - horizontal position
33738     * @param {Number} y - vertical position
33739     * @param {Boolean} isInstant - disables transitions
33740     */
33741     _processVerticalLayoutQueue : function( queue, isInstant )
33742     {
33743         var pos = this.el.getBox(true);
33744         var x = pos.x;
33745         var y = pos.y;
33746         var maxY = [];
33747         
33748         for (var i = 0; i < this.cols; i++){
33749             maxY[i] = pos.y;
33750         }
33751         
33752         Roo.each(queue, function(box, k){
33753             
33754             var col = k % this.cols;
33755             
33756             Roo.each(box, function(b,kk){
33757                 
33758                 b.el.position('absolute');
33759                 
33760                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33761                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33762                 
33763                 if(b.size == 'md-left' || b.size == 'md-right'){
33764                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33765                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33766                 }
33767                 
33768                 b.el.setWidth(width);
33769                 b.el.setHeight(height);
33770                 // iframe?
33771                 b.el.select('iframe',true).setSize(width,height);
33772                 
33773             }, this);
33774             
33775             for (var i = 0; i < this.cols; i++){
33776                 
33777                 if(maxY[i] < maxY[col]){
33778                     col = i;
33779                     continue;
33780                 }
33781                 
33782                 col = Math.min(col, i);
33783                 
33784             }
33785             
33786             x = pos.x + col * (this.colWidth + this.padWidth);
33787             
33788             y = maxY[col];
33789             
33790             var positions = [];
33791             
33792             switch (box.length){
33793                 case 1 :
33794                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33795                     break;
33796                 case 2 :
33797                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33798                     break;
33799                 case 3 :
33800                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33801                     break;
33802                 case 4 :
33803                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33804                     break;
33805                 default :
33806                     break;
33807             }
33808             
33809             Roo.each(box, function(b,kk){
33810                 
33811                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33812                 
33813                 var sz = b.el.getSize();
33814                 
33815                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33816                 
33817             }, this);
33818             
33819         }, this);
33820         
33821         var mY = 0;
33822         
33823         for (var i = 0; i < this.cols; i++){
33824             mY = Math.max(mY, maxY[i]);
33825         }
33826         
33827         this.el.setHeight(mY - pos.y);
33828         
33829     },
33830     
33831 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33832 //    {
33833 //        var pos = this.el.getBox(true);
33834 //        var x = pos.x;
33835 //        var y = pos.y;
33836 //        var maxX = pos.right;
33837 //        
33838 //        var maxHeight = 0;
33839 //        
33840 //        Roo.each(items, function(item, k){
33841 //            
33842 //            var c = k % 2;
33843 //            
33844 //            item.el.position('absolute');
33845 //                
33846 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33847 //
33848 //            item.el.setWidth(width);
33849 //
33850 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33851 //
33852 //            item.el.setHeight(height);
33853 //            
33854 //            if(c == 0){
33855 //                item.el.setXY([x, y], isInstant ? false : true);
33856 //            } else {
33857 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33858 //            }
33859 //            
33860 //            y = y + height + this.alternativePadWidth;
33861 //            
33862 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33863 //            
33864 //        }, this);
33865 //        
33866 //        this.el.setHeight(maxHeight);
33867 //        
33868 //    },
33869     
33870     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33871     {
33872         var pos = this.el.getBox(true);
33873         
33874         var minX = pos.x;
33875         var minY = pos.y;
33876         
33877         var maxX = pos.right;
33878         
33879         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33880         
33881         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33882         
33883         Roo.each(queue, function(box, k){
33884             
33885             Roo.each(box, function(b, kk){
33886                 
33887                 b.el.position('absolute');
33888                 
33889                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33890                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33891                 
33892                 if(b.size == 'md-left' || b.size == 'md-right'){
33893                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33894                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33895                 }
33896                 
33897                 b.el.setWidth(width);
33898                 b.el.setHeight(height);
33899                 
33900             }, this);
33901             
33902             if(!box.length){
33903                 return;
33904             }
33905             
33906             var positions = [];
33907             
33908             switch (box.length){
33909                 case 1 :
33910                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33911                     break;
33912                 case 2 :
33913                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33914                     break;
33915                 case 3 :
33916                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33917                     break;
33918                 case 4 :
33919                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33920                     break;
33921                 default :
33922                     break;
33923             }
33924             
33925             Roo.each(box, function(b,kk){
33926                 
33927                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33928                 
33929                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33930                 
33931             }, this);
33932             
33933         }, this);
33934         
33935     },
33936     
33937     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33938     {
33939         Roo.each(eItems, function(b,k){
33940             
33941             b.size = (k == 0) ? 'sm' : 'xs';
33942             b.x = (k == 0) ? 2 : 1;
33943             b.y = (k == 0) ? 2 : 1;
33944             
33945             b.el.position('absolute');
33946             
33947             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33948                 
33949             b.el.setWidth(width);
33950             
33951             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33952             
33953             b.el.setHeight(height);
33954             
33955         }, this);
33956
33957         var positions = [];
33958         
33959         positions.push({
33960             x : maxX - this.unitWidth * 2 - this.gutter,
33961             y : minY
33962         });
33963         
33964         positions.push({
33965             x : maxX - this.unitWidth,
33966             y : minY + (this.unitWidth + this.gutter) * 2
33967         });
33968         
33969         positions.push({
33970             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33971             y : minY
33972         });
33973         
33974         Roo.each(eItems, function(b,k){
33975             
33976             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33977
33978         }, this);
33979         
33980     },
33981     
33982     getVerticalOneBoxColPositions : function(x, y, box)
33983     {
33984         var pos = [];
33985         
33986         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33987         
33988         if(box[0].size == 'md-left'){
33989             rand = 0;
33990         }
33991         
33992         if(box[0].size == 'md-right'){
33993             rand = 1;
33994         }
33995         
33996         pos.push({
33997             x : x + (this.unitWidth + this.gutter) * rand,
33998             y : y
33999         });
34000         
34001         return pos;
34002     },
34003     
34004     getVerticalTwoBoxColPositions : function(x, y, box)
34005     {
34006         var pos = [];
34007         
34008         if(box[0].size == 'xs'){
34009             
34010             pos.push({
34011                 x : x,
34012                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34013             });
34014
34015             pos.push({
34016                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34017                 y : y
34018             });
34019             
34020             return pos;
34021             
34022         }
34023         
34024         pos.push({
34025             x : x,
34026             y : y
34027         });
34028
34029         pos.push({
34030             x : x + (this.unitWidth + this.gutter) * 2,
34031             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34032         });
34033         
34034         return pos;
34035         
34036     },
34037     
34038     getVerticalThreeBoxColPositions : function(x, y, box)
34039     {
34040         var pos = [];
34041         
34042         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34043             
34044             pos.push({
34045                 x : x,
34046                 y : y
34047             });
34048
34049             pos.push({
34050                 x : x + (this.unitWidth + this.gutter) * 1,
34051                 y : y
34052             });
34053             
34054             pos.push({
34055                 x : x + (this.unitWidth + this.gutter) * 2,
34056                 y : y
34057             });
34058             
34059             return pos;
34060             
34061         }
34062         
34063         if(box[0].size == 'xs' && box[1].size == 'xs'){
34064             
34065             pos.push({
34066                 x : x,
34067                 y : y
34068             });
34069
34070             pos.push({
34071                 x : x,
34072                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34073             });
34074             
34075             pos.push({
34076                 x : x + (this.unitWidth + this.gutter) * 1,
34077                 y : y
34078             });
34079             
34080             return pos;
34081             
34082         }
34083         
34084         pos.push({
34085             x : x,
34086             y : y
34087         });
34088
34089         pos.push({
34090             x : x + (this.unitWidth + this.gutter) * 2,
34091             y : y
34092         });
34093
34094         pos.push({
34095             x : x + (this.unitWidth + this.gutter) * 2,
34096             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34097         });
34098             
34099         return pos;
34100         
34101     },
34102     
34103     getVerticalFourBoxColPositions : function(x, y, box)
34104     {
34105         var pos = [];
34106         
34107         if(box[0].size == 'xs'){
34108             
34109             pos.push({
34110                 x : x,
34111                 y : y
34112             });
34113
34114             pos.push({
34115                 x : x,
34116                 y : y + (this.unitHeight + this.gutter) * 1
34117             });
34118             
34119             pos.push({
34120                 x : x,
34121                 y : y + (this.unitHeight + this.gutter) * 2
34122             });
34123             
34124             pos.push({
34125                 x : x + (this.unitWidth + this.gutter) * 1,
34126                 y : y
34127             });
34128             
34129             return pos;
34130             
34131         }
34132         
34133         pos.push({
34134             x : x,
34135             y : y
34136         });
34137
34138         pos.push({
34139             x : x + (this.unitWidth + this.gutter) * 2,
34140             y : y
34141         });
34142
34143         pos.push({
34144             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34145             y : y + (this.unitHeight + this.gutter) * 1
34146         });
34147
34148         pos.push({
34149             x : x + (this.unitWidth + this.gutter) * 2,
34150             y : y + (this.unitWidth + this.gutter) * 2
34151         });
34152
34153         return pos;
34154         
34155     },
34156     
34157     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34158     {
34159         var pos = [];
34160         
34161         if(box[0].size == 'md-left'){
34162             pos.push({
34163                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34164                 y : minY
34165             });
34166             
34167             return pos;
34168         }
34169         
34170         if(box[0].size == 'md-right'){
34171             pos.push({
34172                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34173                 y : minY + (this.unitWidth + this.gutter) * 1
34174             });
34175             
34176             return pos;
34177         }
34178         
34179         var rand = Math.floor(Math.random() * (4 - box[0].y));
34180         
34181         pos.push({
34182             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34183             y : minY + (this.unitWidth + this.gutter) * rand
34184         });
34185         
34186         return pos;
34187         
34188     },
34189     
34190     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34191     {
34192         var pos = [];
34193         
34194         if(box[0].size == 'xs'){
34195             
34196             pos.push({
34197                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34198                 y : minY
34199             });
34200
34201             pos.push({
34202                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34203                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34204             });
34205             
34206             return pos;
34207             
34208         }
34209         
34210         pos.push({
34211             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34212             y : minY
34213         });
34214
34215         pos.push({
34216             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34217             y : minY + (this.unitWidth + this.gutter) * 2
34218         });
34219         
34220         return pos;
34221         
34222     },
34223     
34224     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34225     {
34226         var pos = [];
34227         
34228         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34229             
34230             pos.push({
34231                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34232                 y : minY
34233             });
34234
34235             pos.push({
34236                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34237                 y : minY + (this.unitWidth + this.gutter) * 1
34238             });
34239             
34240             pos.push({
34241                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34242                 y : minY + (this.unitWidth + this.gutter) * 2
34243             });
34244             
34245             return pos;
34246             
34247         }
34248         
34249         if(box[0].size == 'xs' && box[1].size == 'xs'){
34250             
34251             pos.push({
34252                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34253                 y : minY
34254             });
34255
34256             pos.push({
34257                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34258                 y : minY
34259             });
34260             
34261             pos.push({
34262                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34263                 y : minY + (this.unitWidth + this.gutter) * 1
34264             });
34265             
34266             return pos;
34267             
34268         }
34269         
34270         pos.push({
34271             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34272             y : minY
34273         });
34274
34275         pos.push({
34276             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34277             y : minY + (this.unitWidth + this.gutter) * 2
34278         });
34279
34280         pos.push({
34281             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34282             y : minY + (this.unitWidth + this.gutter) * 2
34283         });
34284             
34285         return pos;
34286         
34287     },
34288     
34289     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34290     {
34291         var pos = [];
34292         
34293         if(box[0].size == 'xs'){
34294             
34295             pos.push({
34296                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34297                 y : minY
34298             });
34299
34300             pos.push({
34301                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34302                 y : minY
34303             });
34304             
34305             pos.push({
34306                 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),
34307                 y : minY
34308             });
34309             
34310             pos.push({
34311                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34312                 y : minY + (this.unitWidth + this.gutter) * 1
34313             });
34314             
34315             return pos;
34316             
34317         }
34318         
34319         pos.push({
34320             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34321             y : minY
34322         });
34323         
34324         pos.push({
34325             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34326             y : minY + (this.unitWidth + this.gutter) * 2
34327         });
34328         
34329         pos.push({
34330             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34331             y : minY + (this.unitWidth + this.gutter) * 2
34332         });
34333         
34334         pos.push({
34335             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),
34336             y : minY + (this.unitWidth + this.gutter) * 2
34337         });
34338
34339         return pos;
34340         
34341     },
34342     
34343     /**
34344     * remove a Masonry Brick
34345     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34346     */
34347     removeBrick : function(brick_id)
34348     {
34349         if (!brick_id) {
34350             return;
34351         }
34352         
34353         for (var i = 0; i<this.bricks.length; i++) {
34354             if (this.bricks[i].id == brick_id) {
34355                 this.bricks.splice(i,1);
34356                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34357                 this.initial();
34358             }
34359         }
34360     },
34361     
34362     /**
34363     * adds a Masonry Brick
34364     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34365     */
34366     addBrick : function(cfg)
34367     {
34368         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34369         //this.register(cn);
34370         cn.parentId = this.id;
34371         cn.render(this.el);
34372         return cn;
34373     },
34374     
34375     /**
34376     * register a Masonry Brick
34377     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34378     */
34379     
34380     register : function(brick)
34381     {
34382         this.bricks.push(brick);
34383         brick.masonryId = this.id;
34384     },
34385     
34386     /**
34387     * clear all the Masonry Brick
34388     */
34389     clearAll : function()
34390     {
34391         this.bricks = [];
34392         //this.getChildContainer().dom.innerHTML = "";
34393         this.el.dom.innerHTML = '';
34394     },
34395     
34396     getSelected : function()
34397     {
34398         if (!this.selectedBrick) {
34399             return false;
34400         }
34401         
34402         return this.selectedBrick;
34403     }
34404 });
34405
34406 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34407     
34408     groups: {},
34409      /**
34410     * register a Masonry Layout
34411     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34412     */
34413     
34414     register : function(layout)
34415     {
34416         this.groups[layout.id] = layout;
34417     },
34418     /**
34419     * fetch a  Masonry Layout based on the masonry layout ID
34420     * @param {string} the masonry layout to add
34421     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34422     */
34423     
34424     get: function(layout_id) {
34425         if (typeof(this.groups[layout_id]) == 'undefined') {
34426             return false;
34427         }
34428         return this.groups[layout_id] ;
34429     }
34430     
34431     
34432     
34433 });
34434
34435  
34436
34437  /**
34438  *
34439  * This is based on 
34440  * http://masonry.desandro.com
34441  *
34442  * The idea is to render all the bricks based on vertical width...
34443  *
34444  * The original code extends 'outlayer' - we might need to use that....
34445  * 
34446  */
34447
34448
34449 /**
34450  * @class Roo.bootstrap.LayoutMasonryAuto
34451  * @extends Roo.bootstrap.Component
34452  * Bootstrap Layout Masonry class
34453  * 
34454  * @constructor
34455  * Create a new Element
34456  * @param {Object} config The config object
34457  */
34458
34459 Roo.bootstrap.LayoutMasonryAuto = function(config){
34460     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34461 };
34462
34463 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34464     
34465       /**
34466      * @cfg {Boolean} isFitWidth  - resize the width..
34467      */   
34468     isFitWidth : false,  // options..
34469     /**
34470      * @cfg {Boolean} isOriginLeft = left align?
34471      */   
34472     isOriginLeft : true,
34473     /**
34474      * @cfg {Boolean} isOriginTop = top align?
34475      */   
34476     isOriginTop : false,
34477     /**
34478      * @cfg {Boolean} isLayoutInstant = no animation?
34479      */   
34480     isLayoutInstant : false, // needed?
34481     /**
34482      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34483      */   
34484     isResizingContainer : true,
34485     /**
34486      * @cfg {Number} columnWidth  width of the columns 
34487      */   
34488     
34489     columnWidth : 0,
34490     
34491     /**
34492      * @cfg {Number} maxCols maximum number of columns
34493      */   
34494     
34495     maxCols: 0,
34496     /**
34497      * @cfg {Number} padHeight padding below box..
34498      */   
34499     
34500     padHeight : 10, 
34501     
34502     /**
34503      * @cfg {Boolean} isAutoInitial defalut true
34504      */   
34505     
34506     isAutoInitial : true, 
34507     
34508     // private?
34509     gutter : 0,
34510     
34511     containerWidth: 0,
34512     initialColumnWidth : 0,
34513     currentSize : null,
34514     
34515     colYs : null, // array.
34516     maxY : 0,
34517     padWidth: 10,
34518     
34519     
34520     tag: 'div',
34521     cls: '',
34522     bricks: null, //CompositeElement
34523     cols : 0, // array?
34524     // element : null, // wrapped now this.el
34525     _isLayoutInited : null, 
34526     
34527     
34528     getAutoCreate : function(){
34529         
34530         var cfg = {
34531             tag: this.tag,
34532             cls: 'blog-masonary-wrapper ' + this.cls,
34533             cn : {
34534                 cls : 'mas-boxes masonary'
34535             }
34536         };
34537         
34538         return cfg;
34539     },
34540     
34541     getChildContainer: function( )
34542     {
34543         if (this.boxesEl) {
34544             return this.boxesEl;
34545         }
34546         
34547         this.boxesEl = this.el.select('.mas-boxes').first();
34548         
34549         return this.boxesEl;
34550     },
34551     
34552     
34553     initEvents : function()
34554     {
34555         var _this = this;
34556         
34557         if(this.isAutoInitial){
34558             Roo.log('hook children rendered');
34559             this.on('childrenrendered', function() {
34560                 Roo.log('children rendered');
34561                 _this.initial();
34562             } ,this);
34563         }
34564         
34565     },
34566     
34567     initial : function()
34568     {
34569         this.reloadItems();
34570
34571         this.currentSize = this.el.getBox(true);
34572
34573         /// was window resize... - let's see if this works..
34574         Roo.EventManager.onWindowResize(this.resize, this); 
34575
34576         if(!this.isAutoInitial){
34577             this.layout();
34578             return;
34579         }
34580         
34581         this.layout.defer(500,this);
34582     },
34583     
34584     reloadItems: function()
34585     {
34586         this.bricks = this.el.select('.masonry-brick', true);
34587         
34588         this.bricks.each(function(b) {
34589             //Roo.log(b.getSize());
34590             if (!b.attr('originalwidth')) {
34591                 b.attr('originalwidth',  b.getSize().width);
34592             }
34593             
34594         });
34595         
34596         Roo.log(this.bricks.elements.length);
34597     },
34598     
34599     resize : function()
34600     {
34601         Roo.log('resize');
34602         var cs = this.el.getBox(true);
34603         
34604         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34605             Roo.log("no change in with or X");
34606             return;
34607         }
34608         this.currentSize = cs;
34609         this.layout();
34610     },
34611     
34612     layout : function()
34613     {
34614          Roo.log('layout');
34615         this._resetLayout();
34616         //this._manageStamps();
34617       
34618         // don't animate first layout
34619         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34620         this.layoutItems( isInstant );
34621       
34622         // flag for initalized
34623         this._isLayoutInited = true;
34624     },
34625     
34626     layoutItems : function( isInstant )
34627     {
34628         //var items = this._getItemsForLayout( this.items );
34629         // original code supports filtering layout items.. we just ignore it..
34630         
34631         this._layoutItems( this.bricks , isInstant );
34632       
34633         this._postLayout();
34634     },
34635     _layoutItems : function ( items , isInstant)
34636     {
34637        //this.fireEvent( 'layout', this, items );
34638     
34639
34640         if ( !items || !items.elements.length ) {
34641           // no items, emit event with empty array
34642             return;
34643         }
34644
34645         var queue = [];
34646         items.each(function(item) {
34647             Roo.log("layout item");
34648             Roo.log(item);
34649             // get x/y object from method
34650             var position = this._getItemLayoutPosition( item );
34651             // enqueue
34652             position.item = item;
34653             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34654             queue.push( position );
34655         }, this);
34656       
34657         this._processLayoutQueue( queue );
34658     },
34659     /** Sets position of item in DOM
34660     * @param {Element} item
34661     * @param {Number} x - horizontal position
34662     * @param {Number} y - vertical position
34663     * @param {Boolean} isInstant - disables transitions
34664     */
34665     _processLayoutQueue : function( queue )
34666     {
34667         for ( var i=0, len = queue.length; i < len; i++ ) {
34668             var obj = queue[i];
34669             obj.item.position('absolute');
34670             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34671         }
34672     },
34673       
34674     
34675     /**
34676     * Any logic you want to do after each layout,
34677     * i.e. size the container
34678     */
34679     _postLayout : function()
34680     {
34681         this.resizeContainer();
34682     },
34683     
34684     resizeContainer : function()
34685     {
34686         if ( !this.isResizingContainer ) {
34687             return;
34688         }
34689         var size = this._getContainerSize();
34690         if ( size ) {
34691             this.el.setSize(size.width,size.height);
34692             this.boxesEl.setSize(size.width,size.height);
34693         }
34694     },
34695     
34696     
34697     
34698     _resetLayout : function()
34699     {
34700         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34701         this.colWidth = this.el.getWidth();
34702         //this.gutter = this.el.getWidth(); 
34703         
34704         this.measureColumns();
34705
34706         // reset column Y
34707         var i = this.cols;
34708         this.colYs = [];
34709         while (i--) {
34710             this.colYs.push( 0 );
34711         }
34712     
34713         this.maxY = 0;
34714     },
34715
34716     measureColumns : function()
34717     {
34718         this.getContainerWidth();
34719       // if columnWidth is 0, default to outerWidth of first item
34720         if ( !this.columnWidth ) {
34721             var firstItem = this.bricks.first();
34722             Roo.log(firstItem);
34723             this.columnWidth  = this.containerWidth;
34724             if (firstItem && firstItem.attr('originalwidth') ) {
34725                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34726             }
34727             // columnWidth fall back to item of first element
34728             Roo.log("set column width?");
34729                         this.initialColumnWidth = this.columnWidth  ;
34730
34731             // if first elem has no width, default to size of container
34732             
34733         }
34734         
34735         
34736         if (this.initialColumnWidth) {
34737             this.columnWidth = this.initialColumnWidth;
34738         }
34739         
34740         
34741             
34742         // column width is fixed at the top - however if container width get's smaller we should
34743         // reduce it...
34744         
34745         // this bit calcs how man columns..
34746             
34747         var columnWidth = this.columnWidth += this.gutter;
34748       
34749         // calculate columns
34750         var containerWidth = this.containerWidth + this.gutter;
34751         
34752         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34753         // fix rounding errors, typically with gutters
34754         var excess = columnWidth - containerWidth % columnWidth;
34755         
34756         
34757         // if overshoot is less than a pixel, round up, otherwise floor it
34758         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34759         cols = Math[ mathMethod ]( cols );
34760         this.cols = Math.max( cols, 1 );
34761         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34762         
34763          // padding positioning..
34764         var totalColWidth = this.cols * this.columnWidth;
34765         var padavail = this.containerWidth - totalColWidth;
34766         // so for 2 columns - we need 3 'pads'
34767         
34768         var padNeeded = (1+this.cols) * this.padWidth;
34769         
34770         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34771         
34772         this.columnWidth += padExtra
34773         //this.padWidth = Math.floor(padavail /  ( this.cols));
34774         
34775         // adjust colum width so that padding is fixed??
34776         
34777         // we have 3 columns ... total = width * 3
34778         // we have X left over... that should be used by 
34779         
34780         //if (this.expandC) {
34781             
34782         //}
34783         
34784         
34785         
34786     },
34787     
34788     getContainerWidth : function()
34789     {
34790        /* // container is parent if fit width
34791         var container = this.isFitWidth ? this.element.parentNode : this.element;
34792         // check that this.size and size are there
34793         // IE8 triggers resize on body size change, so they might not be
34794         
34795         var size = getSize( container );  //FIXME
34796         this.containerWidth = size && size.innerWidth; //FIXME
34797         */
34798          
34799         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34800         
34801     },
34802     
34803     _getItemLayoutPosition : function( item )  // what is item?
34804     {
34805         // we resize the item to our columnWidth..
34806       
34807         item.setWidth(this.columnWidth);
34808         item.autoBoxAdjust  = false;
34809         
34810         var sz = item.getSize();
34811  
34812         // how many columns does this brick span
34813         var remainder = this.containerWidth % this.columnWidth;
34814         
34815         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34816         // round if off by 1 pixel, otherwise use ceil
34817         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34818         colSpan = Math.min( colSpan, this.cols );
34819         
34820         // normally this should be '1' as we dont' currently allow multi width columns..
34821         
34822         var colGroup = this._getColGroup( colSpan );
34823         // get the minimum Y value from the columns
34824         var minimumY = Math.min.apply( Math, colGroup );
34825         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34826         
34827         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34828          
34829         // position the brick
34830         var position = {
34831             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34832             y: this.currentSize.y + minimumY + this.padHeight
34833         };
34834         
34835         Roo.log(position);
34836         // apply setHeight to necessary columns
34837         var setHeight = minimumY + sz.height + this.padHeight;
34838         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34839         
34840         var setSpan = this.cols + 1 - colGroup.length;
34841         for ( var i = 0; i < setSpan; i++ ) {
34842           this.colYs[ shortColIndex + i ] = setHeight ;
34843         }
34844       
34845         return position;
34846     },
34847     
34848     /**
34849      * @param {Number} colSpan - number of columns the element spans
34850      * @returns {Array} colGroup
34851      */
34852     _getColGroup : function( colSpan )
34853     {
34854         if ( colSpan < 2 ) {
34855           // if brick spans only one column, use all the column Ys
34856           return this.colYs;
34857         }
34858       
34859         var colGroup = [];
34860         // how many different places could this brick fit horizontally
34861         var groupCount = this.cols + 1 - colSpan;
34862         // for each group potential horizontal position
34863         for ( var i = 0; i < groupCount; i++ ) {
34864           // make an array of colY values for that one group
34865           var groupColYs = this.colYs.slice( i, i + colSpan );
34866           // and get the max value of the array
34867           colGroup[i] = Math.max.apply( Math, groupColYs );
34868         }
34869         return colGroup;
34870     },
34871     /*
34872     _manageStamp : function( stamp )
34873     {
34874         var stampSize =  stamp.getSize();
34875         var offset = stamp.getBox();
34876         // get the columns that this stamp affects
34877         var firstX = this.isOriginLeft ? offset.x : offset.right;
34878         var lastX = firstX + stampSize.width;
34879         var firstCol = Math.floor( firstX / this.columnWidth );
34880         firstCol = Math.max( 0, firstCol );
34881         
34882         var lastCol = Math.floor( lastX / this.columnWidth );
34883         // lastCol should not go over if multiple of columnWidth #425
34884         lastCol -= lastX % this.columnWidth ? 0 : 1;
34885         lastCol = Math.min( this.cols - 1, lastCol );
34886         
34887         // set colYs to bottom of the stamp
34888         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34889             stampSize.height;
34890             
34891         for ( var i = firstCol; i <= lastCol; i++ ) {
34892           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34893         }
34894     },
34895     */
34896     
34897     _getContainerSize : function()
34898     {
34899         this.maxY = Math.max.apply( Math, this.colYs );
34900         var size = {
34901             height: this.maxY
34902         };
34903       
34904         if ( this.isFitWidth ) {
34905             size.width = this._getContainerFitWidth();
34906         }
34907       
34908         return size;
34909     },
34910     
34911     _getContainerFitWidth : function()
34912     {
34913         var unusedCols = 0;
34914         // count unused columns
34915         var i = this.cols;
34916         while ( --i ) {
34917           if ( this.colYs[i] !== 0 ) {
34918             break;
34919           }
34920           unusedCols++;
34921         }
34922         // fit container to columns that have been used
34923         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34924     },
34925     
34926     needsResizeLayout : function()
34927     {
34928         var previousWidth = this.containerWidth;
34929         this.getContainerWidth();
34930         return previousWidth !== this.containerWidth;
34931     }
34932  
34933 });
34934
34935  
34936
34937  /*
34938  * - LGPL
34939  *
34940  * element
34941  * 
34942  */
34943
34944 /**
34945  * @class Roo.bootstrap.MasonryBrick
34946  * @extends Roo.bootstrap.Component
34947  * Bootstrap MasonryBrick class
34948  * 
34949  * @constructor
34950  * Create a new MasonryBrick
34951  * @param {Object} config The config object
34952  */
34953
34954 Roo.bootstrap.MasonryBrick = function(config){
34955     
34956     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34957     
34958     Roo.bootstrap.MasonryBrick.register(this);
34959     
34960     this.addEvents({
34961         // raw events
34962         /**
34963          * @event click
34964          * When a MasonryBrick is clcik
34965          * @param {Roo.bootstrap.MasonryBrick} this
34966          * @param {Roo.EventObject} e
34967          */
34968         "click" : true
34969     });
34970 };
34971
34972 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34973     
34974     /**
34975      * @cfg {String} title
34976      */   
34977     title : '',
34978     /**
34979      * @cfg {String} html
34980      */   
34981     html : '',
34982     /**
34983      * @cfg {String} bgimage
34984      */   
34985     bgimage : '',
34986     /**
34987      * @cfg {String} videourl
34988      */   
34989     videourl : '',
34990     /**
34991      * @cfg {String} cls
34992      */   
34993     cls : '',
34994     /**
34995      * @cfg {String} href
34996      */   
34997     href : '',
34998     /**
34999      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35000      */   
35001     size : 'xs',
35002     
35003     /**
35004      * @cfg {String} placetitle (center|bottom)
35005      */   
35006     placetitle : '',
35007     
35008     /**
35009      * @cfg {Boolean} isFitContainer defalut true
35010      */   
35011     isFitContainer : true, 
35012     
35013     /**
35014      * @cfg {Boolean} preventDefault defalut false
35015      */   
35016     preventDefault : false, 
35017     
35018     /**
35019      * @cfg {Boolean} inverse defalut false
35020      */   
35021     maskInverse : false, 
35022     
35023     getAutoCreate : function()
35024     {
35025         if(!this.isFitContainer){
35026             return this.getSplitAutoCreate();
35027         }
35028         
35029         var cls = 'masonry-brick masonry-brick-full';
35030         
35031         if(this.href.length){
35032             cls += ' masonry-brick-link';
35033         }
35034         
35035         if(this.bgimage.length){
35036             cls += ' masonry-brick-image';
35037         }
35038         
35039         if(this.maskInverse){
35040             cls += ' mask-inverse';
35041         }
35042         
35043         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35044             cls += ' enable-mask';
35045         }
35046         
35047         if(this.size){
35048             cls += ' masonry-' + this.size + '-brick';
35049         }
35050         
35051         if(this.placetitle.length){
35052             
35053             switch (this.placetitle) {
35054                 case 'center' :
35055                     cls += ' masonry-center-title';
35056                     break;
35057                 case 'bottom' :
35058                     cls += ' masonry-bottom-title';
35059                     break;
35060                 default:
35061                     break;
35062             }
35063             
35064         } else {
35065             if(!this.html.length && !this.bgimage.length){
35066                 cls += ' masonry-center-title';
35067             }
35068
35069             if(!this.html.length && this.bgimage.length){
35070                 cls += ' masonry-bottom-title';
35071             }
35072         }
35073         
35074         if(this.cls){
35075             cls += ' ' + this.cls;
35076         }
35077         
35078         var cfg = {
35079             tag: (this.href.length) ? 'a' : 'div',
35080             cls: cls,
35081             cn: [
35082                 {
35083                     tag: 'div',
35084                     cls: 'masonry-brick-mask'
35085                 },
35086                 {
35087                     tag: 'div',
35088                     cls: 'masonry-brick-paragraph',
35089                     cn: []
35090                 }
35091             ]
35092         };
35093         
35094         if(this.href.length){
35095             cfg.href = this.href;
35096         }
35097         
35098         var cn = cfg.cn[1].cn;
35099         
35100         if(this.title.length){
35101             cn.push({
35102                 tag: 'h4',
35103                 cls: 'masonry-brick-title',
35104                 html: this.title
35105             });
35106         }
35107         
35108         if(this.html.length){
35109             cn.push({
35110                 tag: 'p',
35111                 cls: 'masonry-brick-text',
35112                 html: this.html
35113             });
35114         }
35115         
35116         if (!this.title.length && !this.html.length) {
35117             cfg.cn[1].cls += ' hide';
35118         }
35119         
35120         if(this.bgimage.length){
35121             cfg.cn.push({
35122                 tag: 'img',
35123                 cls: 'masonry-brick-image-view',
35124                 src: this.bgimage
35125             });
35126         }
35127         
35128         if(this.videourl.length){
35129             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35130             // youtube support only?
35131             cfg.cn.push({
35132                 tag: 'iframe',
35133                 cls: 'masonry-brick-image-view',
35134                 src: vurl,
35135                 frameborder : 0,
35136                 allowfullscreen : true
35137             });
35138         }
35139         
35140         return cfg;
35141         
35142     },
35143     
35144     getSplitAutoCreate : function()
35145     {
35146         var cls = 'masonry-brick masonry-brick-split';
35147         
35148         if(this.href.length){
35149             cls += ' masonry-brick-link';
35150         }
35151         
35152         if(this.bgimage.length){
35153             cls += ' masonry-brick-image';
35154         }
35155         
35156         if(this.size){
35157             cls += ' masonry-' + this.size + '-brick';
35158         }
35159         
35160         switch (this.placetitle) {
35161             case 'center' :
35162                 cls += ' masonry-center-title';
35163                 break;
35164             case 'bottom' :
35165                 cls += ' masonry-bottom-title';
35166                 break;
35167             default:
35168                 if(!this.bgimage.length){
35169                     cls += ' masonry-center-title';
35170                 }
35171
35172                 if(this.bgimage.length){
35173                     cls += ' masonry-bottom-title';
35174                 }
35175                 break;
35176         }
35177         
35178         if(this.cls){
35179             cls += ' ' + this.cls;
35180         }
35181         
35182         var cfg = {
35183             tag: (this.href.length) ? 'a' : 'div',
35184             cls: cls,
35185             cn: [
35186                 {
35187                     tag: 'div',
35188                     cls: 'masonry-brick-split-head',
35189                     cn: [
35190                         {
35191                             tag: 'div',
35192                             cls: 'masonry-brick-paragraph',
35193                             cn: []
35194                         }
35195                     ]
35196                 },
35197                 {
35198                     tag: 'div',
35199                     cls: 'masonry-brick-split-body',
35200                     cn: []
35201                 }
35202             ]
35203         };
35204         
35205         if(this.href.length){
35206             cfg.href = this.href;
35207         }
35208         
35209         if(this.title.length){
35210             cfg.cn[0].cn[0].cn.push({
35211                 tag: 'h4',
35212                 cls: 'masonry-brick-title',
35213                 html: this.title
35214             });
35215         }
35216         
35217         if(this.html.length){
35218             cfg.cn[1].cn.push({
35219                 tag: 'p',
35220                 cls: 'masonry-brick-text',
35221                 html: this.html
35222             });
35223         }
35224
35225         if(this.bgimage.length){
35226             cfg.cn[0].cn.push({
35227                 tag: 'img',
35228                 cls: 'masonry-brick-image-view',
35229                 src: this.bgimage
35230             });
35231         }
35232         
35233         if(this.videourl.length){
35234             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35235             // youtube support only?
35236             cfg.cn[0].cn.cn.push({
35237                 tag: 'iframe',
35238                 cls: 'masonry-brick-image-view',
35239                 src: vurl,
35240                 frameborder : 0,
35241                 allowfullscreen : true
35242             });
35243         }
35244         
35245         return cfg;
35246     },
35247     
35248     initEvents: function() 
35249     {
35250         switch (this.size) {
35251             case 'xs' :
35252                 this.x = 1;
35253                 this.y = 1;
35254                 break;
35255             case 'sm' :
35256                 this.x = 2;
35257                 this.y = 2;
35258                 break;
35259             case 'md' :
35260             case 'md-left' :
35261             case 'md-right' :
35262                 this.x = 3;
35263                 this.y = 3;
35264                 break;
35265             case 'tall' :
35266                 this.x = 2;
35267                 this.y = 3;
35268                 break;
35269             case 'wide' :
35270                 this.x = 3;
35271                 this.y = 2;
35272                 break;
35273             case 'wide-thin' :
35274                 this.x = 3;
35275                 this.y = 1;
35276                 break;
35277                         
35278             default :
35279                 break;
35280         }
35281         
35282         if(Roo.isTouch){
35283             this.el.on('touchstart', this.onTouchStart, this);
35284             this.el.on('touchmove', this.onTouchMove, this);
35285             this.el.on('touchend', this.onTouchEnd, this);
35286             this.el.on('contextmenu', this.onContextMenu, this);
35287         } else {
35288             this.el.on('mouseenter'  ,this.enter, this);
35289             this.el.on('mouseleave', this.leave, this);
35290             this.el.on('click', this.onClick, this);
35291         }
35292         
35293         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35294             this.parent().bricks.push(this);   
35295         }
35296         
35297     },
35298     
35299     onClick: function(e, el)
35300     {
35301         var time = this.endTimer - this.startTimer;
35302         // Roo.log(e.preventDefault());
35303         if(Roo.isTouch){
35304             if(time > 1000){
35305                 e.preventDefault();
35306                 return;
35307             }
35308         }
35309         
35310         if(!this.preventDefault){
35311             return;
35312         }
35313         
35314         e.preventDefault();
35315         
35316         if (this.activeClass != '') {
35317             this.selectBrick();
35318         }
35319         
35320         this.fireEvent('click', this, e);
35321     },
35322     
35323     enter: function(e, el)
35324     {
35325         e.preventDefault();
35326         
35327         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35328             return;
35329         }
35330         
35331         if(this.bgimage.length && this.html.length){
35332             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35333         }
35334     },
35335     
35336     leave: function(e, el)
35337     {
35338         e.preventDefault();
35339         
35340         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35341             return;
35342         }
35343         
35344         if(this.bgimage.length && this.html.length){
35345             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35346         }
35347     },
35348     
35349     onTouchStart: function(e, el)
35350     {
35351 //        e.preventDefault();
35352         
35353         this.touchmoved = false;
35354         
35355         if(!this.isFitContainer){
35356             return;
35357         }
35358         
35359         if(!this.bgimage.length || !this.html.length){
35360             return;
35361         }
35362         
35363         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35364         
35365         this.timer = new Date().getTime();
35366         
35367     },
35368     
35369     onTouchMove: function(e, el)
35370     {
35371         this.touchmoved = true;
35372     },
35373     
35374     onContextMenu : function(e,el)
35375     {
35376         e.preventDefault();
35377         e.stopPropagation();
35378         return false;
35379     },
35380     
35381     onTouchEnd: function(e, el)
35382     {
35383 //        e.preventDefault();
35384         
35385         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35386         
35387             this.leave(e,el);
35388             
35389             return;
35390         }
35391         
35392         if(!this.bgimage.length || !this.html.length){
35393             
35394             if(this.href.length){
35395                 window.location.href = this.href;
35396             }
35397             
35398             return;
35399         }
35400         
35401         if(!this.isFitContainer){
35402             return;
35403         }
35404         
35405         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35406         
35407         window.location.href = this.href;
35408     },
35409     
35410     //selection on single brick only
35411     selectBrick : function() {
35412         
35413         if (!this.parentId) {
35414             return;
35415         }
35416         
35417         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35418         var index = m.selectedBrick.indexOf(this.id);
35419         
35420         if ( index > -1) {
35421             m.selectedBrick.splice(index,1);
35422             this.el.removeClass(this.activeClass);
35423             return;
35424         }
35425         
35426         for(var i = 0; i < m.selectedBrick.length; i++) {
35427             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35428             b.el.removeClass(b.activeClass);
35429         }
35430         
35431         m.selectedBrick = [];
35432         
35433         m.selectedBrick.push(this.id);
35434         this.el.addClass(this.activeClass);
35435         return;
35436     },
35437     
35438     isSelected : function(){
35439         return this.el.hasClass(this.activeClass);
35440         
35441     }
35442 });
35443
35444 Roo.apply(Roo.bootstrap.MasonryBrick, {
35445     
35446     //groups: {},
35447     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35448      /**
35449     * register a Masonry Brick
35450     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35451     */
35452     
35453     register : function(brick)
35454     {
35455         //this.groups[brick.id] = brick;
35456         this.groups.add(brick.id, brick);
35457     },
35458     /**
35459     * fetch a  masonry brick based on the masonry brick ID
35460     * @param {string} the masonry brick to add
35461     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35462     */
35463     
35464     get: function(brick_id) 
35465     {
35466         // if (typeof(this.groups[brick_id]) == 'undefined') {
35467         //     return false;
35468         // }
35469         // return this.groups[brick_id] ;
35470         
35471         if(this.groups.key(brick_id)) {
35472             return this.groups.key(brick_id);
35473         }
35474         
35475         return false;
35476     }
35477     
35478     
35479     
35480 });
35481
35482  /*
35483  * - LGPL
35484  *
35485  * element
35486  * 
35487  */
35488
35489 /**
35490  * @class Roo.bootstrap.Brick
35491  * @extends Roo.bootstrap.Component
35492  * Bootstrap Brick class
35493  * 
35494  * @constructor
35495  * Create a new Brick
35496  * @param {Object} config The config object
35497  */
35498
35499 Roo.bootstrap.Brick = function(config){
35500     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35501     
35502     this.addEvents({
35503         // raw events
35504         /**
35505          * @event click
35506          * When a Brick is click
35507          * @param {Roo.bootstrap.Brick} this
35508          * @param {Roo.EventObject} e
35509          */
35510         "click" : true
35511     });
35512 };
35513
35514 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35515     
35516     /**
35517      * @cfg {String} title
35518      */   
35519     title : '',
35520     /**
35521      * @cfg {String} html
35522      */   
35523     html : '',
35524     /**
35525      * @cfg {String} bgimage
35526      */   
35527     bgimage : '',
35528     /**
35529      * @cfg {String} cls
35530      */   
35531     cls : '',
35532     /**
35533      * @cfg {String} href
35534      */   
35535     href : '',
35536     /**
35537      * @cfg {String} video
35538      */   
35539     video : '',
35540     /**
35541      * @cfg {Boolean} square
35542      */   
35543     square : true,
35544     
35545     getAutoCreate : function()
35546     {
35547         var cls = 'roo-brick';
35548         
35549         if(this.href.length){
35550             cls += ' roo-brick-link';
35551         }
35552         
35553         if(this.bgimage.length){
35554             cls += ' roo-brick-image';
35555         }
35556         
35557         if(!this.html.length && !this.bgimage.length){
35558             cls += ' roo-brick-center-title';
35559         }
35560         
35561         if(!this.html.length && this.bgimage.length){
35562             cls += ' roo-brick-bottom-title';
35563         }
35564         
35565         if(this.cls){
35566             cls += ' ' + this.cls;
35567         }
35568         
35569         var cfg = {
35570             tag: (this.href.length) ? 'a' : 'div',
35571             cls: cls,
35572             cn: [
35573                 {
35574                     tag: 'div',
35575                     cls: 'roo-brick-paragraph',
35576                     cn: []
35577                 }
35578             ]
35579         };
35580         
35581         if(this.href.length){
35582             cfg.href = this.href;
35583         }
35584         
35585         var cn = cfg.cn[0].cn;
35586         
35587         if(this.title.length){
35588             cn.push({
35589                 tag: 'h4',
35590                 cls: 'roo-brick-title',
35591                 html: this.title
35592             });
35593         }
35594         
35595         if(this.html.length){
35596             cn.push({
35597                 tag: 'p',
35598                 cls: 'roo-brick-text',
35599                 html: this.html
35600             });
35601         } else {
35602             cn.cls += ' hide';
35603         }
35604         
35605         if(this.bgimage.length){
35606             cfg.cn.push({
35607                 tag: 'img',
35608                 cls: 'roo-brick-image-view',
35609                 src: this.bgimage
35610             });
35611         }
35612         
35613         return cfg;
35614     },
35615     
35616     initEvents: function() 
35617     {
35618         if(this.title.length || this.html.length){
35619             this.el.on('mouseenter'  ,this.enter, this);
35620             this.el.on('mouseleave', this.leave, this);
35621         }
35622         
35623         Roo.EventManager.onWindowResize(this.resize, this); 
35624         
35625         if(this.bgimage.length){
35626             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35627             this.imageEl.on('load', this.onImageLoad, this);
35628             return;
35629         }
35630         
35631         this.resize();
35632     },
35633     
35634     onImageLoad : function()
35635     {
35636         this.resize();
35637     },
35638     
35639     resize : function()
35640     {
35641         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35642         
35643         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35644         
35645         if(this.bgimage.length){
35646             var image = this.el.select('.roo-brick-image-view', true).first();
35647             
35648             image.setWidth(paragraph.getWidth());
35649             
35650             if(this.square){
35651                 image.setHeight(paragraph.getWidth());
35652             }
35653             
35654             this.el.setHeight(image.getHeight());
35655             paragraph.setHeight(image.getHeight());
35656             
35657         }
35658         
35659     },
35660     
35661     enter: function(e, el)
35662     {
35663         e.preventDefault();
35664         
35665         if(this.bgimage.length){
35666             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35667             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35668         }
35669     },
35670     
35671     leave: function(e, el)
35672     {
35673         e.preventDefault();
35674         
35675         if(this.bgimage.length){
35676             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35677             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35678         }
35679     }
35680     
35681 });
35682
35683  
35684
35685  /*
35686  * - LGPL
35687  *
35688  * Number field 
35689  */
35690
35691 /**
35692  * @class Roo.bootstrap.NumberField
35693  * @extends Roo.bootstrap.Input
35694  * Bootstrap NumberField class
35695  * 
35696  * 
35697  * 
35698  * 
35699  * @constructor
35700  * Create a new NumberField
35701  * @param {Object} config The config object
35702  */
35703
35704 Roo.bootstrap.NumberField = function(config){
35705     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35706 };
35707
35708 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35709     
35710     /**
35711      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35712      */
35713     allowDecimals : true,
35714     /**
35715      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35716      */
35717     decimalSeparator : ".",
35718     /**
35719      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35720      */
35721     decimalPrecision : 2,
35722     /**
35723      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35724      */
35725     allowNegative : true,
35726     
35727     /**
35728      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35729      */
35730     allowZero: true,
35731     /**
35732      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35733      */
35734     minValue : Number.NEGATIVE_INFINITY,
35735     /**
35736      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35737      */
35738     maxValue : Number.MAX_VALUE,
35739     /**
35740      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35741      */
35742     minText : "The minimum value for this field is {0}",
35743     /**
35744      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35745      */
35746     maxText : "The maximum value for this field is {0}",
35747     /**
35748      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35749      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35750      */
35751     nanText : "{0} is not a valid number",
35752     /**
35753      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35754      */
35755     thousandsDelimiter : false,
35756     /**
35757      * @cfg {String} valueAlign alignment of value
35758      */
35759     valueAlign : "left",
35760
35761     getAutoCreate : function()
35762     {
35763         var hiddenInput = {
35764             tag: 'input',
35765             type: 'hidden',
35766             id: Roo.id(),
35767             cls: 'hidden-number-input'
35768         };
35769         
35770         if (this.name) {
35771             hiddenInput.name = this.name;
35772         }
35773         
35774         this.name = '';
35775         
35776         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35777         
35778         this.name = hiddenInput.name;
35779         
35780         if(cfg.cn.length > 0) {
35781             cfg.cn.push(hiddenInput);
35782         }
35783         
35784         return cfg;
35785     },
35786
35787     // private
35788     initEvents : function()
35789     {   
35790         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35791         
35792         var allowed = "0123456789";
35793         
35794         if(this.allowDecimals){
35795             allowed += this.decimalSeparator;
35796         }
35797         
35798         if(this.allowNegative){
35799             allowed += "-";
35800         }
35801         
35802         if(this.thousandsDelimiter) {
35803             allowed += ",";
35804         }
35805         
35806         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35807         
35808         var keyPress = function(e){
35809             
35810             var k = e.getKey();
35811             
35812             var c = e.getCharCode();
35813             
35814             if(
35815                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35816                     allowed.indexOf(String.fromCharCode(c)) === -1
35817             ){
35818                 e.stopEvent();
35819                 return;
35820             }
35821             
35822             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35823                 return;
35824             }
35825             
35826             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35827                 e.stopEvent();
35828             }
35829         };
35830         
35831         this.el.on("keypress", keyPress, this);
35832     },
35833     
35834     validateValue : function(value)
35835     {
35836         
35837         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35838             return false;
35839         }
35840         
35841         var num = this.parseValue(value);
35842         
35843         if(isNaN(num)){
35844             this.markInvalid(String.format(this.nanText, value));
35845             return false;
35846         }
35847         
35848         if(num < this.minValue){
35849             this.markInvalid(String.format(this.minText, this.minValue));
35850             return false;
35851         }
35852         
35853         if(num > this.maxValue){
35854             this.markInvalid(String.format(this.maxText, this.maxValue));
35855             return false;
35856         }
35857         
35858         return true;
35859     },
35860
35861     getValue : function()
35862     {
35863         var v = this.hiddenEl().getValue();
35864         
35865         return this.fixPrecision(this.parseValue(v));
35866     },
35867
35868     parseValue : function(value)
35869     {
35870         if(this.thousandsDelimiter) {
35871             value += "";
35872             r = new RegExp(",", "g");
35873             value = value.replace(r, "");
35874         }
35875         
35876         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35877         return isNaN(value) ? '' : value;
35878     },
35879
35880     fixPrecision : function(value)
35881     {
35882         if(this.thousandsDelimiter) {
35883             value += "";
35884             r = new RegExp(",", "g");
35885             value = value.replace(r, "");
35886         }
35887         
35888         var nan = isNaN(value);
35889         
35890         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35891             return nan ? '' : value;
35892         }
35893         return parseFloat(value).toFixed(this.decimalPrecision);
35894     },
35895
35896     setValue : function(v)
35897     {
35898         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35899         
35900         this.value = v;
35901         
35902         if(this.rendered){
35903             
35904             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35905             
35906             this.inputEl().dom.value = (v == '') ? '' :
35907                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35908             
35909             if(!this.allowZero && v === '0') {
35910                 this.hiddenEl().dom.value = '';
35911                 this.inputEl().dom.value = '';
35912             }
35913             
35914             this.validate();
35915         }
35916     },
35917
35918     decimalPrecisionFcn : function(v)
35919     {
35920         return Math.floor(v);
35921     },
35922
35923     beforeBlur : function()
35924     {
35925         var v = this.parseValue(this.getRawValue());
35926         
35927         if(v || v === 0 || v === ''){
35928             this.setValue(v);
35929         }
35930     },
35931     
35932     hiddenEl : function()
35933     {
35934         return this.el.select('input.hidden-number-input',true).first();
35935     }
35936     
35937 });
35938
35939  
35940
35941 /*
35942 * Licence: LGPL
35943 */
35944
35945 /**
35946  * @class Roo.bootstrap.DocumentSlider
35947  * @extends Roo.bootstrap.Component
35948  * Bootstrap DocumentSlider class
35949  * 
35950  * @constructor
35951  * Create a new DocumentViewer
35952  * @param {Object} config The config object
35953  */
35954
35955 Roo.bootstrap.DocumentSlider = function(config){
35956     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35957     
35958     this.files = [];
35959     
35960     this.addEvents({
35961         /**
35962          * @event initial
35963          * Fire after initEvent
35964          * @param {Roo.bootstrap.DocumentSlider} this
35965          */
35966         "initial" : true,
35967         /**
35968          * @event update
35969          * Fire after update
35970          * @param {Roo.bootstrap.DocumentSlider} this
35971          */
35972         "update" : true,
35973         /**
35974          * @event click
35975          * Fire after click
35976          * @param {Roo.bootstrap.DocumentSlider} this
35977          */
35978         "click" : true
35979     });
35980 };
35981
35982 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35983     
35984     files : false,
35985     
35986     indicator : 0,
35987     
35988     getAutoCreate : function()
35989     {
35990         var cfg = {
35991             tag : 'div',
35992             cls : 'roo-document-slider',
35993             cn : [
35994                 {
35995                     tag : 'div',
35996                     cls : 'roo-document-slider-header',
35997                     cn : [
35998                         {
35999                             tag : 'div',
36000                             cls : 'roo-document-slider-header-title'
36001                         }
36002                     ]
36003                 },
36004                 {
36005                     tag : 'div',
36006                     cls : 'roo-document-slider-body',
36007                     cn : [
36008                         {
36009                             tag : 'div',
36010                             cls : 'roo-document-slider-prev',
36011                             cn : [
36012                                 {
36013                                     tag : 'i',
36014                                     cls : 'fa fa-chevron-left'
36015                                 }
36016                             ]
36017                         },
36018                         {
36019                             tag : 'div',
36020                             cls : 'roo-document-slider-thumb',
36021                             cn : [
36022                                 {
36023                                     tag : 'img',
36024                                     cls : 'roo-document-slider-image'
36025                                 }
36026                             ]
36027                         },
36028                         {
36029                             tag : 'div',
36030                             cls : 'roo-document-slider-next',
36031                             cn : [
36032                                 {
36033                                     tag : 'i',
36034                                     cls : 'fa fa-chevron-right'
36035                                 }
36036                             ]
36037                         }
36038                     ]
36039                 }
36040             ]
36041         };
36042         
36043         return cfg;
36044     },
36045     
36046     initEvents : function()
36047     {
36048         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36049         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36050         
36051         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36052         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36053         
36054         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36055         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36056         
36057         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36058         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36059         
36060         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36061         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36062         
36063         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36064         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36065         
36066         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36067         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36068         
36069         this.thumbEl.on('click', this.onClick, this);
36070         
36071         this.prevIndicator.on('click', this.prev, this);
36072         
36073         this.nextIndicator.on('click', this.next, this);
36074         
36075     },
36076     
36077     initial : function()
36078     {
36079         if(this.files.length){
36080             this.indicator = 1;
36081             this.update()
36082         }
36083         
36084         this.fireEvent('initial', this);
36085     },
36086     
36087     update : function()
36088     {
36089         this.imageEl.attr('src', this.files[this.indicator - 1]);
36090         
36091         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36092         
36093         this.prevIndicator.show();
36094         
36095         if(this.indicator == 1){
36096             this.prevIndicator.hide();
36097         }
36098         
36099         this.nextIndicator.show();
36100         
36101         if(this.indicator == this.files.length){
36102             this.nextIndicator.hide();
36103         }
36104         
36105         this.thumbEl.scrollTo('top');
36106         
36107         this.fireEvent('update', this);
36108     },
36109     
36110     onClick : function(e)
36111     {
36112         e.preventDefault();
36113         
36114         this.fireEvent('click', this);
36115     },
36116     
36117     prev : function(e)
36118     {
36119         e.preventDefault();
36120         
36121         this.indicator = Math.max(1, this.indicator - 1);
36122         
36123         this.update();
36124     },
36125     
36126     next : function(e)
36127     {
36128         e.preventDefault();
36129         
36130         this.indicator = Math.min(this.files.length, this.indicator + 1);
36131         
36132         this.update();
36133     }
36134 });
36135 /*
36136  * - LGPL
36137  *
36138  * RadioSet
36139  *
36140  *
36141  */
36142
36143 /**
36144  * @class Roo.bootstrap.RadioSet
36145  * @extends Roo.bootstrap.Input
36146  * Bootstrap RadioSet class
36147  * @cfg {String} indicatorpos (left|right) default left
36148  * @cfg {Boolean} inline (true|false) inline the element (default true)
36149  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36150  * @constructor
36151  * Create a new RadioSet
36152  * @param {Object} config The config object
36153  */
36154
36155 Roo.bootstrap.RadioSet = function(config){
36156     
36157     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36158     
36159     this.radioes = [];
36160     
36161     Roo.bootstrap.RadioSet.register(this);
36162     
36163     this.addEvents({
36164         /**
36165         * @event check
36166         * Fires when the element is checked or unchecked.
36167         * @param {Roo.bootstrap.RadioSet} this This radio
36168         * @param {Roo.bootstrap.Radio} item The checked item
36169         */
36170        check : true,
36171        /**
36172         * @event click
36173         * Fires when the element is click.
36174         * @param {Roo.bootstrap.RadioSet} this This radio set
36175         * @param {Roo.bootstrap.Radio} item The checked item
36176         * @param {Roo.EventObject} e The event object
36177         */
36178        click : true
36179     });
36180     
36181 };
36182
36183 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36184
36185     radioes : false,
36186     
36187     inline : true,
36188     
36189     weight : '',
36190     
36191     indicatorpos : 'left',
36192     
36193     getAutoCreate : function()
36194     {
36195         var label = {
36196             tag : 'label',
36197             cls : 'roo-radio-set-label',
36198             cn : [
36199                 {
36200                     tag : 'span',
36201                     html : this.fieldLabel
36202                 }
36203             ]
36204         };
36205         if (Roo.bootstrap.version == 3) {
36206             
36207             
36208             if(this.indicatorpos == 'left'){
36209                 label.cn.unshift({
36210                     tag : 'i',
36211                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36212                     tooltip : 'This field is required'
36213                 });
36214             } else {
36215                 label.cn.push({
36216                     tag : 'i',
36217                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36218                     tooltip : 'This field is required'
36219                 });
36220             }
36221         }
36222         var items = {
36223             tag : 'div',
36224             cls : 'roo-radio-set-items'
36225         };
36226         
36227         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36228         
36229         if (align === 'left' && this.fieldLabel.length) {
36230             
36231             items = {
36232                 cls : "roo-radio-set-right", 
36233                 cn: [
36234                     items
36235                 ]
36236             };
36237             
36238             if(this.labelWidth > 12){
36239                 label.style = "width: " + this.labelWidth + 'px';
36240             }
36241             
36242             if(this.labelWidth < 13 && this.labelmd == 0){
36243                 this.labelmd = this.labelWidth;
36244             }
36245             
36246             if(this.labellg > 0){
36247                 label.cls += ' col-lg-' + this.labellg;
36248                 items.cls += ' col-lg-' + (12 - this.labellg);
36249             }
36250             
36251             if(this.labelmd > 0){
36252                 label.cls += ' col-md-' + this.labelmd;
36253                 items.cls += ' col-md-' + (12 - this.labelmd);
36254             }
36255             
36256             if(this.labelsm > 0){
36257                 label.cls += ' col-sm-' + this.labelsm;
36258                 items.cls += ' col-sm-' + (12 - this.labelsm);
36259             }
36260             
36261             if(this.labelxs > 0){
36262                 label.cls += ' col-xs-' + this.labelxs;
36263                 items.cls += ' col-xs-' + (12 - this.labelxs);
36264             }
36265         }
36266         
36267         var cfg = {
36268             tag : 'div',
36269             cls : 'roo-radio-set',
36270             cn : [
36271                 {
36272                     tag : 'input',
36273                     cls : 'roo-radio-set-input',
36274                     type : 'hidden',
36275                     name : this.name,
36276                     value : this.value ? this.value :  ''
36277                 },
36278                 label,
36279                 items
36280             ]
36281         };
36282         
36283         if(this.weight.length){
36284             cfg.cls += ' roo-radio-' + this.weight;
36285         }
36286         
36287         if(this.inline) {
36288             cfg.cls += ' roo-radio-set-inline';
36289         }
36290         
36291         var settings=this;
36292         ['xs','sm','md','lg'].map(function(size){
36293             if (settings[size]) {
36294                 cfg.cls += ' col-' + size + '-' + settings[size];
36295             }
36296         });
36297         
36298         return cfg;
36299         
36300     },
36301
36302     initEvents : function()
36303     {
36304         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36305         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36306         
36307         if(!this.fieldLabel.length){
36308             this.labelEl.hide();
36309         }
36310         
36311         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36312         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36313         
36314         this.indicator = this.indicatorEl();
36315         
36316         if(this.indicator){
36317             this.indicator.addClass('invisible');
36318         }
36319         
36320         this.originalValue = this.getValue();
36321         
36322     },
36323     
36324     inputEl: function ()
36325     {
36326         return this.el.select('.roo-radio-set-input', true).first();
36327     },
36328     
36329     getChildContainer : function()
36330     {
36331         return this.itemsEl;
36332     },
36333     
36334     register : function(item)
36335     {
36336         this.radioes.push(item);
36337         
36338     },
36339     
36340     validate : function()
36341     {   
36342         if(this.getVisibilityEl().hasClass('hidden')){
36343             return true;
36344         }
36345         
36346         var valid = false;
36347         
36348         Roo.each(this.radioes, function(i){
36349             if(!i.checked){
36350                 return;
36351             }
36352             
36353             valid = true;
36354             return false;
36355         });
36356         
36357         if(this.allowBlank) {
36358             return true;
36359         }
36360         
36361         if(this.disabled || valid){
36362             this.markValid();
36363             return true;
36364         }
36365         
36366         this.markInvalid();
36367         return false;
36368         
36369     },
36370     
36371     markValid : function()
36372     {
36373         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36374             this.indicatorEl().removeClass('visible');
36375             this.indicatorEl().addClass('invisible');
36376         }
36377         
36378         
36379         if (Roo.bootstrap.version == 3) {
36380             this.el.removeClass([this.invalidClass, this.validClass]);
36381             this.el.addClass(this.validClass);
36382         } else {
36383             this.el.removeClass(['is-invalid','is-valid']);
36384             this.el.addClass(['is-valid']);
36385         }
36386         this.fireEvent('valid', this);
36387     },
36388     
36389     markInvalid : function(msg)
36390     {
36391         if(this.allowBlank || this.disabled){
36392             return;
36393         }
36394         
36395         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36396             this.indicatorEl().removeClass('invisible');
36397             this.indicatorEl().addClass('visible');
36398         }
36399         if (Roo.bootstrap.version == 3) {
36400             this.el.removeClass([this.invalidClass, this.validClass]);
36401             this.el.addClass(this.invalidClass);
36402         } else {
36403             this.el.removeClass(['is-invalid','is-valid']);
36404             this.el.addClass(['is-invalid']);
36405         }
36406         
36407         this.fireEvent('invalid', this, msg);
36408         
36409     },
36410     
36411     setValue : function(v, suppressEvent)
36412     {   
36413         if(this.value === v){
36414             return;
36415         }
36416         
36417         this.value = v;
36418         
36419         if(this.rendered){
36420             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36421         }
36422         
36423         Roo.each(this.radioes, function(i){
36424             i.checked = false;
36425             i.el.removeClass('checked');
36426         });
36427         
36428         Roo.each(this.radioes, function(i){
36429             
36430             if(i.value === v || i.value.toString() === v.toString()){
36431                 i.checked = true;
36432                 i.el.addClass('checked');
36433                 
36434                 if(suppressEvent !== true){
36435                     this.fireEvent('check', this, i);
36436                 }
36437                 
36438                 return false;
36439             }
36440             
36441         }, this);
36442         
36443         this.validate();
36444     },
36445     
36446     clearInvalid : function(){
36447         
36448         if(!this.el || this.preventMark){
36449             return;
36450         }
36451         
36452         this.el.removeClass([this.invalidClass]);
36453         
36454         this.fireEvent('valid', this);
36455     }
36456     
36457 });
36458
36459 Roo.apply(Roo.bootstrap.RadioSet, {
36460     
36461     groups: {},
36462     
36463     register : function(set)
36464     {
36465         this.groups[set.name] = set;
36466     },
36467     
36468     get: function(name) 
36469     {
36470         if (typeof(this.groups[name]) == 'undefined') {
36471             return false;
36472         }
36473         
36474         return this.groups[name] ;
36475     }
36476     
36477 });
36478 /*
36479  * Based on:
36480  * Ext JS Library 1.1.1
36481  * Copyright(c) 2006-2007, Ext JS, LLC.
36482  *
36483  * Originally Released Under LGPL - original licence link has changed is not relivant.
36484  *
36485  * Fork - LGPL
36486  * <script type="text/javascript">
36487  */
36488
36489
36490 /**
36491  * @class Roo.bootstrap.SplitBar
36492  * @extends Roo.util.Observable
36493  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36494  * <br><br>
36495  * Usage:
36496  * <pre><code>
36497 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36498                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36499 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36500 split.minSize = 100;
36501 split.maxSize = 600;
36502 split.animate = true;
36503 split.on('moved', splitterMoved);
36504 </code></pre>
36505  * @constructor
36506  * Create a new SplitBar
36507  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36508  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36509  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36510  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36511                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36512                         position of the SplitBar).
36513  */
36514 Roo.bootstrap.SplitBar = function(cfg){
36515     
36516     /** @private */
36517     
36518     //{
36519     //  dragElement : elm
36520     //  resizingElement: el,
36521         // optional..
36522     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36523     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36524         // existingProxy ???
36525     //}
36526     
36527     this.el = Roo.get(cfg.dragElement, true);
36528     this.el.dom.unselectable = "on";
36529     /** @private */
36530     this.resizingEl = Roo.get(cfg.resizingElement, true);
36531
36532     /**
36533      * @private
36534      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36535      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36536      * @type Number
36537      */
36538     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36539     
36540     /**
36541      * The minimum size of the resizing element. (Defaults to 0)
36542      * @type Number
36543      */
36544     this.minSize = 0;
36545     
36546     /**
36547      * The maximum size of the resizing element. (Defaults to 2000)
36548      * @type Number
36549      */
36550     this.maxSize = 2000;
36551     
36552     /**
36553      * Whether to animate the transition to the new size
36554      * @type Boolean
36555      */
36556     this.animate = false;
36557     
36558     /**
36559      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36560      * @type Boolean
36561      */
36562     this.useShim = false;
36563     
36564     /** @private */
36565     this.shim = null;
36566     
36567     if(!cfg.existingProxy){
36568         /** @private */
36569         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36570     }else{
36571         this.proxy = Roo.get(cfg.existingProxy).dom;
36572     }
36573     /** @private */
36574     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36575     
36576     /** @private */
36577     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36578     
36579     /** @private */
36580     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36581     
36582     /** @private */
36583     this.dragSpecs = {};
36584     
36585     /**
36586      * @private The adapter to use to positon and resize elements
36587      */
36588     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36589     this.adapter.init(this);
36590     
36591     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36592         /** @private */
36593         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36594         this.el.addClass("roo-splitbar-h");
36595     }else{
36596         /** @private */
36597         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36598         this.el.addClass("roo-splitbar-v");
36599     }
36600     
36601     this.addEvents({
36602         /**
36603          * @event resize
36604          * Fires when the splitter is moved (alias for {@link #event-moved})
36605          * @param {Roo.bootstrap.SplitBar} this
36606          * @param {Number} newSize the new width or height
36607          */
36608         "resize" : true,
36609         /**
36610          * @event moved
36611          * Fires when the splitter is moved
36612          * @param {Roo.bootstrap.SplitBar} this
36613          * @param {Number} newSize the new width or height
36614          */
36615         "moved" : true,
36616         /**
36617          * @event beforeresize
36618          * Fires before the splitter is dragged
36619          * @param {Roo.bootstrap.SplitBar} this
36620          */
36621         "beforeresize" : true,
36622
36623         "beforeapply" : true
36624     });
36625
36626     Roo.util.Observable.call(this);
36627 };
36628
36629 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36630     onStartProxyDrag : function(x, y){
36631         this.fireEvent("beforeresize", this);
36632         if(!this.overlay){
36633             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36634             o.unselectable();
36635             o.enableDisplayMode("block");
36636             // all splitbars share the same overlay
36637             Roo.bootstrap.SplitBar.prototype.overlay = o;
36638         }
36639         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36640         this.overlay.show();
36641         Roo.get(this.proxy).setDisplayed("block");
36642         var size = this.adapter.getElementSize(this);
36643         this.activeMinSize = this.getMinimumSize();;
36644         this.activeMaxSize = this.getMaximumSize();;
36645         var c1 = size - this.activeMinSize;
36646         var c2 = Math.max(this.activeMaxSize - size, 0);
36647         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36648             this.dd.resetConstraints();
36649             this.dd.setXConstraint(
36650                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36651                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36652             );
36653             this.dd.setYConstraint(0, 0);
36654         }else{
36655             this.dd.resetConstraints();
36656             this.dd.setXConstraint(0, 0);
36657             this.dd.setYConstraint(
36658                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36659                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36660             );
36661          }
36662         this.dragSpecs.startSize = size;
36663         this.dragSpecs.startPoint = [x, y];
36664         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36665     },
36666     
36667     /** 
36668      * @private Called after the drag operation by the DDProxy
36669      */
36670     onEndProxyDrag : function(e){
36671         Roo.get(this.proxy).setDisplayed(false);
36672         var endPoint = Roo.lib.Event.getXY(e);
36673         if(this.overlay){
36674             this.overlay.hide();
36675         }
36676         var newSize;
36677         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36678             newSize = this.dragSpecs.startSize + 
36679                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36680                     endPoint[0] - this.dragSpecs.startPoint[0] :
36681                     this.dragSpecs.startPoint[0] - endPoint[0]
36682                 );
36683         }else{
36684             newSize = this.dragSpecs.startSize + 
36685                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36686                     endPoint[1] - this.dragSpecs.startPoint[1] :
36687                     this.dragSpecs.startPoint[1] - endPoint[1]
36688                 );
36689         }
36690         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36691         if(newSize != this.dragSpecs.startSize){
36692             if(this.fireEvent('beforeapply', this, newSize) !== false){
36693                 this.adapter.setElementSize(this, newSize);
36694                 this.fireEvent("moved", this, newSize);
36695                 this.fireEvent("resize", this, newSize);
36696             }
36697         }
36698     },
36699     
36700     /**
36701      * Get the adapter this SplitBar uses
36702      * @return The adapter object
36703      */
36704     getAdapter : function(){
36705         return this.adapter;
36706     },
36707     
36708     /**
36709      * Set the adapter this SplitBar uses
36710      * @param {Object} adapter A SplitBar adapter object
36711      */
36712     setAdapter : function(adapter){
36713         this.adapter = adapter;
36714         this.adapter.init(this);
36715     },
36716     
36717     /**
36718      * Gets the minimum size for the resizing element
36719      * @return {Number} The minimum size
36720      */
36721     getMinimumSize : function(){
36722         return this.minSize;
36723     },
36724     
36725     /**
36726      * Sets the minimum size for the resizing element
36727      * @param {Number} minSize The minimum size
36728      */
36729     setMinimumSize : function(minSize){
36730         this.minSize = minSize;
36731     },
36732     
36733     /**
36734      * Gets the maximum size for the resizing element
36735      * @return {Number} The maximum size
36736      */
36737     getMaximumSize : function(){
36738         return this.maxSize;
36739     },
36740     
36741     /**
36742      * Sets the maximum size for the resizing element
36743      * @param {Number} maxSize The maximum size
36744      */
36745     setMaximumSize : function(maxSize){
36746         this.maxSize = maxSize;
36747     },
36748     
36749     /**
36750      * Sets the initialize size for the resizing element
36751      * @param {Number} size The initial size
36752      */
36753     setCurrentSize : function(size){
36754         var oldAnimate = this.animate;
36755         this.animate = false;
36756         this.adapter.setElementSize(this, size);
36757         this.animate = oldAnimate;
36758     },
36759     
36760     /**
36761      * Destroy this splitbar. 
36762      * @param {Boolean} removeEl True to remove the element
36763      */
36764     destroy : function(removeEl){
36765         if(this.shim){
36766             this.shim.remove();
36767         }
36768         this.dd.unreg();
36769         this.proxy.parentNode.removeChild(this.proxy);
36770         if(removeEl){
36771             this.el.remove();
36772         }
36773     }
36774 });
36775
36776 /**
36777  * @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.
36778  */
36779 Roo.bootstrap.SplitBar.createProxy = function(dir){
36780     var proxy = new Roo.Element(document.createElement("div"));
36781     proxy.unselectable();
36782     var cls = 'roo-splitbar-proxy';
36783     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36784     document.body.appendChild(proxy.dom);
36785     return proxy.dom;
36786 };
36787
36788 /** 
36789  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36790  * Default Adapter. It assumes the splitter and resizing element are not positioned
36791  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36792  */
36793 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36794 };
36795
36796 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36797     // do nothing for now
36798     init : function(s){
36799     
36800     },
36801     /**
36802      * Called before drag operations to get the current size of the resizing element. 
36803      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36804      */
36805      getElementSize : function(s){
36806         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36807             return s.resizingEl.getWidth();
36808         }else{
36809             return s.resizingEl.getHeight();
36810         }
36811     },
36812     
36813     /**
36814      * Called after drag operations to set the size of the resizing element.
36815      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36816      * @param {Number} newSize The new size to set
36817      * @param {Function} onComplete A function to be invoked when resizing is complete
36818      */
36819     setElementSize : function(s, newSize, onComplete){
36820         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36821             if(!s.animate){
36822                 s.resizingEl.setWidth(newSize);
36823                 if(onComplete){
36824                     onComplete(s, newSize);
36825                 }
36826             }else{
36827                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36828             }
36829         }else{
36830             
36831             if(!s.animate){
36832                 s.resizingEl.setHeight(newSize);
36833                 if(onComplete){
36834                     onComplete(s, newSize);
36835                 }
36836             }else{
36837                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36838             }
36839         }
36840     }
36841 };
36842
36843 /** 
36844  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36845  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36846  * Adapter that  moves the splitter element to align with the resized sizing element. 
36847  * Used with an absolute positioned SplitBar.
36848  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36849  * document.body, make sure you assign an id to the body element.
36850  */
36851 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36852     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36853     this.container = Roo.get(container);
36854 };
36855
36856 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36857     init : function(s){
36858         this.basic.init(s);
36859     },
36860     
36861     getElementSize : function(s){
36862         return this.basic.getElementSize(s);
36863     },
36864     
36865     setElementSize : function(s, newSize, onComplete){
36866         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36867     },
36868     
36869     moveSplitter : function(s){
36870         var yes = Roo.bootstrap.SplitBar;
36871         switch(s.placement){
36872             case yes.LEFT:
36873                 s.el.setX(s.resizingEl.getRight());
36874                 break;
36875             case yes.RIGHT:
36876                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36877                 break;
36878             case yes.TOP:
36879                 s.el.setY(s.resizingEl.getBottom());
36880                 break;
36881             case yes.BOTTOM:
36882                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36883                 break;
36884         }
36885     }
36886 };
36887
36888 /**
36889  * Orientation constant - Create a vertical SplitBar
36890  * @static
36891  * @type Number
36892  */
36893 Roo.bootstrap.SplitBar.VERTICAL = 1;
36894
36895 /**
36896  * Orientation constant - Create a horizontal SplitBar
36897  * @static
36898  * @type Number
36899  */
36900 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36901
36902 /**
36903  * Placement constant - The resizing element is to the left of the splitter element
36904  * @static
36905  * @type Number
36906  */
36907 Roo.bootstrap.SplitBar.LEFT = 1;
36908
36909 /**
36910  * Placement constant - The resizing element is to the right of the splitter element
36911  * @static
36912  * @type Number
36913  */
36914 Roo.bootstrap.SplitBar.RIGHT = 2;
36915
36916 /**
36917  * Placement constant - The resizing element is positioned above the splitter element
36918  * @static
36919  * @type Number
36920  */
36921 Roo.bootstrap.SplitBar.TOP = 3;
36922
36923 /**
36924  * Placement constant - The resizing element is positioned under splitter element
36925  * @static
36926  * @type Number
36927  */
36928 Roo.bootstrap.SplitBar.BOTTOM = 4;
36929 Roo.namespace("Roo.bootstrap.layout");/*
36930  * Based on:
36931  * Ext JS Library 1.1.1
36932  * Copyright(c) 2006-2007, Ext JS, LLC.
36933  *
36934  * Originally Released Under LGPL - original licence link has changed is not relivant.
36935  *
36936  * Fork - LGPL
36937  * <script type="text/javascript">
36938  */
36939
36940 /**
36941  * @class Roo.bootstrap.layout.Manager
36942  * @extends Roo.bootstrap.Component
36943  * Base class for layout managers.
36944  */
36945 Roo.bootstrap.layout.Manager = function(config)
36946 {
36947     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36948
36949
36950
36951
36952
36953     /** false to disable window resize monitoring @type Boolean */
36954     this.monitorWindowResize = true;
36955     this.regions = {};
36956     this.addEvents({
36957         /**
36958          * @event layout
36959          * Fires when a layout is performed.
36960          * @param {Roo.LayoutManager} this
36961          */
36962         "layout" : true,
36963         /**
36964          * @event regionresized
36965          * Fires when the user resizes a region.
36966          * @param {Roo.LayoutRegion} region The resized region
36967          * @param {Number} newSize The new size (width for east/west, height for north/south)
36968          */
36969         "regionresized" : true,
36970         /**
36971          * @event regioncollapsed
36972          * Fires when a region is collapsed.
36973          * @param {Roo.LayoutRegion} region The collapsed region
36974          */
36975         "regioncollapsed" : true,
36976         /**
36977          * @event regionexpanded
36978          * Fires when a region is expanded.
36979          * @param {Roo.LayoutRegion} region The expanded region
36980          */
36981         "regionexpanded" : true
36982     });
36983     this.updating = false;
36984
36985     if (config.el) {
36986         this.el = Roo.get(config.el);
36987         this.initEvents();
36988     }
36989
36990 };
36991
36992 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36993
36994
36995     regions : null,
36996
36997     monitorWindowResize : true,
36998
36999
37000     updating : false,
37001
37002
37003     onRender : function(ct, position)
37004     {
37005         if(!this.el){
37006             this.el = Roo.get(ct);
37007             this.initEvents();
37008         }
37009         //this.fireEvent('render',this);
37010     },
37011
37012
37013     initEvents: function()
37014     {
37015
37016
37017         // ie scrollbar fix
37018         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37019             document.body.scroll = "no";
37020         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37021             this.el.position('relative');
37022         }
37023         this.id = this.el.id;
37024         this.el.addClass("roo-layout-container");
37025         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37026         if(this.el.dom != document.body ) {
37027             this.el.on('resize', this.layout,this);
37028             this.el.on('show', this.layout,this);
37029         }
37030
37031     },
37032
37033     /**
37034      * Returns true if this layout is currently being updated
37035      * @return {Boolean}
37036      */
37037     isUpdating : function(){
37038         return this.updating;
37039     },
37040
37041     /**
37042      * Suspend the LayoutManager from doing auto-layouts while
37043      * making multiple add or remove calls
37044      */
37045     beginUpdate : function(){
37046         this.updating = true;
37047     },
37048
37049     /**
37050      * Restore auto-layouts and optionally disable the manager from performing a layout
37051      * @param {Boolean} noLayout true to disable a layout update
37052      */
37053     endUpdate : function(noLayout){
37054         this.updating = false;
37055         if(!noLayout){
37056             this.layout();
37057         }
37058     },
37059
37060     layout: function(){
37061         // abstract...
37062     },
37063
37064     onRegionResized : function(region, newSize){
37065         this.fireEvent("regionresized", region, newSize);
37066         this.layout();
37067     },
37068
37069     onRegionCollapsed : function(region){
37070         this.fireEvent("regioncollapsed", region);
37071     },
37072
37073     onRegionExpanded : function(region){
37074         this.fireEvent("regionexpanded", region);
37075     },
37076
37077     /**
37078      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37079      * performs box-model adjustments.
37080      * @return {Object} The size as an object {width: (the width), height: (the height)}
37081      */
37082     getViewSize : function()
37083     {
37084         var size;
37085         if(this.el.dom != document.body){
37086             size = this.el.getSize();
37087         }else{
37088             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37089         }
37090         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37091         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37092         return size;
37093     },
37094
37095     /**
37096      * Returns the Element this layout is bound to.
37097      * @return {Roo.Element}
37098      */
37099     getEl : function(){
37100         return this.el;
37101     },
37102
37103     /**
37104      * Returns the specified region.
37105      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37106      * @return {Roo.LayoutRegion}
37107      */
37108     getRegion : function(target){
37109         return this.regions[target.toLowerCase()];
37110     },
37111
37112     onWindowResize : function(){
37113         if(this.monitorWindowResize){
37114             this.layout();
37115         }
37116     }
37117 });
37118 /*
37119  * Based on:
37120  * Ext JS Library 1.1.1
37121  * Copyright(c) 2006-2007, Ext JS, LLC.
37122  *
37123  * Originally Released Under LGPL - original licence link has changed is not relivant.
37124  *
37125  * Fork - LGPL
37126  * <script type="text/javascript">
37127  */
37128 /**
37129  * @class Roo.bootstrap.layout.Border
37130  * @extends Roo.bootstrap.layout.Manager
37131  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37132  * please see: examples/bootstrap/nested.html<br><br>
37133  
37134 <b>The container the layout is rendered into can be either the body element or any other element.
37135 If it is not the body element, the container needs to either be an absolute positioned element,
37136 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37137 the container size if it is not the body element.</b>
37138
37139 * @constructor
37140 * Create a new Border
37141 * @param {Object} config Configuration options
37142  */
37143 Roo.bootstrap.layout.Border = function(config){
37144     config = config || {};
37145     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37146     
37147     
37148     
37149     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37150         if(config[region]){
37151             config[region].region = region;
37152             this.addRegion(config[region]);
37153         }
37154     },this);
37155     
37156 };
37157
37158 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37159
37160 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37161     
37162     parent : false, // this might point to a 'nest' or a ???
37163     
37164     /**
37165      * Creates and adds a new region if it doesn't already exist.
37166      * @param {String} target The target region key (north, south, east, west or center).
37167      * @param {Object} config The regions config object
37168      * @return {BorderLayoutRegion} The new region
37169      */
37170     addRegion : function(config)
37171     {
37172         if(!this.regions[config.region]){
37173             var r = this.factory(config);
37174             this.bindRegion(r);
37175         }
37176         return this.regions[config.region];
37177     },
37178
37179     // private (kinda)
37180     bindRegion : function(r){
37181         this.regions[r.config.region] = r;
37182         
37183         r.on("visibilitychange",    this.layout, this);
37184         r.on("paneladded",          this.layout, this);
37185         r.on("panelremoved",        this.layout, this);
37186         r.on("invalidated",         this.layout, this);
37187         r.on("resized",             this.onRegionResized, this);
37188         r.on("collapsed",           this.onRegionCollapsed, this);
37189         r.on("expanded",            this.onRegionExpanded, this);
37190     },
37191
37192     /**
37193      * Performs a layout update.
37194      */
37195     layout : function()
37196     {
37197         if(this.updating) {
37198             return;
37199         }
37200         
37201         // render all the rebions if they have not been done alreayd?
37202         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37203             if(this.regions[region] && !this.regions[region].bodyEl){
37204                 this.regions[region].onRender(this.el)
37205             }
37206         },this);
37207         
37208         var size = this.getViewSize();
37209         var w = size.width;
37210         var h = size.height;
37211         var centerW = w;
37212         var centerH = h;
37213         var centerY = 0;
37214         var centerX = 0;
37215         //var x = 0, y = 0;
37216
37217         var rs = this.regions;
37218         var north = rs["north"];
37219         var south = rs["south"]; 
37220         var west = rs["west"];
37221         var east = rs["east"];
37222         var center = rs["center"];
37223         //if(this.hideOnLayout){ // not supported anymore
37224             //c.el.setStyle("display", "none");
37225         //}
37226         if(north && north.isVisible()){
37227             var b = north.getBox();
37228             var m = north.getMargins();
37229             b.width = w - (m.left+m.right);
37230             b.x = m.left;
37231             b.y = m.top;
37232             centerY = b.height + b.y + m.bottom;
37233             centerH -= centerY;
37234             north.updateBox(this.safeBox(b));
37235         }
37236         if(south && south.isVisible()){
37237             var b = south.getBox();
37238             var m = south.getMargins();
37239             b.width = w - (m.left+m.right);
37240             b.x = m.left;
37241             var totalHeight = (b.height + m.top + m.bottom);
37242             b.y = h - totalHeight + m.top;
37243             centerH -= totalHeight;
37244             south.updateBox(this.safeBox(b));
37245         }
37246         if(west && west.isVisible()){
37247             var b = west.getBox();
37248             var m = west.getMargins();
37249             b.height = centerH - (m.top+m.bottom);
37250             b.x = m.left;
37251             b.y = centerY + m.top;
37252             var totalWidth = (b.width + m.left + m.right);
37253             centerX += totalWidth;
37254             centerW -= totalWidth;
37255             west.updateBox(this.safeBox(b));
37256         }
37257         if(east && east.isVisible()){
37258             var b = east.getBox();
37259             var m = east.getMargins();
37260             b.height = centerH - (m.top+m.bottom);
37261             var totalWidth = (b.width + m.left + m.right);
37262             b.x = w - totalWidth + m.left;
37263             b.y = centerY + m.top;
37264             centerW -= totalWidth;
37265             east.updateBox(this.safeBox(b));
37266         }
37267         if(center){
37268             var m = center.getMargins();
37269             var centerBox = {
37270                 x: centerX + m.left,
37271                 y: centerY + m.top,
37272                 width: centerW - (m.left+m.right),
37273                 height: centerH - (m.top+m.bottom)
37274             };
37275             //if(this.hideOnLayout){
37276                 //center.el.setStyle("display", "block");
37277             //}
37278             center.updateBox(this.safeBox(centerBox));
37279         }
37280         this.el.repaint();
37281         this.fireEvent("layout", this);
37282     },
37283
37284     // private
37285     safeBox : function(box){
37286         box.width = Math.max(0, box.width);
37287         box.height = Math.max(0, box.height);
37288         return box;
37289     },
37290
37291     /**
37292      * Adds a ContentPanel (or subclass) to this layout.
37293      * @param {String} target The target region key (north, south, east, west or center).
37294      * @param {Roo.ContentPanel} panel The panel to add
37295      * @return {Roo.ContentPanel} The added panel
37296      */
37297     add : function(target, panel){
37298          
37299         target = target.toLowerCase();
37300         return this.regions[target].add(panel);
37301     },
37302
37303     /**
37304      * Remove a ContentPanel (or subclass) to this layout.
37305      * @param {String} target The target region key (north, south, east, west or center).
37306      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37307      * @return {Roo.ContentPanel} The removed panel
37308      */
37309     remove : function(target, panel){
37310         target = target.toLowerCase();
37311         return this.regions[target].remove(panel);
37312     },
37313
37314     /**
37315      * Searches all regions for a panel with the specified id
37316      * @param {String} panelId
37317      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37318      */
37319     findPanel : function(panelId){
37320         var rs = this.regions;
37321         for(var target in rs){
37322             if(typeof rs[target] != "function"){
37323                 var p = rs[target].getPanel(panelId);
37324                 if(p){
37325                     return p;
37326                 }
37327             }
37328         }
37329         return null;
37330     },
37331
37332     /**
37333      * Searches all regions for a panel with the specified id and activates (shows) it.
37334      * @param {String/ContentPanel} panelId The panels id or the panel itself
37335      * @return {Roo.ContentPanel} The shown panel or null
37336      */
37337     showPanel : function(panelId) {
37338       var rs = this.regions;
37339       for(var target in rs){
37340          var r = rs[target];
37341          if(typeof r != "function"){
37342             if(r.hasPanel(panelId)){
37343                return r.showPanel(panelId);
37344             }
37345          }
37346       }
37347       return null;
37348    },
37349
37350    /**
37351      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37352      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37353      */
37354    /*
37355     restoreState : function(provider){
37356         if(!provider){
37357             provider = Roo.state.Manager;
37358         }
37359         var sm = new Roo.LayoutStateManager();
37360         sm.init(this, provider);
37361     },
37362 */
37363  
37364  
37365     /**
37366      * Adds a xtype elements to the layout.
37367      * <pre><code>
37368
37369 layout.addxtype({
37370        xtype : 'ContentPanel',
37371        region: 'west',
37372        items: [ .... ]
37373    }
37374 );
37375
37376 layout.addxtype({
37377         xtype : 'NestedLayoutPanel',
37378         region: 'west',
37379         layout: {
37380            center: { },
37381            west: { }   
37382         },
37383         items : [ ... list of content panels or nested layout panels.. ]
37384    }
37385 );
37386 </code></pre>
37387      * @param {Object} cfg Xtype definition of item to add.
37388      */
37389     addxtype : function(cfg)
37390     {
37391         // basically accepts a pannel...
37392         // can accept a layout region..!?!?
37393         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37394         
37395         
37396         // theory?  children can only be panels??
37397         
37398         //if (!cfg.xtype.match(/Panel$/)) {
37399         //    return false;
37400         //}
37401         var ret = false;
37402         
37403         if (typeof(cfg.region) == 'undefined') {
37404             Roo.log("Failed to add Panel, region was not set");
37405             Roo.log(cfg);
37406             return false;
37407         }
37408         var region = cfg.region;
37409         delete cfg.region;
37410         
37411           
37412         var xitems = [];
37413         if (cfg.items) {
37414             xitems = cfg.items;
37415             delete cfg.items;
37416         }
37417         var nb = false;
37418         
37419         if ( region == 'center') {
37420             Roo.log("Center: " + cfg.title);
37421         }
37422         
37423         
37424         switch(cfg.xtype) 
37425         {
37426             case 'Content':  // ContentPanel (el, cfg)
37427             case 'Scroll':  // ContentPanel (el, cfg)
37428             case 'View': 
37429                 cfg.autoCreate = cfg.autoCreate || true;
37430                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37431                 //} else {
37432                 //    var el = this.el.createChild();
37433                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37434                 //}
37435                 
37436                 this.add(region, ret);
37437                 break;
37438             
37439             /*
37440             case 'TreePanel': // our new panel!
37441                 cfg.el = this.el.createChild();
37442                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37443                 this.add(region, ret);
37444                 break;
37445             */
37446             
37447             case 'Nest': 
37448                 // create a new Layout (which is  a Border Layout...
37449                 
37450                 var clayout = cfg.layout;
37451                 clayout.el  = this.el.createChild();
37452                 clayout.items   = clayout.items  || [];
37453                 
37454                 delete cfg.layout;
37455                 
37456                 // replace this exitems with the clayout ones..
37457                 xitems = clayout.items;
37458                  
37459                 // force background off if it's in center...
37460                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37461                     cfg.background = false;
37462                 }
37463                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37464                 
37465                 
37466                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37467                 //console.log('adding nested layout panel '  + cfg.toSource());
37468                 this.add(region, ret);
37469                 nb = {}; /// find first...
37470                 break;
37471             
37472             case 'Grid':
37473                 
37474                 // needs grid and region
37475                 
37476                 //var el = this.getRegion(region).el.createChild();
37477                 /*
37478                  *var el = this.el.createChild();
37479                 // create the grid first...
37480                 cfg.grid.container = el;
37481                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37482                 */
37483                 
37484                 if (region == 'center' && this.active ) {
37485                     cfg.background = false;
37486                 }
37487                 
37488                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37489                 
37490                 this.add(region, ret);
37491                 /*
37492                 if (cfg.background) {
37493                     // render grid on panel activation (if panel background)
37494                     ret.on('activate', function(gp) {
37495                         if (!gp.grid.rendered) {
37496                     //        gp.grid.render(el);
37497                         }
37498                     });
37499                 } else {
37500                   //  cfg.grid.render(el);
37501                 }
37502                 */
37503                 break;
37504            
37505            
37506             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37507                 // it was the old xcomponent building that caused this before.
37508                 // espeically if border is the top element in the tree.
37509                 ret = this;
37510                 break; 
37511                 
37512                     
37513                 
37514                 
37515                 
37516             default:
37517                 /*
37518                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37519                     
37520                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37521                     this.add(region, ret);
37522                 } else {
37523                 */
37524                     Roo.log(cfg);
37525                     throw "Can not add '" + cfg.xtype + "' to Border";
37526                     return null;
37527              
37528                                 
37529              
37530         }
37531         this.beginUpdate();
37532         // add children..
37533         var region = '';
37534         var abn = {};
37535         Roo.each(xitems, function(i)  {
37536             region = nb && i.region ? i.region : false;
37537             
37538             var add = ret.addxtype(i);
37539            
37540             if (region) {
37541                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37542                 if (!i.background) {
37543                     abn[region] = nb[region] ;
37544                 }
37545             }
37546             
37547         });
37548         this.endUpdate();
37549
37550         // make the last non-background panel active..
37551         //if (nb) { Roo.log(abn); }
37552         if (nb) {
37553             
37554             for(var r in abn) {
37555                 region = this.getRegion(r);
37556                 if (region) {
37557                     // tried using nb[r], but it does not work..
37558                      
37559                     region.showPanel(abn[r]);
37560                    
37561                 }
37562             }
37563         }
37564         return ret;
37565         
37566     },
37567     
37568     
37569 // private
37570     factory : function(cfg)
37571     {
37572         
37573         var validRegions = Roo.bootstrap.layout.Border.regions;
37574
37575         var target = cfg.region;
37576         cfg.mgr = this;
37577         
37578         var r = Roo.bootstrap.layout;
37579         Roo.log(target);
37580         switch(target){
37581             case "north":
37582                 return new r.North(cfg);
37583             case "south":
37584                 return new r.South(cfg);
37585             case "east":
37586                 return new r.East(cfg);
37587             case "west":
37588                 return new r.West(cfg);
37589             case "center":
37590                 return new r.Center(cfg);
37591         }
37592         throw 'Layout region "'+target+'" not supported.';
37593     }
37594     
37595     
37596 });
37597  /*
37598  * Based on:
37599  * Ext JS Library 1.1.1
37600  * Copyright(c) 2006-2007, Ext JS, LLC.
37601  *
37602  * Originally Released Under LGPL - original licence link has changed is not relivant.
37603  *
37604  * Fork - LGPL
37605  * <script type="text/javascript">
37606  */
37607  
37608 /**
37609  * @class Roo.bootstrap.layout.Basic
37610  * @extends Roo.util.Observable
37611  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37612  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37613  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37614  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37615  * @cfg {string}   region  the region that it inhabits..
37616  * @cfg {bool}   skipConfig skip config?
37617  * 
37618
37619  */
37620 Roo.bootstrap.layout.Basic = function(config){
37621     
37622     this.mgr = config.mgr;
37623     
37624     this.position = config.region;
37625     
37626     var skipConfig = config.skipConfig;
37627     
37628     this.events = {
37629         /**
37630          * @scope Roo.BasicLayoutRegion
37631          */
37632         
37633         /**
37634          * @event beforeremove
37635          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37636          * @param {Roo.LayoutRegion} this
37637          * @param {Roo.ContentPanel} panel The panel
37638          * @param {Object} e The cancel event object
37639          */
37640         "beforeremove" : true,
37641         /**
37642          * @event invalidated
37643          * Fires when the layout for this region is changed.
37644          * @param {Roo.LayoutRegion} this
37645          */
37646         "invalidated" : true,
37647         /**
37648          * @event visibilitychange
37649          * Fires when this region is shown or hidden 
37650          * @param {Roo.LayoutRegion} this
37651          * @param {Boolean} visibility true or false
37652          */
37653         "visibilitychange" : true,
37654         /**
37655          * @event paneladded
37656          * Fires when a panel is added. 
37657          * @param {Roo.LayoutRegion} this
37658          * @param {Roo.ContentPanel} panel The panel
37659          */
37660         "paneladded" : true,
37661         /**
37662          * @event panelremoved
37663          * Fires when a panel is removed. 
37664          * @param {Roo.LayoutRegion} this
37665          * @param {Roo.ContentPanel} panel The panel
37666          */
37667         "panelremoved" : true,
37668         /**
37669          * @event beforecollapse
37670          * Fires when this region before collapse.
37671          * @param {Roo.LayoutRegion} this
37672          */
37673         "beforecollapse" : true,
37674         /**
37675          * @event collapsed
37676          * Fires when this region is collapsed.
37677          * @param {Roo.LayoutRegion} this
37678          */
37679         "collapsed" : true,
37680         /**
37681          * @event expanded
37682          * Fires when this region is expanded.
37683          * @param {Roo.LayoutRegion} this
37684          */
37685         "expanded" : true,
37686         /**
37687          * @event slideshow
37688          * Fires when this region is slid into view.
37689          * @param {Roo.LayoutRegion} this
37690          */
37691         "slideshow" : true,
37692         /**
37693          * @event slidehide
37694          * Fires when this region slides out of view. 
37695          * @param {Roo.LayoutRegion} this
37696          */
37697         "slidehide" : true,
37698         /**
37699          * @event panelactivated
37700          * Fires when a panel is activated. 
37701          * @param {Roo.LayoutRegion} this
37702          * @param {Roo.ContentPanel} panel The activated panel
37703          */
37704         "panelactivated" : true,
37705         /**
37706          * @event resized
37707          * Fires when the user resizes this region. 
37708          * @param {Roo.LayoutRegion} this
37709          * @param {Number} newSize The new size (width for east/west, height for north/south)
37710          */
37711         "resized" : true
37712     };
37713     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37714     this.panels = new Roo.util.MixedCollection();
37715     this.panels.getKey = this.getPanelId.createDelegate(this);
37716     this.box = null;
37717     this.activePanel = null;
37718     // ensure listeners are added...
37719     
37720     if (config.listeners || config.events) {
37721         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37722             listeners : config.listeners || {},
37723             events : config.events || {}
37724         });
37725     }
37726     
37727     if(skipConfig !== true){
37728         this.applyConfig(config);
37729     }
37730 };
37731
37732 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37733 {
37734     getPanelId : function(p){
37735         return p.getId();
37736     },
37737     
37738     applyConfig : function(config){
37739         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37740         this.config = config;
37741         
37742     },
37743     
37744     /**
37745      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37746      * the width, for horizontal (north, south) the height.
37747      * @param {Number} newSize The new width or height
37748      */
37749     resizeTo : function(newSize){
37750         var el = this.el ? this.el :
37751                  (this.activePanel ? this.activePanel.getEl() : null);
37752         if(el){
37753             switch(this.position){
37754                 case "east":
37755                 case "west":
37756                     el.setWidth(newSize);
37757                     this.fireEvent("resized", this, newSize);
37758                 break;
37759                 case "north":
37760                 case "south":
37761                     el.setHeight(newSize);
37762                     this.fireEvent("resized", this, newSize);
37763                 break;                
37764             }
37765         }
37766     },
37767     
37768     getBox : function(){
37769         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37770     },
37771     
37772     getMargins : function(){
37773         return this.margins;
37774     },
37775     
37776     updateBox : function(box){
37777         this.box = box;
37778         var el = this.activePanel.getEl();
37779         el.dom.style.left = box.x + "px";
37780         el.dom.style.top = box.y + "px";
37781         this.activePanel.setSize(box.width, box.height);
37782     },
37783     
37784     /**
37785      * Returns the container element for this region.
37786      * @return {Roo.Element}
37787      */
37788     getEl : function(){
37789         return this.activePanel;
37790     },
37791     
37792     /**
37793      * Returns true if this region is currently visible.
37794      * @return {Boolean}
37795      */
37796     isVisible : function(){
37797         return this.activePanel ? true : false;
37798     },
37799     
37800     setActivePanel : function(panel){
37801         panel = this.getPanel(panel);
37802         if(this.activePanel && this.activePanel != panel){
37803             this.activePanel.setActiveState(false);
37804             this.activePanel.getEl().setLeftTop(-10000,-10000);
37805         }
37806         this.activePanel = panel;
37807         panel.setActiveState(true);
37808         if(this.box){
37809             panel.setSize(this.box.width, this.box.height);
37810         }
37811         this.fireEvent("panelactivated", this, panel);
37812         this.fireEvent("invalidated");
37813     },
37814     
37815     /**
37816      * Show the specified panel.
37817      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37818      * @return {Roo.ContentPanel} The shown panel or null
37819      */
37820     showPanel : function(panel){
37821         panel = this.getPanel(panel);
37822         if(panel){
37823             this.setActivePanel(panel);
37824         }
37825         return panel;
37826     },
37827     
37828     /**
37829      * Get the active panel for this region.
37830      * @return {Roo.ContentPanel} The active panel or null
37831      */
37832     getActivePanel : function(){
37833         return this.activePanel;
37834     },
37835     
37836     /**
37837      * Add the passed ContentPanel(s)
37838      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37839      * @return {Roo.ContentPanel} The panel added (if only one was added)
37840      */
37841     add : function(panel){
37842         if(arguments.length > 1){
37843             for(var i = 0, len = arguments.length; i < len; i++) {
37844                 this.add(arguments[i]);
37845             }
37846             return null;
37847         }
37848         if(this.hasPanel(panel)){
37849             this.showPanel(panel);
37850             return panel;
37851         }
37852         var el = panel.getEl();
37853         if(el.dom.parentNode != this.mgr.el.dom){
37854             this.mgr.el.dom.appendChild(el.dom);
37855         }
37856         if(panel.setRegion){
37857             panel.setRegion(this);
37858         }
37859         this.panels.add(panel);
37860         el.setStyle("position", "absolute");
37861         if(!panel.background){
37862             this.setActivePanel(panel);
37863             if(this.config.initialSize && this.panels.getCount()==1){
37864                 this.resizeTo(this.config.initialSize);
37865             }
37866         }
37867         this.fireEvent("paneladded", this, panel);
37868         return panel;
37869     },
37870     
37871     /**
37872      * Returns true if the panel is in this region.
37873      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37874      * @return {Boolean}
37875      */
37876     hasPanel : function(panel){
37877         if(typeof panel == "object"){ // must be panel obj
37878             panel = panel.getId();
37879         }
37880         return this.getPanel(panel) ? true : false;
37881     },
37882     
37883     /**
37884      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37885      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37886      * @param {Boolean} preservePanel Overrides the config preservePanel option
37887      * @return {Roo.ContentPanel} The panel that was removed
37888      */
37889     remove : function(panel, preservePanel){
37890         panel = this.getPanel(panel);
37891         if(!panel){
37892             return null;
37893         }
37894         var e = {};
37895         this.fireEvent("beforeremove", this, panel, e);
37896         if(e.cancel === true){
37897             return null;
37898         }
37899         var panelId = panel.getId();
37900         this.panels.removeKey(panelId);
37901         return panel;
37902     },
37903     
37904     /**
37905      * Returns the panel specified or null if it's not in this region.
37906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37907      * @return {Roo.ContentPanel}
37908      */
37909     getPanel : function(id){
37910         if(typeof id == "object"){ // must be panel obj
37911             return id;
37912         }
37913         return this.panels.get(id);
37914     },
37915     
37916     /**
37917      * Returns this regions position (north/south/east/west/center).
37918      * @return {String} 
37919      */
37920     getPosition: function(){
37921         return this.position;    
37922     }
37923 });/*
37924  * Based on:
37925  * Ext JS Library 1.1.1
37926  * Copyright(c) 2006-2007, Ext JS, LLC.
37927  *
37928  * Originally Released Under LGPL - original licence link has changed is not relivant.
37929  *
37930  * Fork - LGPL
37931  * <script type="text/javascript">
37932  */
37933  
37934 /**
37935  * @class Roo.bootstrap.layout.Region
37936  * @extends Roo.bootstrap.layout.Basic
37937  * This class represents a region in a layout manager.
37938  
37939  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37940  * @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})
37941  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37942  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37943  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37944  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37945  * @cfg {String}    title           The title for the region (overrides panel titles)
37946  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37947  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37948  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37949  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37950  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37951  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37952  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37953  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37954  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37955  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37956
37957  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37958  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37959  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37960  * @cfg {Number}    width           For East/West panels
37961  * @cfg {Number}    height          For North/South panels
37962  * @cfg {Boolean}   split           To show the splitter
37963  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37964  * 
37965  * @cfg {string}   cls             Extra CSS classes to add to region
37966  * 
37967  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37968  * @cfg {string}   region  the region that it inhabits..
37969  *
37970
37971  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37972  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37973
37974  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37975  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37976  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37977  */
37978 Roo.bootstrap.layout.Region = function(config)
37979 {
37980     this.applyConfig(config);
37981
37982     var mgr = config.mgr;
37983     var pos = config.region;
37984     config.skipConfig = true;
37985     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37986     
37987     if (mgr.el) {
37988         this.onRender(mgr.el);   
37989     }
37990      
37991     this.visible = true;
37992     this.collapsed = false;
37993     this.unrendered_panels = [];
37994 };
37995
37996 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37997
37998     position: '', // set by wrapper (eg. north/south etc..)
37999     unrendered_panels : null,  // unrendered panels.
38000     
38001     tabPosition : false,
38002     
38003     mgr: false, // points to 'Border'
38004     
38005     
38006     createBody : function(){
38007         /** This region's body element 
38008         * @type Roo.Element */
38009         this.bodyEl = this.el.createChild({
38010                 tag: "div",
38011                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38012         });
38013     },
38014
38015     onRender: function(ctr, pos)
38016     {
38017         var dh = Roo.DomHelper;
38018         /** This region's container element 
38019         * @type Roo.Element */
38020         this.el = dh.append(ctr.dom, {
38021                 tag: "div",
38022                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38023             }, true);
38024         /** This region's title element 
38025         * @type Roo.Element */
38026     
38027         this.titleEl = dh.append(this.el.dom,  {
38028                 tag: "div",
38029                 unselectable: "on",
38030                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38031                 children:[
38032                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38033                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38034                 ]
38035             }, true);
38036         
38037         this.titleEl.enableDisplayMode();
38038         /** This region's title text element 
38039         * @type HTMLElement */
38040         this.titleTextEl = this.titleEl.dom.firstChild;
38041         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38042         /*
38043         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38044         this.closeBtn.enableDisplayMode();
38045         this.closeBtn.on("click", this.closeClicked, this);
38046         this.closeBtn.hide();
38047     */
38048         this.createBody(this.config);
38049         if(this.config.hideWhenEmpty){
38050             this.hide();
38051             this.on("paneladded", this.validateVisibility, this);
38052             this.on("panelremoved", this.validateVisibility, this);
38053         }
38054         if(this.autoScroll){
38055             this.bodyEl.setStyle("overflow", "auto");
38056         }else{
38057             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38058         }
38059         //if(c.titlebar !== false){
38060             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38061                 this.titleEl.hide();
38062             }else{
38063                 this.titleEl.show();
38064                 if(this.config.title){
38065                     this.titleTextEl.innerHTML = this.config.title;
38066                 }
38067             }
38068         //}
38069         if(this.config.collapsed){
38070             this.collapse(true);
38071         }
38072         if(this.config.hidden){
38073             this.hide();
38074         }
38075         
38076         if (this.unrendered_panels && this.unrendered_panels.length) {
38077             for (var i =0;i< this.unrendered_panels.length; i++) {
38078                 this.add(this.unrendered_panels[i]);
38079             }
38080             this.unrendered_panels = null;
38081             
38082         }
38083         
38084     },
38085     
38086     applyConfig : function(c)
38087     {
38088         /*
38089          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38090             var dh = Roo.DomHelper;
38091             if(c.titlebar !== false){
38092                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38093                 this.collapseBtn.on("click", this.collapse, this);
38094                 this.collapseBtn.enableDisplayMode();
38095                 /*
38096                 if(c.showPin === true || this.showPin){
38097                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38098                     this.stickBtn.enableDisplayMode();
38099                     this.stickBtn.on("click", this.expand, this);
38100                     this.stickBtn.hide();
38101                 }
38102                 
38103             }
38104             */
38105             /** This region's collapsed element
38106             * @type Roo.Element */
38107             /*
38108              *
38109             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38110                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38111             ]}, true);
38112             
38113             if(c.floatable !== false){
38114                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38115                this.collapsedEl.on("click", this.collapseClick, this);
38116             }
38117
38118             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38119                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38120                    id: "message", unselectable: "on", style:{"float":"left"}});
38121                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38122              }
38123             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38124             this.expandBtn.on("click", this.expand, this);
38125             
38126         }
38127         
38128         if(this.collapseBtn){
38129             this.collapseBtn.setVisible(c.collapsible == true);
38130         }
38131         
38132         this.cmargins = c.cmargins || this.cmargins ||
38133                          (this.position == "west" || this.position == "east" ?
38134                              {top: 0, left: 2, right:2, bottom: 0} :
38135                              {top: 2, left: 0, right:0, bottom: 2});
38136         */
38137         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38138         
38139         
38140         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38141         
38142         this.autoScroll = c.autoScroll || false;
38143         
38144         
38145        
38146         
38147         this.duration = c.duration || .30;
38148         this.slideDuration = c.slideDuration || .45;
38149         this.config = c;
38150        
38151     },
38152     /**
38153      * Returns true if this region is currently visible.
38154      * @return {Boolean}
38155      */
38156     isVisible : function(){
38157         return this.visible;
38158     },
38159
38160     /**
38161      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38162      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38163      */
38164     //setCollapsedTitle : function(title){
38165     //    title = title || "&#160;";
38166      //   if(this.collapsedTitleTextEl){
38167       //      this.collapsedTitleTextEl.innerHTML = title;
38168        // }
38169     //},
38170
38171     getBox : function(){
38172         var b;
38173       //  if(!this.collapsed){
38174             b = this.el.getBox(false, true);
38175        // }else{
38176           //  b = this.collapsedEl.getBox(false, true);
38177         //}
38178         return b;
38179     },
38180
38181     getMargins : function(){
38182         return this.margins;
38183         //return this.collapsed ? this.cmargins : this.margins;
38184     },
38185 /*
38186     highlight : function(){
38187         this.el.addClass("x-layout-panel-dragover");
38188     },
38189
38190     unhighlight : function(){
38191         this.el.removeClass("x-layout-panel-dragover");
38192     },
38193 */
38194     updateBox : function(box)
38195     {
38196         if (!this.bodyEl) {
38197             return; // not rendered yet..
38198         }
38199         
38200         this.box = box;
38201         if(!this.collapsed){
38202             this.el.dom.style.left = box.x + "px";
38203             this.el.dom.style.top = box.y + "px";
38204             this.updateBody(box.width, box.height);
38205         }else{
38206             this.collapsedEl.dom.style.left = box.x + "px";
38207             this.collapsedEl.dom.style.top = box.y + "px";
38208             this.collapsedEl.setSize(box.width, box.height);
38209         }
38210         if(this.tabs){
38211             this.tabs.autoSizeTabs();
38212         }
38213     },
38214
38215     updateBody : function(w, h)
38216     {
38217         if(w !== null){
38218             this.el.setWidth(w);
38219             w -= this.el.getBorderWidth("rl");
38220             if(this.config.adjustments){
38221                 w += this.config.adjustments[0];
38222             }
38223         }
38224         if(h !== null && h > 0){
38225             this.el.setHeight(h);
38226             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38227             h -= this.el.getBorderWidth("tb");
38228             if(this.config.adjustments){
38229                 h += this.config.adjustments[1];
38230             }
38231             this.bodyEl.setHeight(h);
38232             if(this.tabs){
38233                 h = this.tabs.syncHeight(h);
38234             }
38235         }
38236         if(this.panelSize){
38237             w = w !== null ? w : this.panelSize.width;
38238             h = h !== null ? h : this.panelSize.height;
38239         }
38240         if(this.activePanel){
38241             var el = this.activePanel.getEl();
38242             w = w !== null ? w : el.getWidth();
38243             h = h !== null ? h : el.getHeight();
38244             this.panelSize = {width: w, height: h};
38245             this.activePanel.setSize(w, h);
38246         }
38247         if(Roo.isIE && this.tabs){
38248             this.tabs.el.repaint();
38249         }
38250     },
38251
38252     /**
38253      * Returns the container element for this region.
38254      * @return {Roo.Element}
38255      */
38256     getEl : function(){
38257         return this.el;
38258     },
38259
38260     /**
38261      * Hides this region.
38262      */
38263     hide : function(){
38264         //if(!this.collapsed){
38265             this.el.dom.style.left = "-2000px";
38266             this.el.hide();
38267         //}else{
38268          //   this.collapsedEl.dom.style.left = "-2000px";
38269          //   this.collapsedEl.hide();
38270        // }
38271         this.visible = false;
38272         this.fireEvent("visibilitychange", this, false);
38273     },
38274
38275     /**
38276      * Shows this region if it was previously hidden.
38277      */
38278     show : function(){
38279         //if(!this.collapsed){
38280             this.el.show();
38281         //}else{
38282         //    this.collapsedEl.show();
38283        // }
38284         this.visible = true;
38285         this.fireEvent("visibilitychange", this, true);
38286     },
38287 /*
38288     closeClicked : function(){
38289         if(this.activePanel){
38290             this.remove(this.activePanel);
38291         }
38292     },
38293
38294     collapseClick : function(e){
38295         if(this.isSlid){
38296            e.stopPropagation();
38297            this.slideIn();
38298         }else{
38299            e.stopPropagation();
38300            this.slideOut();
38301         }
38302     },
38303 */
38304     /**
38305      * Collapses this region.
38306      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38307      */
38308     /*
38309     collapse : function(skipAnim, skipCheck = false){
38310         if(this.collapsed) {
38311             return;
38312         }
38313         
38314         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38315             
38316             this.collapsed = true;
38317             if(this.split){
38318                 this.split.el.hide();
38319             }
38320             if(this.config.animate && skipAnim !== true){
38321                 this.fireEvent("invalidated", this);
38322                 this.animateCollapse();
38323             }else{
38324                 this.el.setLocation(-20000,-20000);
38325                 this.el.hide();
38326                 this.collapsedEl.show();
38327                 this.fireEvent("collapsed", this);
38328                 this.fireEvent("invalidated", this);
38329             }
38330         }
38331         
38332     },
38333 */
38334     animateCollapse : function(){
38335         // overridden
38336     },
38337
38338     /**
38339      * Expands this region if it was previously collapsed.
38340      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38341      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38342      */
38343     /*
38344     expand : function(e, skipAnim){
38345         if(e) {
38346             e.stopPropagation();
38347         }
38348         if(!this.collapsed || this.el.hasActiveFx()) {
38349             return;
38350         }
38351         if(this.isSlid){
38352             this.afterSlideIn();
38353             skipAnim = true;
38354         }
38355         this.collapsed = false;
38356         if(this.config.animate && skipAnim !== true){
38357             this.animateExpand();
38358         }else{
38359             this.el.show();
38360             if(this.split){
38361                 this.split.el.show();
38362             }
38363             this.collapsedEl.setLocation(-2000,-2000);
38364             this.collapsedEl.hide();
38365             this.fireEvent("invalidated", this);
38366             this.fireEvent("expanded", this);
38367         }
38368     },
38369 */
38370     animateExpand : function(){
38371         // overridden
38372     },
38373
38374     initTabs : function()
38375     {
38376         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38377         
38378         var ts = new Roo.bootstrap.panel.Tabs({
38379             el: this.bodyEl.dom,
38380             region : this,
38381             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38382             disableTooltips: this.config.disableTabTips,
38383             toolbar : this.config.toolbar
38384         });
38385         
38386         if(this.config.hideTabs){
38387             ts.stripWrap.setDisplayed(false);
38388         }
38389         this.tabs = ts;
38390         ts.resizeTabs = this.config.resizeTabs === true;
38391         ts.minTabWidth = this.config.minTabWidth || 40;
38392         ts.maxTabWidth = this.config.maxTabWidth || 250;
38393         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38394         ts.monitorResize = false;
38395         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38396         ts.bodyEl.addClass('roo-layout-tabs-body');
38397         this.panels.each(this.initPanelAsTab, this);
38398     },
38399
38400     initPanelAsTab : function(panel){
38401         var ti = this.tabs.addTab(
38402             panel.getEl().id,
38403             panel.getTitle(),
38404             null,
38405             this.config.closeOnTab && panel.isClosable(),
38406             panel.tpl
38407         );
38408         if(panel.tabTip !== undefined){
38409             ti.setTooltip(panel.tabTip);
38410         }
38411         ti.on("activate", function(){
38412               this.setActivePanel(panel);
38413         }, this);
38414         
38415         if(this.config.closeOnTab){
38416             ti.on("beforeclose", function(t, e){
38417                 e.cancel = true;
38418                 this.remove(panel);
38419             }, this);
38420         }
38421         
38422         panel.tabItem = ti;
38423         
38424         return ti;
38425     },
38426
38427     updatePanelTitle : function(panel, title)
38428     {
38429         if(this.activePanel == panel){
38430             this.updateTitle(title);
38431         }
38432         if(this.tabs){
38433             var ti = this.tabs.getTab(panel.getEl().id);
38434             ti.setText(title);
38435             if(panel.tabTip !== undefined){
38436                 ti.setTooltip(panel.tabTip);
38437             }
38438         }
38439     },
38440
38441     updateTitle : function(title){
38442         if(this.titleTextEl && !this.config.title){
38443             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38444         }
38445     },
38446
38447     setActivePanel : function(panel)
38448     {
38449         panel = this.getPanel(panel);
38450         if(this.activePanel && this.activePanel != panel){
38451             if(this.activePanel.setActiveState(false) === false){
38452                 return;
38453             }
38454         }
38455         this.activePanel = panel;
38456         panel.setActiveState(true);
38457         if(this.panelSize){
38458             panel.setSize(this.panelSize.width, this.panelSize.height);
38459         }
38460         if(this.closeBtn){
38461             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38462         }
38463         this.updateTitle(panel.getTitle());
38464         if(this.tabs){
38465             this.fireEvent("invalidated", this);
38466         }
38467         this.fireEvent("panelactivated", this, panel);
38468     },
38469
38470     /**
38471      * Shows the specified panel.
38472      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38473      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38474      */
38475     showPanel : function(panel)
38476     {
38477         panel = this.getPanel(panel);
38478         if(panel){
38479             if(this.tabs){
38480                 var tab = this.tabs.getTab(panel.getEl().id);
38481                 if(tab.isHidden()){
38482                     this.tabs.unhideTab(tab.id);
38483                 }
38484                 tab.activate();
38485             }else{
38486                 this.setActivePanel(panel);
38487             }
38488         }
38489         return panel;
38490     },
38491
38492     /**
38493      * Get the active panel for this region.
38494      * @return {Roo.ContentPanel} The active panel or null
38495      */
38496     getActivePanel : function(){
38497         return this.activePanel;
38498     },
38499
38500     validateVisibility : function(){
38501         if(this.panels.getCount() < 1){
38502             this.updateTitle("&#160;");
38503             this.closeBtn.hide();
38504             this.hide();
38505         }else{
38506             if(!this.isVisible()){
38507                 this.show();
38508             }
38509         }
38510     },
38511
38512     /**
38513      * Adds the passed ContentPanel(s) to this region.
38514      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38515      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38516      */
38517     add : function(panel)
38518     {
38519         if(arguments.length > 1){
38520             for(var i = 0, len = arguments.length; i < len; i++) {
38521                 this.add(arguments[i]);
38522             }
38523             return null;
38524         }
38525         
38526         // if we have not been rendered yet, then we can not really do much of this..
38527         if (!this.bodyEl) {
38528             this.unrendered_panels.push(panel);
38529             return panel;
38530         }
38531         
38532         
38533         
38534         
38535         if(this.hasPanel(panel)){
38536             this.showPanel(panel);
38537             return panel;
38538         }
38539         panel.setRegion(this);
38540         this.panels.add(panel);
38541        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38542             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38543             // and hide them... ???
38544             this.bodyEl.dom.appendChild(panel.getEl().dom);
38545             if(panel.background !== true){
38546                 this.setActivePanel(panel);
38547             }
38548             this.fireEvent("paneladded", this, panel);
38549             return panel;
38550         }
38551         */
38552         if(!this.tabs){
38553             this.initTabs();
38554         }else{
38555             this.initPanelAsTab(panel);
38556         }
38557         
38558         
38559         if(panel.background !== true){
38560             this.tabs.activate(panel.getEl().id);
38561         }
38562         this.fireEvent("paneladded", this, panel);
38563         return panel;
38564     },
38565
38566     /**
38567      * Hides the tab for the specified panel.
38568      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38569      */
38570     hidePanel : function(panel){
38571         if(this.tabs && (panel = this.getPanel(panel))){
38572             this.tabs.hideTab(panel.getEl().id);
38573         }
38574     },
38575
38576     /**
38577      * Unhides the tab for a previously hidden panel.
38578      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38579      */
38580     unhidePanel : function(panel){
38581         if(this.tabs && (panel = this.getPanel(panel))){
38582             this.tabs.unhideTab(panel.getEl().id);
38583         }
38584     },
38585
38586     clearPanels : function(){
38587         while(this.panels.getCount() > 0){
38588              this.remove(this.panels.first());
38589         }
38590     },
38591
38592     /**
38593      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38594      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38595      * @param {Boolean} preservePanel Overrides the config preservePanel option
38596      * @return {Roo.ContentPanel} The panel that was removed
38597      */
38598     remove : function(panel, preservePanel)
38599     {
38600         panel = this.getPanel(panel);
38601         if(!panel){
38602             return null;
38603         }
38604         var e = {};
38605         this.fireEvent("beforeremove", this, panel, e);
38606         if(e.cancel === true){
38607             return null;
38608         }
38609         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38610         var panelId = panel.getId();
38611         this.panels.removeKey(panelId);
38612         if(preservePanel){
38613             document.body.appendChild(panel.getEl().dom);
38614         }
38615         if(this.tabs){
38616             this.tabs.removeTab(panel.getEl().id);
38617         }else if (!preservePanel){
38618             this.bodyEl.dom.removeChild(panel.getEl().dom);
38619         }
38620         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38621             var p = this.panels.first();
38622             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38623             tempEl.appendChild(p.getEl().dom);
38624             this.bodyEl.update("");
38625             this.bodyEl.dom.appendChild(p.getEl().dom);
38626             tempEl = null;
38627             this.updateTitle(p.getTitle());
38628             this.tabs = null;
38629             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38630             this.setActivePanel(p);
38631         }
38632         panel.setRegion(null);
38633         if(this.activePanel == panel){
38634             this.activePanel = null;
38635         }
38636         if(this.config.autoDestroy !== false && preservePanel !== true){
38637             try{panel.destroy();}catch(e){}
38638         }
38639         this.fireEvent("panelremoved", this, panel);
38640         return panel;
38641     },
38642
38643     /**
38644      * Returns the TabPanel component used by this region
38645      * @return {Roo.TabPanel}
38646      */
38647     getTabs : function(){
38648         return this.tabs;
38649     },
38650
38651     createTool : function(parentEl, className){
38652         var btn = Roo.DomHelper.append(parentEl, {
38653             tag: "div",
38654             cls: "x-layout-tools-button",
38655             children: [ {
38656                 tag: "div",
38657                 cls: "roo-layout-tools-button-inner " + className,
38658                 html: "&#160;"
38659             }]
38660         }, true);
38661         btn.addClassOnOver("roo-layout-tools-button-over");
38662         return btn;
38663     }
38664 });/*
38665  * Based on:
38666  * Ext JS Library 1.1.1
38667  * Copyright(c) 2006-2007, Ext JS, LLC.
38668  *
38669  * Originally Released Under LGPL - original licence link has changed is not relivant.
38670  *
38671  * Fork - LGPL
38672  * <script type="text/javascript">
38673  */
38674  
38675
38676
38677 /**
38678  * @class Roo.SplitLayoutRegion
38679  * @extends Roo.LayoutRegion
38680  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38681  */
38682 Roo.bootstrap.layout.Split = function(config){
38683     this.cursor = config.cursor;
38684     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38685 };
38686
38687 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38688 {
38689     splitTip : "Drag to resize.",
38690     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38691     useSplitTips : false,
38692
38693     applyConfig : function(config){
38694         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38695     },
38696     
38697     onRender : function(ctr,pos) {
38698         
38699         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38700         if(!this.config.split){
38701             return;
38702         }
38703         if(!this.split){
38704             
38705             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38706                             tag: "div",
38707                             id: this.el.id + "-split",
38708                             cls: "roo-layout-split roo-layout-split-"+this.position,
38709                             html: "&#160;"
38710             });
38711             /** The SplitBar for this region 
38712             * @type Roo.SplitBar */
38713             // does not exist yet...
38714             Roo.log([this.position, this.orientation]);
38715             
38716             this.split = new Roo.bootstrap.SplitBar({
38717                 dragElement : splitEl,
38718                 resizingElement: this.el,
38719                 orientation : this.orientation
38720             });
38721             
38722             this.split.on("moved", this.onSplitMove, this);
38723             this.split.useShim = this.config.useShim === true;
38724             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38725             if(this.useSplitTips){
38726                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38727             }
38728             //if(config.collapsible){
38729             //    this.split.el.on("dblclick", this.collapse,  this);
38730             //}
38731         }
38732         if(typeof this.config.minSize != "undefined"){
38733             this.split.minSize = this.config.minSize;
38734         }
38735         if(typeof this.config.maxSize != "undefined"){
38736             this.split.maxSize = this.config.maxSize;
38737         }
38738         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38739             this.hideSplitter();
38740         }
38741         
38742     },
38743
38744     getHMaxSize : function(){
38745          var cmax = this.config.maxSize || 10000;
38746          var center = this.mgr.getRegion("center");
38747          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38748     },
38749
38750     getVMaxSize : function(){
38751          var cmax = this.config.maxSize || 10000;
38752          var center = this.mgr.getRegion("center");
38753          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38754     },
38755
38756     onSplitMove : function(split, newSize){
38757         this.fireEvent("resized", this, newSize);
38758     },
38759     
38760     /** 
38761      * Returns the {@link Roo.SplitBar} for this region.
38762      * @return {Roo.SplitBar}
38763      */
38764     getSplitBar : function(){
38765         return this.split;
38766     },
38767     
38768     hide : function(){
38769         this.hideSplitter();
38770         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38771     },
38772
38773     hideSplitter : function(){
38774         if(this.split){
38775             this.split.el.setLocation(-2000,-2000);
38776             this.split.el.hide();
38777         }
38778     },
38779
38780     show : function(){
38781         if(this.split){
38782             this.split.el.show();
38783         }
38784         Roo.bootstrap.layout.Split.superclass.show.call(this);
38785     },
38786     
38787     beforeSlide: function(){
38788         if(Roo.isGecko){// firefox overflow auto bug workaround
38789             this.bodyEl.clip();
38790             if(this.tabs) {
38791                 this.tabs.bodyEl.clip();
38792             }
38793             if(this.activePanel){
38794                 this.activePanel.getEl().clip();
38795                 
38796                 if(this.activePanel.beforeSlide){
38797                     this.activePanel.beforeSlide();
38798                 }
38799             }
38800         }
38801     },
38802     
38803     afterSlide : function(){
38804         if(Roo.isGecko){// firefox overflow auto bug workaround
38805             this.bodyEl.unclip();
38806             if(this.tabs) {
38807                 this.tabs.bodyEl.unclip();
38808             }
38809             if(this.activePanel){
38810                 this.activePanel.getEl().unclip();
38811                 if(this.activePanel.afterSlide){
38812                     this.activePanel.afterSlide();
38813                 }
38814             }
38815         }
38816     },
38817
38818     initAutoHide : function(){
38819         if(this.autoHide !== false){
38820             if(!this.autoHideHd){
38821                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38822                 this.autoHideHd = {
38823                     "mouseout": function(e){
38824                         if(!e.within(this.el, true)){
38825                             st.delay(500);
38826                         }
38827                     },
38828                     "mouseover" : function(e){
38829                         st.cancel();
38830                     },
38831                     scope : this
38832                 };
38833             }
38834             this.el.on(this.autoHideHd);
38835         }
38836     },
38837
38838     clearAutoHide : function(){
38839         if(this.autoHide !== false){
38840             this.el.un("mouseout", this.autoHideHd.mouseout);
38841             this.el.un("mouseover", this.autoHideHd.mouseover);
38842         }
38843     },
38844
38845     clearMonitor : function(){
38846         Roo.get(document).un("click", this.slideInIf, this);
38847     },
38848
38849     // these names are backwards but not changed for compat
38850     slideOut : function(){
38851         if(this.isSlid || this.el.hasActiveFx()){
38852             return;
38853         }
38854         this.isSlid = true;
38855         if(this.collapseBtn){
38856             this.collapseBtn.hide();
38857         }
38858         this.closeBtnState = this.closeBtn.getStyle('display');
38859         this.closeBtn.hide();
38860         if(this.stickBtn){
38861             this.stickBtn.show();
38862         }
38863         this.el.show();
38864         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38865         this.beforeSlide();
38866         this.el.setStyle("z-index", 10001);
38867         this.el.slideIn(this.getSlideAnchor(), {
38868             callback: function(){
38869                 this.afterSlide();
38870                 this.initAutoHide();
38871                 Roo.get(document).on("click", this.slideInIf, this);
38872                 this.fireEvent("slideshow", this);
38873             },
38874             scope: this,
38875             block: true
38876         });
38877     },
38878
38879     afterSlideIn : function(){
38880         this.clearAutoHide();
38881         this.isSlid = false;
38882         this.clearMonitor();
38883         this.el.setStyle("z-index", "");
38884         if(this.collapseBtn){
38885             this.collapseBtn.show();
38886         }
38887         this.closeBtn.setStyle('display', this.closeBtnState);
38888         if(this.stickBtn){
38889             this.stickBtn.hide();
38890         }
38891         this.fireEvent("slidehide", this);
38892     },
38893
38894     slideIn : function(cb){
38895         if(!this.isSlid || this.el.hasActiveFx()){
38896             Roo.callback(cb);
38897             return;
38898         }
38899         this.isSlid = false;
38900         this.beforeSlide();
38901         this.el.slideOut(this.getSlideAnchor(), {
38902             callback: function(){
38903                 this.el.setLeftTop(-10000, -10000);
38904                 this.afterSlide();
38905                 this.afterSlideIn();
38906                 Roo.callback(cb);
38907             },
38908             scope: this,
38909             block: true
38910         });
38911     },
38912     
38913     slideInIf : function(e){
38914         if(!e.within(this.el)){
38915             this.slideIn();
38916         }
38917     },
38918
38919     animateCollapse : function(){
38920         this.beforeSlide();
38921         this.el.setStyle("z-index", 20000);
38922         var anchor = this.getSlideAnchor();
38923         this.el.slideOut(anchor, {
38924             callback : function(){
38925                 this.el.setStyle("z-index", "");
38926                 this.collapsedEl.slideIn(anchor, {duration:.3});
38927                 this.afterSlide();
38928                 this.el.setLocation(-10000,-10000);
38929                 this.el.hide();
38930                 this.fireEvent("collapsed", this);
38931             },
38932             scope: this,
38933             block: true
38934         });
38935     },
38936
38937     animateExpand : function(){
38938         this.beforeSlide();
38939         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38940         this.el.setStyle("z-index", 20000);
38941         this.collapsedEl.hide({
38942             duration:.1
38943         });
38944         this.el.slideIn(this.getSlideAnchor(), {
38945             callback : function(){
38946                 this.el.setStyle("z-index", "");
38947                 this.afterSlide();
38948                 if(this.split){
38949                     this.split.el.show();
38950                 }
38951                 this.fireEvent("invalidated", this);
38952                 this.fireEvent("expanded", this);
38953             },
38954             scope: this,
38955             block: true
38956         });
38957     },
38958
38959     anchors : {
38960         "west" : "left",
38961         "east" : "right",
38962         "north" : "top",
38963         "south" : "bottom"
38964     },
38965
38966     sanchors : {
38967         "west" : "l",
38968         "east" : "r",
38969         "north" : "t",
38970         "south" : "b"
38971     },
38972
38973     canchors : {
38974         "west" : "tl-tr",
38975         "east" : "tr-tl",
38976         "north" : "tl-bl",
38977         "south" : "bl-tl"
38978     },
38979
38980     getAnchor : function(){
38981         return this.anchors[this.position];
38982     },
38983
38984     getCollapseAnchor : function(){
38985         return this.canchors[this.position];
38986     },
38987
38988     getSlideAnchor : function(){
38989         return this.sanchors[this.position];
38990     },
38991
38992     getAlignAdj : function(){
38993         var cm = this.cmargins;
38994         switch(this.position){
38995             case "west":
38996                 return [0, 0];
38997             break;
38998             case "east":
38999                 return [0, 0];
39000             break;
39001             case "north":
39002                 return [0, 0];
39003             break;
39004             case "south":
39005                 return [0, 0];
39006             break;
39007         }
39008     },
39009
39010     getExpandAdj : function(){
39011         var c = this.collapsedEl, cm = this.cmargins;
39012         switch(this.position){
39013             case "west":
39014                 return [-(cm.right+c.getWidth()+cm.left), 0];
39015             break;
39016             case "east":
39017                 return [cm.right+c.getWidth()+cm.left, 0];
39018             break;
39019             case "north":
39020                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39021             break;
39022             case "south":
39023                 return [0, cm.top+cm.bottom+c.getHeight()];
39024             break;
39025         }
39026     }
39027 });/*
39028  * Based on:
39029  * Ext JS Library 1.1.1
39030  * Copyright(c) 2006-2007, Ext JS, LLC.
39031  *
39032  * Originally Released Under LGPL - original licence link has changed is not relivant.
39033  *
39034  * Fork - LGPL
39035  * <script type="text/javascript">
39036  */
39037 /*
39038  * These classes are private internal classes
39039  */
39040 Roo.bootstrap.layout.Center = function(config){
39041     config.region = "center";
39042     Roo.bootstrap.layout.Region.call(this, config);
39043     this.visible = true;
39044     this.minWidth = config.minWidth || 20;
39045     this.minHeight = config.minHeight || 20;
39046 };
39047
39048 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39049     hide : function(){
39050         // center panel can't be hidden
39051     },
39052     
39053     show : function(){
39054         // center panel can't be hidden
39055     },
39056     
39057     getMinWidth: function(){
39058         return this.minWidth;
39059     },
39060     
39061     getMinHeight: function(){
39062         return this.minHeight;
39063     }
39064 });
39065
39066
39067
39068
39069  
39070
39071
39072
39073
39074
39075
39076 Roo.bootstrap.layout.North = function(config)
39077 {
39078     config.region = 'north';
39079     config.cursor = 'n-resize';
39080     
39081     Roo.bootstrap.layout.Split.call(this, config);
39082     
39083     
39084     if(this.split){
39085         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39086         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39087         this.split.el.addClass("roo-layout-split-v");
39088     }
39089     var size = config.initialSize || config.height;
39090     if(typeof size != "undefined"){
39091         this.el.setHeight(size);
39092     }
39093 };
39094 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39095 {
39096     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39097     
39098     
39099     
39100     getBox : function(){
39101         if(this.collapsed){
39102             return this.collapsedEl.getBox();
39103         }
39104         var box = this.el.getBox();
39105         if(this.split){
39106             box.height += this.split.el.getHeight();
39107         }
39108         return box;
39109     },
39110     
39111     updateBox : function(box){
39112         if(this.split && !this.collapsed){
39113             box.height -= this.split.el.getHeight();
39114             this.split.el.setLeft(box.x);
39115             this.split.el.setTop(box.y+box.height);
39116             this.split.el.setWidth(box.width);
39117         }
39118         if(this.collapsed){
39119             this.updateBody(box.width, null);
39120         }
39121         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39122     }
39123 });
39124
39125
39126
39127
39128
39129 Roo.bootstrap.layout.South = function(config){
39130     config.region = 'south';
39131     config.cursor = 's-resize';
39132     Roo.bootstrap.layout.Split.call(this, config);
39133     if(this.split){
39134         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39135         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39136         this.split.el.addClass("roo-layout-split-v");
39137     }
39138     var size = config.initialSize || config.height;
39139     if(typeof size != "undefined"){
39140         this.el.setHeight(size);
39141     }
39142 };
39143
39144 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39145     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39146     getBox : function(){
39147         if(this.collapsed){
39148             return this.collapsedEl.getBox();
39149         }
39150         var box = this.el.getBox();
39151         if(this.split){
39152             var sh = this.split.el.getHeight();
39153             box.height += sh;
39154             box.y -= sh;
39155         }
39156         return box;
39157     },
39158     
39159     updateBox : function(box){
39160         if(this.split && !this.collapsed){
39161             var sh = this.split.el.getHeight();
39162             box.height -= sh;
39163             box.y += sh;
39164             this.split.el.setLeft(box.x);
39165             this.split.el.setTop(box.y-sh);
39166             this.split.el.setWidth(box.width);
39167         }
39168         if(this.collapsed){
39169             this.updateBody(box.width, null);
39170         }
39171         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39172     }
39173 });
39174
39175 Roo.bootstrap.layout.East = function(config){
39176     config.region = "east";
39177     config.cursor = "e-resize";
39178     Roo.bootstrap.layout.Split.call(this, config);
39179     if(this.split){
39180         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39181         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39182         this.split.el.addClass("roo-layout-split-h");
39183     }
39184     var size = config.initialSize || config.width;
39185     if(typeof size != "undefined"){
39186         this.el.setWidth(size);
39187     }
39188 };
39189 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39190     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39191     getBox : function(){
39192         if(this.collapsed){
39193             return this.collapsedEl.getBox();
39194         }
39195         var box = this.el.getBox();
39196         if(this.split){
39197             var sw = this.split.el.getWidth();
39198             box.width += sw;
39199             box.x -= sw;
39200         }
39201         return box;
39202     },
39203
39204     updateBox : function(box){
39205         if(this.split && !this.collapsed){
39206             var sw = this.split.el.getWidth();
39207             box.width -= sw;
39208             this.split.el.setLeft(box.x);
39209             this.split.el.setTop(box.y);
39210             this.split.el.setHeight(box.height);
39211             box.x += sw;
39212         }
39213         if(this.collapsed){
39214             this.updateBody(null, box.height);
39215         }
39216         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39217     }
39218 });
39219
39220 Roo.bootstrap.layout.West = function(config){
39221     config.region = "west";
39222     config.cursor = "w-resize";
39223     
39224     Roo.bootstrap.layout.Split.call(this, config);
39225     if(this.split){
39226         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39227         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39228         this.split.el.addClass("roo-layout-split-h");
39229     }
39230     
39231 };
39232 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39233     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39234     
39235     onRender: function(ctr, pos)
39236     {
39237         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39238         var size = this.config.initialSize || this.config.width;
39239         if(typeof size != "undefined"){
39240             this.el.setWidth(size);
39241         }
39242     },
39243     
39244     getBox : function(){
39245         if(this.collapsed){
39246             return this.collapsedEl.getBox();
39247         }
39248         var box = this.el.getBox();
39249         if(this.split){
39250             box.width += this.split.el.getWidth();
39251         }
39252         return box;
39253     },
39254     
39255     updateBox : function(box){
39256         if(this.split && !this.collapsed){
39257             var sw = this.split.el.getWidth();
39258             box.width -= sw;
39259             this.split.el.setLeft(box.x+box.width);
39260             this.split.el.setTop(box.y);
39261             this.split.el.setHeight(box.height);
39262         }
39263         if(this.collapsed){
39264             this.updateBody(null, box.height);
39265         }
39266         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39267     }
39268 });Roo.namespace("Roo.bootstrap.panel");/*
39269  * Based on:
39270  * Ext JS Library 1.1.1
39271  * Copyright(c) 2006-2007, Ext JS, LLC.
39272  *
39273  * Originally Released Under LGPL - original licence link has changed is not relivant.
39274  *
39275  * Fork - LGPL
39276  * <script type="text/javascript">
39277  */
39278 /**
39279  * @class Roo.ContentPanel
39280  * @extends Roo.util.Observable
39281  * A basic ContentPanel element.
39282  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39283  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39284  * @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
39285  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39286  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39287  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39288  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39289  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39290  * @cfg {String} title          The title for this panel
39291  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39292  * @cfg {String} url            Calls {@link #setUrl} with this value
39293  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39294  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39295  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39296  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39297  * @cfg {Boolean} badges render the badges
39298  * @cfg {String} cls  extra classes to use  
39299  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39300
39301  * @constructor
39302  * Create a new ContentPanel.
39303  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39304  * @param {String/Object} config A string to set only the title or a config object
39305  * @param {String} content (optional) Set the HTML content for this panel
39306  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39307  */
39308 Roo.bootstrap.panel.Content = function( config){
39309     
39310     this.tpl = config.tpl || false;
39311     
39312     var el = config.el;
39313     var content = config.content;
39314
39315     if(config.autoCreate){ // xtype is available if this is called from factory
39316         el = Roo.id();
39317     }
39318     this.el = Roo.get(el);
39319     if(!this.el && config && config.autoCreate){
39320         if(typeof config.autoCreate == "object"){
39321             if(!config.autoCreate.id){
39322                 config.autoCreate.id = config.id||el;
39323             }
39324             this.el = Roo.DomHelper.append(document.body,
39325                         config.autoCreate, true);
39326         }else{
39327             var elcfg =  {
39328                 tag: "div",
39329                 cls: (config.cls || '') +
39330                     (config.background ? ' bg-' + config.background : '') +
39331                     " roo-layout-inactive-content",
39332                 id: config.id||el
39333             };
39334             if (config.html) {
39335                 elcfg.html = config.html;
39336                 
39337             }
39338                         
39339             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39340         }
39341     } 
39342     this.closable = false;
39343     this.loaded = false;
39344     this.active = false;
39345    
39346       
39347     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39348         
39349         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39350         
39351         this.wrapEl = this.el; //this.el.wrap();
39352         var ti = [];
39353         if (config.toolbar.items) {
39354             ti = config.toolbar.items ;
39355             delete config.toolbar.items ;
39356         }
39357         
39358         var nitems = [];
39359         this.toolbar.render(this.wrapEl, 'before');
39360         for(var i =0;i < ti.length;i++) {
39361           //  Roo.log(['add child', items[i]]);
39362             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39363         }
39364         this.toolbar.items = nitems;
39365         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39366         delete config.toolbar;
39367         
39368     }
39369     /*
39370     // xtype created footer. - not sure if will work as we normally have to render first..
39371     if (this.footer && !this.footer.el && this.footer.xtype) {
39372         if (!this.wrapEl) {
39373             this.wrapEl = this.el.wrap();
39374         }
39375     
39376         this.footer.container = this.wrapEl.createChild();
39377          
39378         this.footer = Roo.factory(this.footer, Roo);
39379         
39380     }
39381     */
39382     
39383      if(typeof config == "string"){
39384         this.title = config;
39385     }else{
39386         Roo.apply(this, config);
39387     }
39388     
39389     if(this.resizeEl){
39390         this.resizeEl = Roo.get(this.resizeEl, true);
39391     }else{
39392         this.resizeEl = this.el;
39393     }
39394     // handle view.xtype
39395     
39396  
39397     
39398     
39399     this.addEvents({
39400         /**
39401          * @event activate
39402          * Fires when this panel is activated. 
39403          * @param {Roo.ContentPanel} this
39404          */
39405         "activate" : true,
39406         /**
39407          * @event deactivate
39408          * Fires when this panel is activated. 
39409          * @param {Roo.ContentPanel} this
39410          */
39411         "deactivate" : true,
39412
39413         /**
39414          * @event resize
39415          * Fires when this panel is resized if fitToFrame is true.
39416          * @param {Roo.ContentPanel} this
39417          * @param {Number} width The width after any component adjustments
39418          * @param {Number} height The height after any component adjustments
39419          */
39420         "resize" : true,
39421         
39422          /**
39423          * @event render
39424          * Fires when this tab is created
39425          * @param {Roo.ContentPanel} this
39426          */
39427         "render" : true
39428         
39429         
39430         
39431     });
39432     
39433
39434     
39435     
39436     if(this.autoScroll){
39437         this.resizeEl.setStyle("overflow", "auto");
39438     } else {
39439         // fix randome scrolling
39440         //this.el.on('scroll', function() {
39441         //    Roo.log('fix random scolling');
39442         //    this.scrollTo('top',0); 
39443         //});
39444     }
39445     content = content || this.content;
39446     if(content){
39447         this.setContent(content);
39448     }
39449     if(config && config.url){
39450         this.setUrl(this.url, this.params, this.loadOnce);
39451     }
39452     
39453     
39454     
39455     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39456     
39457     if (this.view && typeof(this.view.xtype) != 'undefined') {
39458         this.view.el = this.el.appendChild(document.createElement("div"));
39459         this.view = Roo.factory(this.view); 
39460         this.view.render  &&  this.view.render(false, '');  
39461     }
39462     
39463     
39464     this.fireEvent('render', this);
39465 };
39466
39467 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39468     
39469     cls : '',
39470     background : '',
39471     
39472     tabTip : '',
39473     
39474     setRegion : function(region){
39475         this.region = region;
39476         this.setActiveClass(region && !this.background);
39477     },
39478     
39479     
39480     setActiveClass: function(state)
39481     {
39482         if(state){
39483            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39484            this.el.setStyle('position','relative');
39485         }else{
39486            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39487            this.el.setStyle('position', 'absolute');
39488         } 
39489     },
39490     
39491     /**
39492      * Returns the toolbar for this Panel if one was configured. 
39493      * @return {Roo.Toolbar} 
39494      */
39495     getToolbar : function(){
39496         return this.toolbar;
39497     },
39498     
39499     setActiveState : function(active)
39500     {
39501         this.active = active;
39502         this.setActiveClass(active);
39503         if(!active){
39504             if(this.fireEvent("deactivate", this) === false){
39505                 return false;
39506             }
39507             return true;
39508         }
39509         this.fireEvent("activate", this);
39510         return true;
39511     },
39512     /**
39513      * Updates this panel's element
39514      * @param {String} content The new content
39515      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39516     */
39517     setContent : function(content, loadScripts){
39518         this.el.update(content, loadScripts);
39519     },
39520
39521     ignoreResize : function(w, h){
39522         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39523             return true;
39524         }else{
39525             this.lastSize = {width: w, height: h};
39526             return false;
39527         }
39528     },
39529     /**
39530      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39531      * @return {Roo.UpdateManager} The UpdateManager
39532      */
39533     getUpdateManager : function(){
39534         return this.el.getUpdateManager();
39535     },
39536      /**
39537      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39538      * @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:
39539 <pre><code>
39540 panel.load({
39541     url: "your-url.php",
39542     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39543     callback: yourFunction,
39544     scope: yourObject, //(optional scope)
39545     discardUrl: false,
39546     nocache: false,
39547     text: "Loading...",
39548     timeout: 30,
39549     scripts: false
39550 });
39551 </code></pre>
39552      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39553      * 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.
39554      * @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}
39555      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39556      * @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.
39557      * @return {Roo.ContentPanel} this
39558      */
39559     load : function(){
39560         var um = this.el.getUpdateManager();
39561         um.update.apply(um, arguments);
39562         return this;
39563     },
39564
39565
39566     /**
39567      * 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.
39568      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39569      * @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)
39570      * @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)
39571      * @return {Roo.UpdateManager} The UpdateManager
39572      */
39573     setUrl : function(url, params, loadOnce){
39574         if(this.refreshDelegate){
39575             this.removeListener("activate", this.refreshDelegate);
39576         }
39577         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39578         this.on("activate", this.refreshDelegate);
39579         return this.el.getUpdateManager();
39580     },
39581     
39582     _handleRefresh : function(url, params, loadOnce){
39583         if(!loadOnce || !this.loaded){
39584             var updater = this.el.getUpdateManager();
39585             updater.update(url, params, this._setLoaded.createDelegate(this));
39586         }
39587     },
39588     
39589     _setLoaded : function(){
39590         this.loaded = true;
39591     }, 
39592     
39593     /**
39594      * Returns this panel's id
39595      * @return {String} 
39596      */
39597     getId : function(){
39598         return this.el.id;
39599     },
39600     
39601     /** 
39602      * Returns this panel's element - used by regiosn to add.
39603      * @return {Roo.Element} 
39604      */
39605     getEl : function(){
39606         return this.wrapEl || this.el;
39607     },
39608     
39609    
39610     
39611     adjustForComponents : function(width, height)
39612     {
39613         //Roo.log('adjustForComponents ');
39614         if(this.resizeEl != this.el){
39615             width -= this.el.getFrameWidth('lr');
39616             height -= this.el.getFrameWidth('tb');
39617         }
39618         if(this.toolbar){
39619             var te = this.toolbar.getEl();
39620             te.setWidth(width);
39621             height -= te.getHeight();
39622         }
39623         if(this.footer){
39624             var te = this.footer.getEl();
39625             te.setWidth(width);
39626             height -= te.getHeight();
39627         }
39628         
39629         
39630         if(this.adjustments){
39631             width += this.adjustments[0];
39632             height += this.adjustments[1];
39633         }
39634         return {"width": width, "height": height};
39635     },
39636     
39637     setSize : function(width, height){
39638         if(this.fitToFrame && !this.ignoreResize(width, height)){
39639             if(this.fitContainer && this.resizeEl != this.el){
39640                 this.el.setSize(width, height);
39641             }
39642             var size = this.adjustForComponents(width, height);
39643             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39644             this.fireEvent('resize', this, size.width, size.height);
39645         }
39646     },
39647     
39648     /**
39649      * Returns this panel's title
39650      * @return {String} 
39651      */
39652     getTitle : function(){
39653         
39654         if (typeof(this.title) != 'object') {
39655             return this.title;
39656         }
39657         
39658         var t = '';
39659         for (var k in this.title) {
39660             if (!this.title.hasOwnProperty(k)) {
39661                 continue;
39662             }
39663             
39664             if (k.indexOf('-') >= 0) {
39665                 var s = k.split('-');
39666                 for (var i = 0; i<s.length; i++) {
39667                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39668                 }
39669             } else {
39670                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39671             }
39672         }
39673         return t;
39674     },
39675     
39676     /**
39677      * Set this panel's title
39678      * @param {String} title
39679      */
39680     setTitle : function(title){
39681         this.title = title;
39682         if(this.region){
39683             this.region.updatePanelTitle(this, title);
39684         }
39685     },
39686     
39687     /**
39688      * Returns true is this panel was configured to be closable
39689      * @return {Boolean} 
39690      */
39691     isClosable : function(){
39692         return this.closable;
39693     },
39694     
39695     beforeSlide : function(){
39696         this.el.clip();
39697         this.resizeEl.clip();
39698     },
39699     
39700     afterSlide : function(){
39701         this.el.unclip();
39702         this.resizeEl.unclip();
39703     },
39704     
39705     /**
39706      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39707      *   Will fail silently if the {@link #setUrl} method has not been called.
39708      *   This does not activate the panel, just updates its content.
39709      */
39710     refresh : function(){
39711         if(this.refreshDelegate){
39712            this.loaded = false;
39713            this.refreshDelegate();
39714         }
39715     },
39716     
39717     /**
39718      * Destroys this panel
39719      */
39720     destroy : function(){
39721         this.el.removeAllListeners();
39722         var tempEl = document.createElement("span");
39723         tempEl.appendChild(this.el.dom);
39724         tempEl.innerHTML = "";
39725         this.el.remove();
39726         this.el = null;
39727     },
39728     
39729     /**
39730      * form - if the content panel contains a form - this is a reference to it.
39731      * @type {Roo.form.Form}
39732      */
39733     form : false,
39734     /**
39735      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39736      *    This contains a reference to it.
39737      * @type {Roo.View}
39738      */
39739     view : false,
39740     
39741       /**
39742      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39743      * <pre><code>
39744
39745 layout.addxtype({
39746        xtype : 'Form',
39747        items: [ .... ]
39748    }
39749 );
39750
39751 </code></pre>
39752      * @param {Object} cfg Xtype definition of item to add.
39753      */
39754     
39755     
39756     getChildContainer: function () {
39757         return this.getEl();
39758     }
39759     
39760     
39761     /*
39762         var  ret = new Roo.factory(cfg);
39763         return ret;
39764         
39765         
39766         // add form..
39767         if (cfg.xtype.match(/^Form$/)) {
39768             
39769             var el;
39770             //if (this.footer) {
39771             //    el = this.footer.container.insertSibling(false, 'before');
39772             //} else {
39773                 el = this.el.createChild();
39774             //}
39775
39776             this.form = new  Roo.form.Form(cfg);
39777             
39778             
39779             if ( this.form.allItems.length) {
39780                 this.form.render(el.dom);
39781             }
39782             return this.form;
39783         }
39784         // should only have one of theses..
39785         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39786             // views.. should not be just added - used named prop 'view''
39787             
39788             cfg.el = this.el.appendChild(document.createElement("div"));
39789             // factory?
39790             
39791             var ret = new Roo.factory(cfg);
39792              
39793              ret.render && ret.render(false, ''); // render blank..
39794             this.view = ret;
39795             return ret;
39796         }
39797         return false;
39798     }
39799     \*/
39800 });
39801  
39802 /**
39803  * @class Roo.bootstrap.panel.Grid
39804  * @extends Roo.bootstrap.panel.Content
39805  * @constructor
39806  * Create a new GridPanel.
39807  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39808  * @param {Object} config A the config object
39809   
39810  */
39811
39812
39813
39814 Roo.bootstrap.panel.Grid = function(config)
39815 {
39816     
39817       
39818     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39819         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39820
39821     config.el = this.wrapper;
39822     //this.el = this.wrapper;
39823     
39824       if (config.container) {
39825         // ctor'ed from a Border/panel.grid
39826         
39827         
39828         this.wrapper.setStyle("overflow", "hidden");
39829         this.wrapper.addClass('roo-grid-container');
39830
39831     }
39832     
39833     
39834     if(config.toolbar){
39835         var tool_el = this.wrapper.createChild();    
39836         this.toolbar = Roo.factory(config.toolbar);
39837         var ti = [];
39838         if (config.toolbar.items) {
39839             ti = config.toolbar.items ;
39840             delete config.toolbar.items ;
39841         }
39842         
39843         var nitems = [];
39844         this.toolbar.render(tool_el);
39845         for(var i =0;i < ti.length;i++) {
39846           //  Roo.log(['add child', items[i]]);
39847             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39848         }
39849         this.toolbar.items = nitems;
39850         
39851         delete config.toolbar;
39852     }
39853     
39854     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39855     config.grid.scrollBody = true;;
39856     config.grid.monitorWindowResize = false; // turn off autosizing
39857     config.grid.autoHeight = false;
39858     config.grid.autoWidth = false;
39859     
39860     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39861     
39862     if (config.background) {
39863         // render grid on panel activation (if panel background)
39864         this.on('activate', function(gp) {
39865             if (!gp.grid.rendered) {
39866                 gp.grid.render(this.wrapper);
39867                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39868             }
39869         });
39870             
39871     } else {
39872         this.grid.render(this.wrapper);
39873         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39874
39875     }
39876     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39877     // ??? needed ??? config.el = this.wrapper;
39878     
39879     
39880     
39881   
39882     // xtype created footer. - not sure if will work as we normally have to render first..
39883     if (this.footer && !this.footer.el && this.footer.xtype) {
39884         
39885         var ctr = this.grid.getView().getFooterPanel(true);
39886         this.footer.dataSource = this.grid.dataSource;
39887         this.footer = Roo.factory(this.footer, Roo);
39888         this.footer.render(ctr);
39889         
39890     }
39891     
39892     
39893     
39894     
39895      
39896 };
39897
39898 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39899     getId : function(){
39900         return this.grid.id;
39901     },
39902     
39903     /**
39904      * Returns the grid for this panel
39905      * @return {Roo.bootstrap.Table} 
39906      */
39907     getGrid : function(){
39908         return this.grid;    
39909     },
39910     
39911     setSize : function(width, height){
39912         if(!this.ignoreResize(width, height)){
39913             var grid = this.grid;
39914             var size = this.adjustForComponents(width, height);
39915             // tfoot is not a footer?
39916           
39917             
39918             var gridel = grid.getGridEl();
39919             gridel.setSize(size.width, size.height);
39920             
39921             var tbd = grid.getGridEl().select('tbody', true).first();
39922             var thd = grid.getGridEl().select('thead',true).first();
39923             var tbf= grid.getGridEl().select('tfoot', true).first();
39924
39925             if (tbf) {
39926                 size.height -= thd.getHeight();
39927             }
39928             if (thd) {
39929                 size.height -= thd.getHeight();
39930             }
39931             
39932             tbd.setSize(size.width, size.height );
39933             // this is for the account management tab -seems to work there.
39934             var thd = grid.getGridEl().select('thead',true).first();
39935             //if (tbd) {
39936             //    tbd.setSize(size.width, size.height - thd.getHeight());
39937             //}
39938              
39939             grid.autoSize();
39940         }
39941     },
39942      
39943     
39944     
39945     beforeSlide : function(){
39946         this.grid.getView().scroller.clip();
39947     },
39948     
39949     afterSlide : function(){
39950         this.grid.getView().scroller.unclip();
39951     },
39952     
39953     destroy : function(){
39954         this.grid.destroy();
39955         delete this.grid;
39956         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39957     }
39958 });
39959
39960 /**
39961  * @class Roo.bootstrap.panel.Nest
39962  * @extends Roo.bootstrap.panel.Content
39963  * @constructor
39964  * Create a new Panel, that can contain a layout.Border.
39965  * 
39966  * 
39967  * @param {Roo.BorderLayout} layout The layout for this panel
39968  * @param {String/Object} config A string to set only the title or a config object
39969  */
39970 Roo.bootstrap.panel.Nest = function(config)
39971 {
39972     // construct with only one argument..
39973     /* FIXME - implement nicer consturctors
39974     if (layout.layout) {
39975         config = layout;
39976         layout = config.layout;
39977         delete config.layout;
39978     }
39979     if (layout.xtype && !layout.getEl) {
39980         // then layout needs constructing..
39981         layout = Roo.factory(layout, Roo);
39982     }
39983     */
39984     
39985     config.el =  config.layout.getEl();
39986     
39987     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39988     
39989     config.layout.monitorWindowResize = false; // turn off autosizing
39990     this.layout = config.layout;
39991     this.layout.getEl().addClass("roo-layout-nested-layout");
39992     this.layout.parent = this;
39993     
39994     
39995     
39996     
39997 };
39998
39999 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40000
40001     setSize : function(width, height){
40002         if(!this.ignoreResize(width, height)){
40003             var size = this.adjustForComponents(width, height);
40004             var el = this.layout.getEl();
40005             if (size.height < 1) {
40006                 el.setWidth(size.width);   
40007             } else {
40008                 el.setSize(size.width, size.height);
40009             }
40010             var touch = el.dom.offsetWidth;
40011             this.layout.layout();
40012             // ie requires a double layout on the first pass
40013             if(Roo.isIE && !this.initialized){
40014                 this.initialized = true;
40015                 this.layout.layout();
40016             }
40017         }
40018     },
40019     
40020     // activate all subpanels if not currently active..
40021     
40022     setActiveState : function(active){
40023         this.active = active;
40024         this.setActiveClass(active);
40025         
40026         if(!active){
40027             this.fireEvent("deactivate", this);
40028             return;
40029         }
40030         
40031         this.fireEvent("activate", this);
40032         // not sure if this should happen before or after..
40033         if (!this.layout) {
40034             return; // should not happen..
40035         }
40036         var reg = false;
40037         for (var r in this.layout.regions) {
40038             reg = this.layout.getRegion(r);
40039             if (reg.getActivePanel()) {
40040                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40041                 reg.setActivePanel(reg.getActivePanel());
40042                 continue;
40043             }
40044             if (!reg.panels.length) {
40045                 continue;
40046             }
40047             reg.showPanel(reg.getPanel(0));
40048         }
40049         
40050         
40051         
40052         
40053     },
40054     
40055     /**
40056      * Returns the nested BorderLayout for this panel
40057      * @return {Roo.BorderLayout} 
40058      */
40059     getLayout : function(){
40060         return this.layout;
40061     },
40062     
40063      /**
40064      * Adds a xtype elements to the layout of the nested panel
40065      * <pre><code>
40066
40067 panel.addxtype({
40068        xtype : 'ContentPanel',
40069        region: 'west',
40070        items: [ .... ]
40071    }
40072 );
40073
40074 panel.addxtype({
40075         xtype : 'NestedLayoutPanel',
40076         region: 'west',
40077         layout: {
40078            center: { },
40079            west: { }   
40080         },
40081         items : [ ... list of content panels or nested layout panels.. ]
40082    }
40083 );
40084 </code></pre>
40085      * @param {Object} cfg Xtype definition of item to add.
40086      */
40087     addxtype : function(cfg) {
40088         return this.layout.addxtype(cfg);
40089     
40090     }
40091 });/*
40092  * Based on:
40093  * Ext JS Library 1.1.1
40094  * Copyright(c) 2006-2007, Ext JS, LLC.
40095  *
40096  * Originally Released Under LGPL - original licence link has changed is not relivant.
40097  *
40098  * Fork - LGPL
40099  * <script type="text/javascript">
40100  */
40101 /**
40102  * @class Roo.TabPanel
40103  * @extends Roo.util.Observable
40104  * A lightweight tab container.
40105  * <br><br>
40106  * Usage:
40107  * <pre><code>
40108 // basic tabs 1, built from existing content
40109 var tabs = new Roo.TabPanel("tabs1");
40110 tabs.addTab("script", "View Script");
40111 tabs.addTab("markup", "View Markup");
40112 tabs.activate("script");
40113
40114 // more advanced tabs, built from javascript
40115 var jtabs = new Roo.TabPanel("jtabs");
40116 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40117
40118 // set up the UpdateManager
40119 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40120 var updater = tab2.getUpdateManager();
40121 updater.setDefaultUrl("ajax1.htm");
40122 tab2.on('activate', updater.refresh, updater, true);
40123
40124 // Use setUrl for Ajax loading
40125 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40126 tab3.setUrl("ajax2.htm", null, true);
40127
40128 // Disabled tab
40129 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40130 tab4.disable();
40131
40132 jtabs.activate("jtabs-1");
40133  * </code></pre>
40134  * @constructor
40135  * Create a new TabPanel.
40136  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40137  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40138  */
40139 Roo.bootstrap.panel.Tabs = function(config){
40140     /**
40141     * The container element for this TabPanel.
40142     * @type Roo.Element
40143     */
40144     this.el = Roo.get(config.el);
40145     delete config.el;
40146     if(config){
40147         if(typeof config == "boolean"){
40148             this.tabPosition = config ? "bottom" : "top";
40149         }else{
40150             Roo.apply(this, config);
40151         }
40152     }
40153     
40154     if(this.tabPosition == "bottom"){
40155         // if tabs are at the bottom = create the body first.
40156         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40157         this.el.addClass("roo-tabs-bottom");
40158     }
40159     // next create the tabs holders
40160     
40161     if (this.tabPosition == "west"){
40162         
40163         var reg = this.region; // fake it..
40164         while (reg) {
40165             if (!reg.mgr.parent) {
40166                 break;
40167             }
40168             reg = reg.mgr.parent.region;
40169         }
40170         Roo.log("got nest?");
40171         Roo.log(reg);
40172         if (reg.mgr.getRegion('west')) {
40173             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40174             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40175             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40176             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40177             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40178         
40179             
40180         }
40181         
40182         
40183     } else {
40184      
40185         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40186         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40187         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40188         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40189     }
40190     
40191     
40192     if(Roo.isIE){
40193         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40194     }
40195     
40196     // finally - if tabs are at the top, then create the body last..
40197     if(this.tabPosition != "bottom"){
40198         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40199          * @type Roo.Element
40200          */
40201         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40202         this.el.addClass("roo-tabs-top");
40203     }
40204     this.items = [];
40205
40206     this.bodyEl.setStyle("position", "relative");
40207
40208     this.active = null;
40209     this.activateDelegate = this.activate.createDelegate(this);
40210
40211     this.addEvents({
40212         /**
40213          * @event tabchange
40214          * Fires when the active tab changes
40215          * @param {Roo.TabPanel} this
40216          * @param {Roo.TabPanelItem} activePanel The new active tab
40217          */
40218         "tabchange": true,
40219         /**
40220          * @event beforetabchange
40221          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40222          * @param {Roo.TabPanel} this
40223          * @param {Object} e Set cancel to true on this object to cancel the tab change
40224          * @param {Roo.TabPanelItem} tab The tab being changed to
40225          */
40226         "beforetabchange" : true
40227     });
40228
40229     Roo.EventManager.onWindowResize(this.onResize, this);
40230     this.cpad = this.el.getPadding("lr");
40231     this.hiddenCount = 0;
40232
40233
40234     // toolbar on the tabbar support...
40235     if (this.toolbar) {
40236         alert("no toolbar support yet");
40237         this.toolbar  = false;
40238         /*
40239         var tcfg = this.toolbar;
40240         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40241         this.toolbar = new Roo.Toolbar(tcfg);
40242         if (Roo.isSafari) {
40243             var tbl = tcfg.container.child('table', true);
40244             tbl.setAttribute('width', '100%');
40245         }
40246         */
40247         
40248     }
40249    
40250
40251
40252     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40253 };
40254
40255 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40256     /*
40257      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40258      */
40259     tabPosition : "top",
40260     /*
40261      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40262      */
40263     currentTabWidth : 0,
40264     /*
40265      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40266      */
40267     minTabWidth : 40,
40268     /*
40269      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40270      */
40271     maxTabWidth : 250,
40272     /*
40273      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40274      */
40275     preferredTabWidth : 175,
40276     /*
40277      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40278      */
40279     resizeTabs : false,
40280     /*
40281      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40282      */
40283     monitorResize : true,
40284     /*
40285      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40286      */
40287     toolbar : false,  // set by caller..
40288     
40289     region : false, /// set by caller
40290     
40291     disableTooltips : true, // not used yet...
40292
40293     /**
40294      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40295      * @param {String} id The id of the div to use <b>or create</b>
40296      * @param {String} text The text for the tab
40297      * @param {String} content (optional) Content to put in the TabPanelItem body
40298      * @param {Boolean} closable (optional) True to create a close icon on the tab
40299      * @return {Roo.TabPanelItem} The created TabPanelItem
40300      */
40301     addTab : function(id, text, content, closable, tpl)
40302     {
40303         var item = new Roo.bootstrap.panel.TabItem({
40304             panel: this,
40305             id : id,
40306             text : text,
40307             closable : closable,
40308             tpl : tpl
40309         });
40310         this.addTabItem(item);
40311         if(content){
40312             item.setContent(content);
40313         }
40314         return item;
40315     },
40316
40317     /**
40318      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40319      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40320      * @return {Roo.TabPanelItem}
40321      */
40322     getTab : function(id){
40323         return this.items[id];
40324     },
40325
40326     /**
40327      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40328      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40329      */
40330     hideTab : function(id){
40331         var t = this.items[id];
40332         if(!t.isHidden()){
40333            t.setHidden(true);
40334            this.hiddenCount++;
40335            this.autoSizeTabs();
40336         }
40337     },
40338
40339     /**
40340      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40341      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40342      */
40343     unhideTab : function(id){
40344         var t = this.items[id];
40345         if(t.isHidden()){
40346            t.setHidden(false);
40347            this.hiddenCount--;
40348            this.autoSizeTabs();
40349         }
40350     },
40351
40352     /**
40353      * Adds an existing {@link Roo.TabPanelItem}.
40354      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40355      */
40356     addTabItem : function(item)
40357     {
40358         this.items[item.id] = item;
40359         this.items.push(item);
40360         this.autoSizeTabs();
40361       //  if(this.resizeTabs){
40362     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40363   //         this.autoSizeTabs();
40364 //        }else{
40365 //            item.autoSize();
40366        // }
40367     },
40368
40369     /**
40370      * Removes a {@link Roo.TabPanelItem}.
40371      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40372      */
40373     removeTab : function(id){
40374         var items = this.items;
40375         var tab = items[id];
40376         if(!tab) { return; }
40377         var index = items.indexOf(tab);
40378         if(this.active == tab && items.length > 1){
40379             var newTab = this.getNextAvailable(index);
40380             if(newTab) {
40381                 newTab.activate();
40382             }
40383         }
40384         this.stripEl.dom.removeChild(tab.pnode.dom);
40385         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40386             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40387         }
40388         items.splice(index, 1);
40389         delete this.items[tab.id];
40390         tab.fireEvent("close", tab);
40391         tab.purgeListeners();
40392         this.autoSizeTabs();
40393     },
40394
40395     getNextAvailable : function(start){
40396         var items = this.items;
40397         var index = start;
40398         // look for a next tab that will slide over to
40399         // replace the one being removed
40400         while(index < items.length){
40401             var item = items[++index];
40402             if(item && !item.isHidden()){
40403                 return item;
40404             }
40405         }
40406         // if one isn't found select the previous tab (on the left)
40407         index = start;
40408         while(index >= 0){
40409             var item = items[--index];
40410             if(item && !item.isHidden()){
40411                 return item;
40412             }
40413         }
40414         return null;
40415     },
40416
40417     /**
40418      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40419      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40420      */
40421     disableTab : function(id){
40422         var tab = this.items[id];
40423         if(tab && this.active != tab){
40424             tab.disable();
40425         }
40426     },
40427
40428     /**
40429      * Enables a {@link Roo.TabPanelItem} that is disabled.
40430      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40431      */
40432     enableTab : function(id){
40433         var tab = this.items[id];
40434         tab.enable();
40435     },
40436
40437     /**
40438      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40439      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40440      * @return {Roo.TabPanelItem} The TabPanelItem.
40441      */
40442     activate : function(id)
40443     {
40444         //Roo.log('activite:'  + id);
40445         
40446         var tab = this.items[id];
40447         if(!tab){
40448             return null;
40449         }
40450         if(tab == this.active || tab.disabled){
40451             return tab;
40452         }
40453         var e = {};
40454         this.fireEvent("beforetabchange", this, e, tab);
40455         if(e.cancel !== true && !tab.disabled){
40456             if(this.active){
40457                 this.active.hide();
40458             }
40459             this.active = this.items[id];
40460             this.active.show();
40461             this.fireEvent("tabchange", this, this.active);
40462         }
40463         return tab;
40464     },
40465
40466     /**
40467      * Gets the active {@link Roo.TabPanelItem}.
40468      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40469      */
40470     getActiveTab : function(){
40471         return this.active;
40472     },
40473
40474     /**
40475      * Updates the tab body element to fit the height of the container element
40476      * for overflow scrolling
40477      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40478      */
40479     syncHeight : function(targetHeight){
40480         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40481         var bm = this.bodyEl.getMargins();
40482         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40483         this.bodyEl.setHeight(newHeight);
40484         return newHeight;
40485     },
40486
40487     onResize : function(){
40488         if(this.monitorResize){
40489             this.autoSizeTabs();
40490         }
40491     },
40492
40493     /**
40494      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40495      */
40496     beginUpdate : function(){
40497         this.updating = true;
40498     },
40499
40500     /**
40501      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40502      */
40503     endUpdate : function(){
40504         this.updating = false;
40505         this.autoSizeTabs();
40506     },
40507
40508     /**
40509      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40510      */
40511     autoSizeTabs : function()
40512     {
40513         var count = this.items.length;
40514         var vcount = count - this.hiddenCount;
40515         
40516         if (vcount < 2) {
40517             this.stripEl.hide();
40518         } else {
40519             this.stripEl.show();
40520         }
40521         
40522         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40523             return;
40524         }
40525         
40526         
40527         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40528         var availWidth = Math.floor(w / vcount);
40529         var b = this.stripBody;
40530         if(b.getWidth() > w){
40531             var tabs = this.items;
40532             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40533             if(availWidth < this.minTabWidth){
40534                 /*if(!this.sleft){    // incomplete scrolling code
40535                     this.createScrollButtons();
40536                 }
40537                 this.showScroll();
40538                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40539             }
40540         }else{
40541             if(this.currentTabWidth < this.preferredTabWidth){
40542                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40543             }
40544         }
40545     },
40546
40547     /**
40548      * Returns the number of tabs in this TabPanel.
40549      * @return {Number}
40550      */
40551      getCount : function(){
40552          return this.items.length;
40553      },
40554
40555     /**
40556      * Resizes all the tabs to the passed width
40557      * @param {Number} The new width
40558      */
40559     setTabWidth : function(width){
40560         this.currentTabWidth = width;
40561         for(var i = 0, len = this.items.length; i < len; i++) {
40562                 if(!this.items[i].isHidden()) {
40563                 this.items[i].setWidth(width);
40564             }
40565         }
40566     },
40567
40568     /**
40569      * Destroys this TabPanel
40570      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40571      */
40572     destroy : function(removeEl){
40573         Roo.EventManager.removeResizeListener(this.onResize, this);
40574         for(var i = 0, len = this.items.length; i < len; i++){
40575             this.items[i].purgeListeners();
40576         }
40577         if(removeEl === true){
40578             this.el.update("");
40579             this.el.remove();
40580         }
40581     },
40582     
40583     createStrip : function(container)
40584     {
40585         var strip = document.createElement("nav");
40586         strip.className = Roo.bootstrap.version == 4 ?
40587             "navbar-light bg-light" : 
40588             "navbar navbar-default"; //"x-tabs-wrap";
40589         container.appendChild(strip);
40590         return strip;
40591     },
40592     
40593     createStripList : function(strip)
40594     {
40595         // div wrapper for retard IE
40596         // returns the "tr" element.
40597         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40598         //'<div class="x-tabs-strip-wrap">'+
40599           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40600           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40601         return strip.firstChild; //.firstChild.firstChild.firstChild;
40602     },
40603     createBody : function(container)
40604     {
40605         var body = document.createElement("div");
40606         Roo.id(body, "tab-body");
40607         //Roo.fly(body).addClass("x-tabs-body");
40608         Roo.fly(body).addClass("tab-content");
40609         container.appendChild(body);
40610         return body;
40611     },
40612     createItemBody :function(bodyEl, id){
40613         var body = Roo.getDom(id);
40614         if(!body){
40615             body = document.createElement("div");
40616             body.id = id;
40617         }
40618         //Roo.fly(body).addClass("x-tabs-item-body");
40619         Roo.fly(body).addClass("tab-pane");
40620          bodyEl.insertBefore(body, bodyEl.firstChild);
40621         return body;
40622     },
40623     /** @private */
40624     createStripElements :  function(stripEl, text, closable, tpl)
40625     {
40626         var td = document.createElement("li"); // was td..
40627         td.className = 'nav-item';
40628         
40629         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40630         
40631         
40632         stripEl.appendChild(td);
40633         /*if(closable){
40634             td.className = "x-tabs-closable";
40635             if(!this.closeTpl){
40636                 this.closeTpl = new Roo.Template(
40637                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40638                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40639                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40640                 );
40641             }
40642             var el = this.closeTpl.overwrite(td, {"text": text});
40643             var close = el.getElementsByTagName("div")[0];
40644             var inner = el.getElementsByTagName("em")[0];
40645             return {"el": el, "close": close, "inner": inner};
40646         } else {
40647         */
40648         // not sure what this is..
40649 //            if(!this.tabTpl){
40650                 //this.tabTpl = new Roo.Template(
40651                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40652                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40653                 //);
40654 //                this.tabTpl = new Roo.Template(
40655 //                   '<a href="#">' +
40656 //                   '<span unselectable="on"' +
40657 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40658 //                            ' >{text}</span></a>'
40659 //                );
40660 //                
40661 //            }
40662
40663
40664             var template = tpl || this.tabTpl || false;
40665             
40666             if(!template){
40667                 template =  new Roo.Template(
40668                         Roo.bootstrap.version == 4 ? 
40669                             (
40670                                 '<a class="nav-link" href="#" unselectable="on"' +
40671                                      (this.disableTooltips ? '' : ' title="{text}"') +
40672                                      ' >{text}</a>'
40673                             ) : (
40674                                 '<a class="nav-link" href="#">' +
40675                                 '<span unselectable="on"' +
40676                                          (this.disableTooltips ? '' : ' title="{text}"') +
40677                                     ' >{text}</span></a>'
40678                             )
40679                 );
40680             }
40681             
40682             switch (typeof(template)) {
40683                 case 'object' :
40684                     break;
40685                 case 'string' :
40686                     template = new Roo.Template(template);
40687                     break;
40688                 default :
40689                     break;
40690             }
40691             
40692             var el = template.overwrite(td, {"text": text});
40693             
40694             var inner = el.getElementsByTagName("span")[0];
40695             
40696             return {"el": el, "inner": inner};
40697             
40698     }
40699         
40700     
40701 });
40702
40703 /**
40704  * @class Roo.TabPanelItem
40705  * @extends Roo.util.Observable
40706  * Represents an individual item (tab plus body) in a TabPanel.
40707  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40708  * @param {String} id The id of this TabPanelItem
40709  * @param {String} text The text for the tab of this TabPanelItem
40710  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40711  */
40712 Roo.bootstrap.panel.TabItem = function(config){
40713     /**
40714      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40715      * @type Roo.TabPanel
40716      */
40717     this.tabPanel = config.panel;
40718     /**
40719      * The id for this TabPanelItem
40720      * @type String
40721      */
40722     this.id = config.id;
40723     /** @private */
40724     this.disabled = false;
40725     /** @private */
40726     this.text = config.text;
40727     /** @private */
40728     this.loaded = false;
40729     this.closable = config.closable;
40730
40731     /**
40732      * The body element for this TabPanelItem.
40733      * @type Roo.Element
40734      */
40735     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40736     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40737     this.bodyEl.setStyle("display", "block");
40738     this.bodyEl.setStyle("zoom", "1");
40739     //this.hideAction();
40740
40741     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40742     /** @private */
40743     this.el = Roo.get(els.el);
40744     this.inner = Roo.get(els.inner, true);
40745      this.textEl = Roo.bootstrap.version == 4 ?
40746         this.el : Roo.get(this.el.dom.firstChild, true);
40747
40748     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40749     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40750
40751     
40752 //    this.el.on("mousedown", this.onTabMouseDown, this);
40753     this.el.on("click", this.onTabClick, this);
40754     /** @private */
40755     if(config.closable){
40756         var c = Roo.get(els.close, true);
40757         c.dom.title = this.closeText;
40758         c.addClassOnOver("close-over");
40759         c.on("click", this.closeClick, this);
40760      }
40761
40762     this.addEvents({
40763          /**
40764          * @event activate
40765          * Fires when this tab becomes the active tab.
40766          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40767          * @param {Roo.TabPanelItem} this
40768          */
40769         "activate": true,
40770         /**
40771          * @event beforeclose
40772          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40773          * @param {Roo.TabPanelItem} this
40774          * @param {Object} e Set cancel to true on this object to cancel the close.
40775          */
40776         "beforeclose": true,
40777         /**
40778          * @event close
40779          * Fires when this tab is closed.
40780          * @param {Roo.TabPanelItem} this
40781          */
40782          "close": true,
40783         /**
40784          * @event deactivate
40785          * Fires when this tab is no longer the active tab.
40786          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40787          * @param {Roo.TabPanelItem} this
40788          */
40789          "deactivate" : true
40790     });
40791     this.hidden = false;
40792
40793     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40794 };
40795
40796 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40797            {
40798     purgeListeners : function(){
40799        Roo.util.Observable.prototype.purgeListeners.call(this);
40800        this.el.removeAllListeners();
40801     },
40802     /**
40803      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40804      */
40805     show : function(){
40806         this.status_node.addClass("active");
40807         this.showAction();
40808         if(Roo.isOpera){
40809             this.tabPanel.stripWrap.repaint();
40810         }
40811         this.fireEvent("activate", this.tabPanel, this);
40812     },
40813
40814     /**
40815      * Returns true if this tab is the active tab.
40816      * @return {Boolean}
40817      */
40818     isActive : function(){
40819         return this.tabPanel.getActiveTab() == this;
40820     },
40821
40822     /**
40823      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40824      */
40825     hide : function(){
40826         this.status_node.removeClass("active");
40827         this.hideAction();
40828         this.fireEvent("deactivate", this.tabPanel, this);
40829     },
40830
40831     hideAction : function(){
40832         this.bodyEl.hide();
40833         this.bodyEl.setStyle("position", "absolute");
40834         this.bodyEl.setLeft("-20000px");
40835         this.bodyEl.setTop("-20000px");
40836     },
40837
40838     showAction : function(){
40839         this.bodyEl.setStyle("position", "relative");
40840         this.bodyEl.setTop("");
40841         this.bodyEl.setLeft("");
40842         this.bodyEl.show();
40843     },
40844
40845     /**
40846      * Set the tooltip for the tab.
40847      * @param {String} tooltip The tab's tooltip
40848      */
40849     setTooltip : function(text){
40850         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40851             this.textEl.dom.qtip = text;
40852             this.textEl.dom.removeAttribute('title');
40853         }else{
40854             this.textEl.dom.title = text;
40855         }
40856     },
40857
40858     onTabClick : function(e){
40859         e.preventDefault();
40860         this.tabPanel.activate(this.id);
40861     },
40862
40863     onTabMouseDown : function(e){
40864         e.preventDefault();
40865         this.tabPanel.activate(this.id);
40866     },
40867 /*
40868     getWidth : function(){
40869         return this.inner.getWidth();
40870     },
40871
40872     setWidth : function(width){
40873         var iwidth = width - this.linode.getPadding("lr");
40874         this.inner.setWidth(iwidth);
40875         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40876         this.linode.setWidth(width);
40877     },
40878 */
40879     /**
40880      * Show or hide the tab
40881      * @param {Boolean} hidden True to hide or false to show.
40882      */
40883     setHidden : function(hidden){
40884         this.hidden = hidden;
40885         this.linode.setStyle("display", hidden ? "none" : "");
40886     },
40887
40888     /**
40889      * Returns true if this tab is "hidden"
40890      * @return {Boolean}
40891      */
40892     isHidden : function(){
40893         return this.hidden;
40894     },
40895
40896     /**
40897      * Returns the text for this tab
40898      * @return {String}
40899      */
40900     getText : function(){
40901         return this.text;
40902     },
40903     /*
40904     autoSize : function(){
40905         //this.el.beginMeasure();
40906         this.textEl.setWidth(1);
40907         /*
40908          *  #2804 [new] Tabs in Roojs
40909          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40910          */
40911         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40912         //this.el.endMeasure();
40913     //},
40914
40915     /**
40916      * Sets the text for the tab (Note: this also sets the tooltip text)
40917      * @param {String} text The tab's text and tooltip
40918      */
40919     setText : function(text){
40920         this.text = text;
40921         this.textEl.update(text);
40922         this.setTooltip(text);
40923         //if(!this.tabPanel.resizeTabs){
40924         //    this.autoSize();
40925         //}
40926     },
40927     /**
40928      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40929      */
40930     activate : function(){
40931         this.tabPanel.activate(this.id);
40932     },
40933
40934     /**
40935      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40936      */
40937     disable : function(){
40938         if(this.tabPanel.active != this){
40939             this.disabled = true;
40940             this.status_node.addClass("disabled");
40941         }
40942     },
40943
40944     /**
40945      * Enables this TabPanelItem if it was previously disabled.
40946      */
40947     enable : function(){
40948         this.disabled = false;
40949         this.status_node.removeClass("disabled");
40950     },
40951
40952     /**
40953      * Sets the content for this TabPanelItem.
40954      * @param {String} content The content
40955      * @param {Boolean} loadScripts true to look for and load scripts
40956      */
40957     setContent : function(content, loadScripts){
40958         this.bodyEl.update(content, loadScripts);
40959     },
40960
40961     /**
40962      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40963      * @return {Roo.UpdateManager} The UpdateManager
40964      */
40965     getUpdateManager : function(){
40966         return this.bodyEl.getUpdateManager();
40967     },
40968
40969     /**
40970      * Set a URL to be used to load the content for this TabPanelItem.
40971      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40972      * @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)
40973      * @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)
40974      * @return {Roo.UpdateManager} The UpdateManager
40975      */
40976     setUrl : function(url, params, loadOnce){
40977         if(this.refreshDelegate){
40978             this.un('activate', this.refreshDelegate);
40979         }
40980         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40981         this.on("activate", this.refreshDelegate);
40982         return this.bodyEl.getUpdateManager();
40983     },
40984
40985     /** @private */
40986     _handleRefresh : function(url, params, loadOnce){
40987         if(!loadOnce || !this.loaded){
40988             var updater = this.bodyEl.getUpdateManager();
40989             updater.update(url, params, this._setLoaded.createDelegate(this));
40990         }
40991     },
40992
40993     /**
40994      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40995      *   Will fail silently if the setUrl method has not been called.
40996      *   This does not activate the panel, just updates its content.
40997      */
40998     refresh : function(){
40999         if(this.refreshDelegate){
41000            this.loaded = false;
41001            this.refreshDelegate();
41002         }
41003     },
41004
41005     /** @private */
41006     _setLoaded : function(){
41007         this.loaded = true;
41008     },
41009
41010     /** @private */
41011     closeClick : function(e){
41012         var o = {};
41013         e.stopEvent();
41014         this.fireEvent("beforeclose", this, o);
41015         if(o.cancel !== true){
41016             this.tabPanel.removeTab(this.id);
41017         }
41018     },
41019     /**
41020      * The text displayed in the tooltip for the close icon.
41021      * @type String
41022      */
41023     closeText : "Close this tab"
41024 });
41025 /**
41026 *    This script refer to:
41027 *    Title: International Telephone Input
41028 *    Author: Jack O'Connor
41029 *    Code version:  v12.1.12
41030 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41031 **/
41032
41033 Roo.bootstrap.PhoneInputData = function() {
41034     var d = [
41035       [
41036         "Afghanistan (‫افغانستان‬‎)",
41037         "af",
41038         "93"
41039       ],
41040       [
41041         "Albania (Shqipëri)",
41042         "al",
41043         "355"
41044       ],
41045       [
41046         "Algeria (‫الجزائر‬‎)",
41047         "dz",
41048         "213"
41049       ],
41050       [
41051         "American Samoa",
41052         "as",
41053         "1684"
41054       ],
41055       [
41056         "Andorra",
41057         "ad",
41058         "376"
41059       ],
41060       [
41061         "Angola",
41062         "ao",
41063         "244"
41064       ],
41065       [
41066         "Anguilla",
41067         "ai",
41068         "1264"
41069       ],
41070       [
41071         "Antigua and Barbuda",
41072         "ag",
41073         "1268"
41074       ],
41075       [
41076         "Argentina",
41077         "ar",
41078         "54"
41079       ],
41080       [
41081         "Armenia (Հայաստան)",
41082         "am",
41083         "374"
41084       ],
41085       [
41086         "Aruba",
41087         "aw",
41088         "297"
41089       ],
41090       [
41091         "Australia",
41092         "au",
41093         "61",
41094         0
41095       ],
41096       [
41097         "Austria (Österreich)",
41098         "at",
41099         "43"
41100       ],
41101       [
41102         "Azerbaijan (Azərbaycan)",
41103         "az",
41104         "994"
41105       ],
41106       [
41107         "Bahamas",
41108         "bs",
41109         "1242"
41110       ],
41111       [
41112         "Bahrain (‫البحرين‬‎)",
41113         "bh",
41114         "973"
41115       ],
41116       [
41117         "Bangladesh (বাংলাদেশ)",
41118         "bd",
41119         "880"
41120       ],
41121       [
41122         "Barbados",
41123         "bb",
41124         "1246"
41125       ],
41126       [
41127         "Belarus (Беларусь)",
41128         "by",
41129         "375"
41130       ],
41131       [
41132         "Belgium (België)",
41133         "be",
41134         "32"
41135       ],
41136       [
41137         "Belize",
41138         "bz",
41139         "501"
41140       ],
41141       [
41142         "Benin (Bénin)",
41143         "bj",
41144         "229"
41145       ],
41146       [
41147         "Bermuda",
41148         "bm",
41149         "1441"
41150       ],
41151       [
41152         "Bhutan (འབྲུག)",
41153         "bt",
41154         "975"
41155       ],
41156       [
41157         "Bolivia",
41158         "bo",
41159         "591"
41160       ],
41161       [
41162         "Bosnia and Herzegovina (Босна и Херцеговина)",
41163         "ba",
41164         "387"
41165       ],
41166       [
41167         "Botswana",
41168         "bw",
41169         "267"
41170       ],
41171       [
41172         "Brazil (Brasil)",
41173         "br",
41174         "55"
41175       ],
41176       [
41177         "British Indian Ocean Territory",
41178         "io",
41179         "246"
41180       ],
41181       [
41182         "British Virgin Islands",
41183         "vg",
41184         "1284"
41185       ],
41186       [
41187         "Brunei",
41188         "bn",
41189         "673"
41190       ],
41191       [
41192         "Bulgaria (България)",
41193         "bg",
41194         "359"
41195       ],
41196       [
41197         "Burkina Faso",
41198         "bf",
41199         "226"
41200       ],
41201       [
41202         "Burundi (Uburundi)",
41203         "bi",
41204         "257"
41205       ],
41206       [
41207         "Cambodia (កម្ពុជា)",
41208         "kh",
41209         "855"
41210       ],
41211       [
41212         "Cameroon (Cameroun)",
41213         "cm",
41214         "237"
41215       ],
41216       [
41217         "Canada",
41218         "ca",
41219         "1",
41220         1,
41221         ["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"]
41222       ],
41223       [
41224         "Cape Verde (Kabu Verdi)",
41225         "cv",
41226         "238"
41227       ],
41228       [
41229         "Caribbean Netherlands",
41230         "bq",
41231         "599",
41232         1
41233       ],
41234       [
41235         "Cayman Islands",
41236         "ky",
41237         "1345"
41238       ],
41239       [
41240         "Central African Republic (République centrafricaine)",
41241         "cf",
41242         "236"
41243       ],
41244       [
41245         "Chad (Tchad)",
41246         "td",
41247         "235"
41248       ],
41249       [
41250         "Chile",
41251         "cl",
41252         "56"
41253       ],
41254       [
41255         "China (中国)",
41256         "cn",
41257         "86"
41258       ],
41259       [
41260         "Christmas Island",
41261         "cx",
41262         "61",
41263         2
41264       ],
41265       [
41266         "Cocos (Keeling) Islands",
41267         "cc",
41268         "61",
41269         1
41270       ],
41271       [
41272         "Colombia",
41273         "co",
41274         "57"
41275       ],
41276       [
41277         "Comoros (‫جزر القمر‬‎)",
41278         "km",
41279         "269"
41280       ],
41281       [
41282         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41283         "cd",
41284         "243"
41285       ],
41286       [
41287         "Congo (Republic) (Congo-Brazzaville)",
41288         "cg",
41289         "242"
41290       ],
41291       [
41292         "Cook Islands",
41293         "ck",
41294         "682"
41295       ],
41296       [
41297         "Costa Rica",
41298         "cr",
41299         "506"
41300       ],
41301       [
41302         "Côte d’Ivoire",
41303         "ci",
41304         "225"
41305       ],
41306       [
41307         "Croatia (Hrvatska)",
41308         "hr",
41309         "385"
41310       ],
41311       [
41312         "Cuba",
41313         "cu",
41314         "53"
41315       ],
41316       [
41317         "Curaçao",
41318         "cw",
41319         "599",
41320         0
41321       ],
41322       [
41323         "Cyprus (Κύπρος)",
41324         "cy",
41325         "357"
41326       ],
41327       [
41328         "Czech Republic (Česká republika)",
41329         "cz",
41330         "420"
41331       ],
41332       [
41333         "Denmark (Danmark)",
41334         "dk",
41335         "45"
41336       ],
41337       [
41338         "Djibouti",
41339         "dj",
41340         "253"
41341       ],
41342       [
41343         "Dominica",
41344         "dm",
41345         "1767"
41346       ],
41347       [
41348         "Dominican Republic (República Dominicana)",
41349         "do",
41350         "1",
41351         2,
41352         ["809", "829", "849"]
41353       ],
41354       [
41355         "Ecuador",
41356         "ec",
41357         "593"
41358       ],
41359       [
41360         "Egypt (‫مصر‬‎)",
41361         "eg",
41362         "20"
41363       ],
41364       [
41365         "El Salvador",
41366         "sv",
41367         "503"
41368       ],
41369       [
41370         "Equatorial Guinea (Guinea Ecuatorial)",
41371         "gq",
41372         "240"
41373       ],
41374       [
41375         "Eritrea",
41376         "er",
41377         "291"
41378       ],
41379       [
41380         "Estonia (Eesti)",
41381         "ee",
41382         "372"
41383       ],
41384       [
41385         "Ethiopia",
41386         "et",
41387         "251"
41388       ],
41389       [
41390         "Falkland Islands (Islas Malvinas)",
41391         "fk",
41392         "500"
41393       ],
41394       [
41395         "Faroe Islands (Føroyar)",
41396         "fo",
41397         "298"
41398       ],
41399       [
41400         "Fiji",
41401         "fj",
41402         "679"
41403       ],
41404       [
41405         "Finland (Suomi)",
41406         "fi",
41407         "358",
41408         0
41409       ],
41410       [
41411         "France",
41412         "fr",
41413         "33"
41414       ],
41415       [
41416         "French Guiana (Guyane française)",
41417         "gf",
41418         "594"
41419       ],
41420       [
41421         "French Polynesia (Polynésie française)",
41422         "pf",
41423         "689"
41424       ],
41425       [
41426         "Gabon",
41427         "ga",
41428         "241"
41429       ],
41430       [
41431         "Gambia",
41432         "gm",
41433         "220"
41434       ],
41435       [
41436         "Georgia (საქართველო)",
41437         "ge",
41438         "995"
41439       ],
41440       [
41441         "Germany (Deutschland)",
41442         "de",
41443         "49"
41444       ],
41445       [
41446         "Ghana (Gaana)",
41447         "gh",
41448         "233"
41449       ],
41450       [
41451         "Gibraltar",
41452         "gi",
41453         "350"
41454       ],
41455       [
41456         "Greece (Ελλάδα)",
41457         "gr",
41458         "30"
41459       ],
41460       [
41461         "Greenland (Kalaallit Nunaat)",
41462         "gl",
41463         "299"
41464       ],
41465       [
41466         "Grenada",
41467         "gd",
41468         "1473"
41469       ],
41470       [
41471         "Guadeloupe",
41472         "gp",
41473         "590",
41474         0
41475       ],
41476       [
41477         "Guam",
41478         "gu",
41479         "1671"
41480       ],
41481       [
41482         "Guatemala",
41483         "gt",
41484         "502"
41485       ],
41486       [
41487         "Guernsey",
41488         "gg",
41489         "44",
41490         1
41491       ],
41492       [
41493         "Guinea (Guinée)",
41494         "gn",
41495         "224"
41496       ],
41497       [
41498         "Guinea-Bissau (Guiné Bissau)",
41499         "gw",
41500         "245"
41501       ],
41502       [
41503         "Guyana",
41504         "gy",
41505         "592"
41506       ],
41507       [
41508         "Haiti",
41509         "ht",
41510         "509"
41511       ],
41512       [
41513         "Honduras",
41514         "hn",
41515         "504"
41516       ],
41517       [
41518         "Hong Kong (香港)",
41519         "hk",
41520         "852"
41521       ],
41522       [
41523         "Hungary (Magyarország)",
41524         "hu",
41525         "36"
41526       ],
41527       [
41528         "Iceland (Ísland)",
41529         "is",
41530         "354"
41531       ],
41532       [
41533         "India (भारत)",
41534         "in",
41535         "91"
41536       ],
41537       [
41538         "Indonesia",
41539         "id",
41540         "62"
41541       ],
41542       [
41543         "Iran (‫ایران‬‎)",
41544         "ir",
41545         "98"
41546       ],
41547       [
41548         "Iraq (‫العراق‬‎)",
41549         "iq",
41550         "964"
41551       ],
41552       [
41553         "Ireland",
41554         "ie",
41555         "353"
41556       ],
41557       [
41558         "Isle of Man",
41559         "im",
41560         "44",
41561         2
41562       ],
41563       [
41564         "Israel (‫ישראל‬‎)",
41565         "il",
41566         "972"
41567       ],
41568       [
41569         "Italy (Italia)",
41570         "it",
41571         "39",
41572         0
41573       ],
41574       [
41575         "Jamaica",
41576         "jm",
41577         "1876"
41578       ],
41579       [
41580         "Japan (日本)",
41581         "jp",
41582         "81"
41583       ],
41584       [
41585         "Jersey",
41586         "je",
41587         "44",
41588         3
41589       ],
41590       [
41591         "Jordan (‫الأردن‬‎)",
41592         "jo",
41593         "962"
41594       ],
41595       [
41596         "Kazakhstan (Казахстан)",
41597         "kz",
41598         "7",
41599         1
41600       ],
41601       [
41602         "Kenya",
41603         "ke",
41604         "254"
41605       ],
41606       [
41607         "Kiribati",
41608         "ki",
41609         "686"
41610       ],
41611       [
41612         "Kosovo",
41613         "xk",
41614         "383"
41615       ],
41616       [
41617         "Kuwait (‫الكويت‬‎)",
41618         "kw",
41619         "965"
41620       ],
41621       [
41622         "Kyrgyzstan (Кыргызстан)",
41623         "kg",
41624         "996"
41625       ],
41626       [
41627         "Laos (ລາວ)",
41628         "la",
41629         "856"
41630       ],
41631       [
41632         "Latvia (Latvija)",
41633         "lv",
41634         "371"
41635       ],
41636       [
41637         "Lebanon (‫لبنان‬‎)",
41638         "lb",
41639         "961"
41640       ],
41641       [
41642         "Lesotho",
41643         "ls",
41644         "266"
41645       ],
41646       [
41647         "Liberia",
41648         "lr",
41649         "231"
41650       ],
41651       [
41652         "Libya (‫ليبيا‬‎)",
41653         "ly",
41654         "218"
41655       ],
41656       [
41657         "Liechtenstein",
41658         "li",
41659         "423"
41660       ],
41661       [
41662         "Lithuania (Lietuva)",
41663         "lt",
41664         "370"
41665       ],
41666       [
41667         "Luxembourg",
41668         "lu",
41669         "352"
41670       ],
41671       [
41672         "Macau (澳門)",
41673         "mo",
41674         "853"
41675       ],
41676       [
41677         "Macedonia (FYROM) (Македонија)",
41678         "mk",
41679         "389"
41680       ],
41681       [
41682         "Madagascar (Madagasikara)",
41683         "mg",
41684         "261"
41685       ],
41686       [
41687         "Malawi",
41688         "mw",
41689         "265"
41690       ],
41691       [
41692         "Malaysia",
41693         "my",
41694         "60"
41695       ],
41696       [
41697         "Maldives",
41698         "mv",
41699         "960"
41700       ],
41701       [
41702         "Mali",
41703         "ml",
41704         "223"
41705       ],
41706       [
41707         "Malta",
41708         "mt",
41709         "356"
41710       ],
41711       [
41712         "Marshall Islands",
41713         "mh",
41714         "692"
41715       ],
41716       [
41717         "Martinique",
41718         "mq",
41719         "596"
41720       ],
41721       [
41722         "Mauritania (‫موريتانيا‬‎)",
41723         "mr",
41724         "222"
41725       ],
41726       [
41727         "Mauritius (Moris)",
41728         "mu",
41729         "230"
41730       ],
41731       [
41732         "Mayotte",
41733         "yt",
41734         "262",
41735         1
41736       ],
41737       [
41738         "Mexico (México)",
41739         "mx",
41740         "52"
41741       ],
41742       [
41743         "Micronesia",
41744         "fm",
41745         "691"
41746       ],
41747       [
41748         "Moldova (Republica Moldova)",
41749         "md",
41750         "373"
41751       ],
41752       [
41753         "Monaco",
41754         "mc",
41755         "377"
41756       ],
41757       [
41758         "Mongolia (Монгол)",
41759         "mn",
41760         "976"
41761       ],
41762       [
41763         "Montenegro (Crna Gora)",
41764         "me",
41765         "382"
41766       ],
41767       [
41768         "Montserrat",
41769         "ms",
41770         "1664"
41771       ],
41772       [
41773         "Morocco (‫المغرب‬‎)",
41774         "ma",
41775         "212",
41776         0
41777       ],
41778       [
41779         "Mozambique (Moçambique)",
41780         "mz",
41781         "258"
41782       ],
41783       [
41784         "Myanmar (Burma) (မြန်မာ)",
41785         "mm",
41786         "95"
41787       ],
41788       [
41789         "Namibia (Namibië)",
41790         "na",
41791         "264"
41792       ],
41793       [
41794         "Nauru",
41795         "nr",
41796         "674"
41797       ],
41798       [
41799         "Nepal (नेपाल)",
41800         "np",
41801         "977"
41802       ],
41803       [
41804         "Netherlands (Nederland)",
41805         "nl",
41806         "31"
41807       ],
41808       [
41809         "New Caledonia (Nouvelle-Calédonie)",
41810         "nc",
41811         "687"
41812       ],
41813       [
41814         "New Zealand",
41815         "nz",
41816         "64"
41817       ],
41818       [
41819         "Nicaragua",
41820         "ni",
41821         "505"
41822       ],
41823       [
41824         "Niger (Nijar)",
41825         "ne",
41826         "227"
41827       ],
41828       [
41829         "Nigeria",
41830         "ng",
41831         "234"
41832       ],
41833       [
41834         "Niue",
41835         "nu",
41836         "683"
41837       ],
41838       [
41839         "Norfolk Island",
41840         "nf",
41841         "672"
41842       ],
41843       [
41844         "North Korea (조선 민주주의 인민 공화국)",
41845         "kp",
41846         "850"
41847       ],
41848       [
41849         "Northern Mariana Islands",
41850         "mp",
41851         "1670"
41852       ],
41853       [
41854         "Norway (Norge)",
41855         "no",
41856         "47",
41857         0
41858       ],
41859       [
41860         "Oman (‫عُمان‬‎)",
41861         "om",
41862         "968"
41863       ],
41864       [
41865         "Pakistan (‫پاکستان‬‎)",
41866         "pk",
41867         "92"
41868       ],
41869       [
41870         "Palau",
41871         "pw",
41872         "680"
41873       ],
41874       [
41875         "Palestine (‫فلسطين‬‎)",
41876         "ps",
41877         "970"
41878       ],
41879       [
41880         "Panama (Panamá)",
41881         "pa",
41882         "507"
41883       ],
41884       [
41885         "Papua New Guinea",
41886         "pg",
41887         "675"
41888       ],
41889       [
41890         "Paraguay",
41891         "py",
41892         "595"
41893       ],
41894       [
41895         "Peru (Perú)",
41896         "pe",
41897         "51"
41898       ],
41899       [
41900         "Philippines",
41901         "ph",
41902         "63"
41903       ],
41904       [
41905         "Poland (Polska)",
41906         "pl",
41907         "48"
41908       ],
41909       [
41910         "Portugal",
41911         "pt",
41912         "351"
41913       ],
41914       [
41915         "Puerto Rico",
41916         "pr",
41917         "1",
41918         3,
41919         ["787", "939"]
41920       ],
41921       [
41922         "Qatar (‫قطر‬‎)",
41923         "qa",
41924         "974"
41925       ],
41926       [
41927         "Réunion (La Réunion)",
41928         "re",
41929         "262",
41930         0
41931       ],
41932       [
41933         "Romania (România)",
41934         "ro",
41935         "40"
41936       ],
41937       [
41938         "Russia (Россия)",
41939         "ru",
41940         "7",
41941         0
41942       ],
41943       [
41944         "Rwanda",
41945         "rw",
41946         "250"
41947       ],
41948       [
41949         "Saint Barthélemy",
41950         "bl",
41951         "590",
41952         1
41953       ],
41954       [
41955         "Saint Helena",
41956         "sh",
41957         "290"
41958       ],
41959       [
41960         "Saint Kitts and Nevis",
41961         "kn",
41962         "1869"
41963       ],
41964       [
41965         "Saint Lucia",
41966         "lc",
41967         "1758"
41968       ],
41969       [
41970         "Saint Martin (Saint-Martin (partie française))",
41971         "mf",
41972         "590",
41973         2
41974       ],
41975       [
41976         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41977         "pm",
41978         "508"
41979       ],
41980       [
41981         "Saint Vincent and the Grenadines",
41982         "vc",
41983         "1784"
41984       ],
41985       [
41986         "Samoa",
41987         "ws",
41988         "685"
41989       ],
41990       [
41991         "San Marino",
41992         "sm",
41993         "378"
41994       ],
41995       [
41996         "São Tomé and Príncipe (São Tomé e Príncipe)",
41997         "st",
41998         "239"
41999       ],
42000       [
42001         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42002         "sa",
42003         "966"
42004       ],
42005       [
42006         "Senegal (Sénégal)",
42007         "sn",
42008         "221"
42009       ],
42010       [
42011         "Serbia (Србија)",
42012         "rs",
42013         "381"
42014       ],
42015       [
42016         "Seychelles",
42017         "sc",
42018         "248"
42019       ],
42020       [
42021         "Sierra Leone",
42022         "sl",
42023         "232"
42024       ],
42025       [
42026         "Singapore",
42027         "sg",
42028         "65"
42029       ],
42030       [
42031         "Sint Maarten",
42032         "sx",
42033         "1721"
42034       ],
42035       [
42036         "Slovakia (Slovensko)",
42037         "sk",
42038         "421"
42039       ],
42040       [
42041         "Slovenia (Slovenija)",
42042         "si",
42043         "386"
42044       ],
42045       [
42046         "Solomon Islands",
42047         "sb",
42048         "677"
42049       ],
42050       [
42051         "Somalia (Soomaaliya)",
42052         "so",
42053         "252"
42054       ],
42055       [
42056         "South Africa",
42057         "za",
42058         "27"
42059       ],
42060       [
42061         "South Korea (대한민국)",
42062         "kr",
42063         "82"
42064       ],
42065       [
42066         "South Sudan (‫جنوب السودان‬‎)",
42067         "ss",
42068         "211"
42069       ],
42070       [
42071         "Spain (España)",
42072         "es",
42073         "34"
42074       ],
42075       [
42076         "Sri Lanka (ශ්‍රී ලංකාව)",
42077         "lk",
42078         "94"
42079       ],
42080       [
42081         "Sudan (‫السودان‬‎)",
42082         "sd",
42083         "249"
42084       ],
42085       [
42086         "Suriname",
42087         "sr",
42088         "597"
42089       ],
42090       [
42091         "Svalbard and Jan Mayen",
42092         "sj",
42093         "47",
42094         1
42095       ],
42096       [
42097         "Swaziland",
42098         "sz",
42099         "268"
42100       ],
42101       [
42102         "Sweden (Sverige)",
42103         "se",
42104         "46"
42105       ],
42106       [
42107         "Switzerland (Schweiz)",
42108         "ch",
42109         "41"
42110       ],
42111       [
42112         "Syria (‫سوريا‬‎)",
42113         "sy",
42114         "963"
42115       ],
42116       [
42117         "Taiwan (台灣)",
42118         "tw",
42119         "886"
42120       ],
42121       [
42122         "Tajikistan",
42123         "tj",
42124         "992"
42125       ],
42126       [
42127         "Tanzania",
42128         "tz",
42129         "255"
42130       ],
42131       [
42132         "Thailand (ไทย)",
42133         "th",
42134         "66"
42135       ],
42136       [
42137         "Timor-Leste",
42138         "tl",
42139         "670"
42140       ],
42141       [
42142         "Togo",
42143         "tg",
42144         "228"
42145       ],
42146       [
42147         "Tokelau",
42148         "tk",
42149         "690"
42150       ],
42151       [
42152         "Tonga",
42153         "to",
42154         "676"
42155       ],
42156       [
42157         "Trinidad and Tobago",
42158         "tt",
42159         "1868"
42160       ],
42161       [
42162         "Tunisia (‫تونس‬‎)",
42163         "tn",
42164         "216"
42165       ],
42166       [
42167         "Turkey (Türkiye)",
42168         "tr",
42169         "90"
42170       ],
42171       [
42172         "Turkmenistan",
42173         "tm",
42174         "993"
42175       ],
42176       [
42177         "Turks and Caicos Islands",
42178         "tc",
42179         "1649"
42180       ],
42181       [
42182         "Tuvalu",
42183         "tv",
42184         "688"
42185       ],
42186       [
42187         "U.S. Virgin Islands",
42188         "vi",
42189         "1340"
42190       ],
42191       [
42192         "Uganda",
42193         "ug",
42194         "256"
42195       ],
42196       [
42197         "Ukraine (Україна)",
42198         "ua",
42199         "380"
42200       ],
42201       [
42202         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42203         "ae",
42204         "971"
42205       ],
42206       [
42207         "United Kingdom",
42208         "gb",
42209         "44",
42210         0
42211       ],
42212       [
42213         "United States",
42214         "us",
42215         "1",
42216         0
42217       ],
42218       [
42219         "Uruguay",
42220         "uy",
42221         "598"
42222       ],
42223       [
42224         "Uzbekistan (Oʻzbekiston)",
42225         "uz",
42226         "998"
42227       ],
42228       [
42229         "Vanuatu",
42230         "vu",
42231         "678"
42232       ],
42233       [
42234         "Vatican City (Città del Vaticano)",
42235         "va",
42236         "39",
42237         1
42238       ],
42239       [
42240         "Venezuela",
42241         "ve",
42242         "58"
42243       ],
42244       [
42245         "Vietnam (Việt Nam)",
42246         "vn",
42247         "84"
42248       ],
42249       [
42250         "Wallis and Futuna (Wallis-et-Futuna)",
42251         "wf",
42252         "681"
42253       ],
42254       [
42255         "Western Sahara (‫الصحراء الغربية‬‎)",
42256         "eh",
42257         "212",
42258         1
42259       ],
42260       [
42261         "Yemen (‫اليمن‬‎)",
42262         "ye",
42263         "967"
42264       ],
42265       [
42266         "Zambia",
42267         "zm",
42268         "260"
42269       ],
42270       [
42271         "Zimbabwe",
42272         "zw",
42273         "263"
42274       ],
42275       [
42276         "Åland Islands",
42277         "ax",
42278         "358",
42279         1
42280       ]
42281   ];
42282   
42283   return d;
42284 }/**
42285 *    This script refer to:
42286 *    Title: International Telephone Input
42287 *    Author: Jack O'Connor
42288 *    Code version:  v12.1.12
42289 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42290 **/
42291
42292 /**
42293  * @class Roo.bootstrap.PhoneInput
42294  * @extends Roo.bootstrap.TriggerField
42295  * An input with International dial-code selection
42296  
42297  * @cfg {String} defaultDialCode default '+852'
42298  * @cfg {Array} preferedCountries default []
42299   
42300  * @constructor
42301  * Create a new PhoneInput.
42302  * @param {Object} config Configuration options
42303  */
42304
42305 Roo.bootstrap.PhoneInput = function(config) {
42306     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42307 };
42308
42309 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42310         
42311         listWidth: undefined,
42312         
42313         selectedClass: 'active',
42314         
42315         invalidClass : "has-warning",
42316         
42317         validClass: 'has-success',
42318         
42319         allowed: '0123456789',
42320         
42321         max_length: 15,
42322         
42323         /**
42324          * @cfg {String} defaultDialCode The default dial code when initializing the input
42325          */
42326         defaultDialCode: '+852',
42327         
42328         /**
42329          * @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
42330          */
42331         preferedCountries: false,
42332         
42333         getAutoCreate : function()
42334         {
42335             var data = Roo.bootstrap.PhoneInputData();
42336             var align = this.labelAlign || this.parentLabelAlign();
42337             var id = Roo.id();
42338             
42339             this.allCountries = [];
42340             this.dialCodeMapping = [];
42341             
42342             for (var i = 0; i < data.length; i++) {
42343               var c = data[i];
42344               this.allCountries[i] = {
42345                 name: c[0],
42346                 iso2: c[1],
42347                 dialCode: c[2],
42348                 priority: c[3] || 0,
42349                 areaCodes: c[4] || null
42350               };
42351               this.dialCodeMapping[c[2]] = {
42352                   name: c[0],
42353                   iso2: c[1],
42354                   priority: c[3] || 0,
42355                   areaCodes: c[4] || null
42356               };
42357             }
42358             
42359             var cfg = {
42360                 cls: 'form-group',
42361                 cn: []
42362             };
42363             
42364             var input =  {
42365                 tag: 'input',
42366                 id : id,
42367                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42368                 maxlength: this.max_length,
42369                 cls : 'form-control tel-input',
42370                 autocomplete: 'new-password'
42371             };
42372             
42373             var hiddenInput = {
42374                 tag: 'input',
42375                 type: 'hidden',
42376                 cls: 'hidden-tel-input'
42377             };
42378             
42379             if (this.name) {
42380                 hiddenInput.name = this.name;
42381             }
42382             
42383             if (this.disabled) {
42384                 input.disabled = true;
42385             }
42386             
42387             var flag_container = {
42388                 tag: 'div',
42389                 cls: 'flag-box',
42390                 cn: [
42391                     {
42392                         tag: 'div',
42393                         cls: 'flag'
42394                     },
42395                     {
42396                         tag: 'div',
42397                         cls: 'caret'
42398                     }
42399                 ]
42400             };
42401             
42402             var box = {
42403                 tag: 'div',
42404                 cls: this.hasFeedback ? 'has-feedback' : '',
42405                 cn: [
42406                     hiddenInput,
42407                     input,
42408                     {
42409                         tag: 'input',
42410                         cls: 'dial-code-holder',
42411                         disabled: true
42412                     }
42413                 ]
42414             };
42415             
42416             var container = {
42417                 cls: 'roo-select2-container input-group',
42418                 cn: [
42419                     flag_container,
42420                     box
42421                 ]
42422             };
42423             
42424             if (this.fieldLabel.length) {
42425                 var indicator = {
42426                     tag: 'i',
42427                     tooltip: 'This field is required'
42428                 };
42429                 
42430                 var label = {
42431                     tag: 'label',
42432                     'for':  id,
42433                     cls: 'control-label',
42434                     cn: []
42435                 };
42436                 
42437                 var label_text = {
42438                     tag: 'span',
42439                     html: this.fieldLabel
42440                 };
42441                 
42442                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42443                 label.cn = [
42444                     indicator,
42445                     label_text
42446                 ];
42447                 
42448                 if(this.indicatorpos == 'right') {
42449                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42450                     label.cn = [
42451                         label_text,
42452                         indicator
42453                     ];
42454                 }
42455                 
42456                 if(align == 'left') {
42457                     container = {
42458                         tag: 'div',
42459                         cn: [
42460                             container
42461                         ]
42462                     };
42463                     
42464                     if(this.labelWidth > 12){
42465                         label.style = "width: " + this.labelWidth + 'px';
42466                     }
42467                     if(this.labelWidth < 13 && this.labelmd == 0){
42468                         this.labelmd = this.labelWidth;
42469                     }
42470                     if(this.labellg > 0){
42471                         label.cls += ' col-lg-' + this.labellg;
42472                         input.cls += ' col-lg-' + (12 - this.labellg);
42473                     }
42474                     if(this.labelmd > 0){
42475                         label.cls += ' col-md-' + this.labelmd;
42476                         container.cls += ' col-md-' + (12 - this.labelmd);
42477                     }
42478                     if(this.labelsm > 0){
42479                         label.cls += ' col-sm-' + this.labelsm;
42480                         container.cls += ' col-sm-' + (12 - this.labelsm);
42481                     }
42482                     if(this.labelxs > 0){
42483                         label.cls += ' col-xs-' + this.labelxs;
42484                         container.cls += ' col-xs-' + (12 - this.labelxs);
42485                     }
42486                 }
42487             }
42488             
42489             cfg.cn = [
42490                 label,
42491                 container
42492             ];
42493             
42494             var settings = this;
42495             
42496             ['xs','sm','md','lg'].map(function(size){
42497                 if (settings[size]) {
42498                     cfg.cls += ' col-' + size + '-' + settings[size];
42499                 }
42500             });
42501             
42502             this.store = new Roo.data.Store({
42503                 proxy : new Roo.data.MemoryProxy({}),
42504                 reader : new Roo.data.JsonReader({
42505                     fields : [
42506                         {
42507                             'name' : 'name',
42508                             'type' : 'string'
42509                         },
42510                         {
42511                             'name' : 'iso2',
42512                             'type' : 'string'
42513                         },
42514                         {
42515                             'name' : 'dialCode',
42516                             'type' : 'string'
42517                         },
42518                         {
42519                             'name' : 'priority',
42520                             'type' : 'string'
42521                         },
42522                         {
42523                             'name' : 'areaCodes',
42524                             'type' : 'string'
42525                         }
42526                     ]
42527                 })
42528             });
42529             
42530             if(!this.preferedCountries) {
42531                 this.preferedCountries = [
42532                     'hk',
42533                     'gb',
42534                     'us'
42535                 ];
42536             }
42537             
42538             var p = this.preferedCountries.reverse();
42539             
42540             if(p) {
42541                 for (var i = 0; i < p.length; i++) {
42542                     for (var j = 0; j < this.allCountries.length; j++) {
42543                         if(this.allCountries[j].iso2 == p[i]) {
42544                             var t = this.allCountries[j];
42545                             this.allCountries.splice(j,1);
42546                             this.allCountries.unshift(t);
42547                         }
42548                     } 
42549                 }
42550             }
42551             
42552             this.store.proxy.data = {
42553                 success: true,
42554                 data: this.allCountries
42555             };
42556             
42557             return cfg;
42558         },
42559         
42560         initEvents : function()
42561         {
42562             this.createList();
42563             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42564             
42565             this.indicator = this.indicatorEl();
42566             this.flag = this.flagEl();
42567             this.dialCodeHolder = this.dialCodeHolderEl();
42568             
42569             this.trigger = this.el.select('div.flag-box',true).first();
42570             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42571             
42572             var _this = this;
42573             
42574             (function(){
42575                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42576                 _this.list.setWidth(lw);
42577             }).defer(100);
42578             
42579             this.list.on('mouseover', this.onViewOver, this);
42580             this.list.on('mousemove', this.onViewMove, this);
42581             this.inputEl().on("keyup", this.onKeyUp, this);
42582             this.inputEl().on("keypress", this.onKeyPress, this);
42583             
42584             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42585
42586             this.view = new Roo.View(this.list, this.tpl, {
42587                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42588             });
42589             
42590             this.view.on('click', this.onViewClick, this);
42591             this.setValue(this.defaultDialCode);
42592         },
42593         
42594         onTriggerClick : function(e)
42595         {
42596             Roo.log('trigger click');
42597             if(this.disabled){
42598                 return;
42599             }
42600             
42601             if(this.isExpanded()){
42602                 this.collapse();
42603                 this.hasFocus = false;
42604             }else {
42605                 this.store.load({});
42606                 this.hasFocus = true;
42607                 this.expand();
42608             }
42609         },
42610         
42611         isExpanded : function()
42612         {
42613             return this.list.isVisible();
42614         },
42615         
42616         collapse : function()
42617         {
42618             if(!this.isExpanded()){
42619                 return;
42620             }
42621             this.list.hide();
42622             Roo.get(document).un('mousedown', this.collapseIf, this);
42623             Roo.get(document).un('mousewheel', this.collapseIf, this);
42624             this.fireEvent('collapse', this);
42625             this.validate();
42626         },
42627         
42628         expand : function()
42629         {
42630             Roo.log('expand');
42631
42632             if(this.isExpanded() || !this.hasFocus){
42633                 return;
42634             }
42635             
42636             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42637             this.list.setWidth(lw);
42638             
42639             this.list.show();
42640             this.restrictHeight();
42641             
42642             Roo.get(document).on('mousedown', this.collapseIf, this);
42643             Roo.get(document).on('mousewheel', this.collapseIf, this);
42644             
42645             this.fireEvent('expand', this);
42646         },
42647         
42648         restrictHeight : function()
42649         {
42650             this.list.alignTo(this.inputEl(), this.listAlign);
42651             this.list.alignTo(this.inputEl(), this.listAlign);
42652         },
42653         
42654         onViewOver : function(e, t)
42655         {
42656             if(this.inKeyMode){
42657                 return;
42658             }
42659             var item = this.view.findItemFromChild(t);
42660             
42661             if(item){
42662                 var index = this.view.indexOf(item);
42663                 this.select(index, false);
42664             }
42665         },
42666
42667         // private
42668         onViewClick : function(view, doFocus, el, e)
42669         {
42670             var index = this.view.getSelectedIndexes()[0];
42671             
42672             var r = this.store.getAt(index);
42673             
42674             if(r){
42675                 this.onSelect(r, index);
42676             }
42677             if(doFocus !== false && !this.blockFocus){
42678                 this.inputEl().focus();
42679             }
42680         },
42681         
42682         onViewMove : function(e, t)
42683         {
42684             this.inKeyMode = false;
42685         },
42686         
42687         select : function(index, scrollIntoView)
42688         {
42689             this.selectedIndex = index;
42690             this.view.select(index);
42691             if(scrollIntoView !== false){
42692                 var el = this.view.getNode(index);
42693                 if(el){
42694                     this.list.scrollChildIntoView(el, false);
42695                 }
42696             }
42697         },
42698         
42699         createList : function()
42700         {
42701             this.list = Roo.get(document.body).createChild({
42702                 tag: 'ul',
42703                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42704                 style: 'display:none'
42705             });
42706             
42707             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42708         },
42709         
42710         collapseIf : function(e)
42711         {
42712             var in_combo  = e.within(this.el);
42713             var in_list =  e.within(this.list);
42714             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42715             
42716             if (in_combo || in_list || is_list) {
42717                 return;
42718             }
42719             this.collapse();
42720         },
42721         
42722         onSelect : function(record, index)
42723         {
42724             if(this.fireEvent('beforeselect', this, record, index) !== false){
42725                 
42726                 this.setFlagClass(record.data.iso2);
42727                 this.setDialCode(record.data.dialCode);
42728                 this.hasFocus = false;
42729                 this.collapse();
42730                 this.fireEvent('select', this, record, index);
42731             }
42732         },
42733         
42734         flagEl : function()
42735         {
42736             var flag = this.el.select('div.flag',true).first();
42737             if(!flag){
42738                 return false;
42739             }
42740             return flag;
42741         },
42742         
42743         dialCodeHolderEl : function()
42744         {
42745             var d = this.el.select('input.dial-code-holder',true).first();
42746             if(!d){
42747                 return false;
42748             }
42749             return d;
42750         },
42751         
42752         setDialCode : function(v)
42753         {
42754             this.dialCodeHolder.dom.value = '+'+v;
42755         },
42756         
42757         setFlagClass : function(n)
42758         {
42759             this.flag.dom.className = 'flag '+n;
42760         },
42761         
42762         getValue : function()
42763         {
42764             var v = this.inputEl().getValue();
42765             if(this.dialCodeHolder) {
42766                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42767             }
42768             return v;
42769         },
42770         
42771         setValue : function(v)
42772         {
42773             var d = this.getDialCode(v);
42774             
42775             //invalid dial code
42776             if(v.length == 0 || !d || d.length == 0) {
42777                 if(this.rendered){
42778                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42779                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42780                 }
42781                 return;
42782             }
42783             
42784             //valid dial code
42785             this.setFlagClass(this.dialCodeMapping[d].iso2);
42786             this.setDialCode(d);
42787             this.inputEl().dom.value = v.replace('+'+d,'');
42788             this.hiddenEl().dom.value = this.getValue();
42789             
42790             this.validate();
42791         },
42792         
42793         getDialCode : function(v)
42794         {
42795             v = v ||  '';
42796             
42797             if (v.length == 0) {
42798                 return this.dialCodeHolder.dom.value;
42799             }
42800             
42801             var dialCode = "";
42802             if (v.charAt(0) != "+") {
42803                 return false;
42804             }
42805             var numericChars = "";
42806             for (var i = 1; i < v.length; i++) {
42807               var c = v.charAt(i);
42808               if (!isNaN(c)) {
42809                 numericChars += c;
42810                 if (this.dialCodeMapping[numericChars]) {
42811                   dialCode = v.substr(1, i);
42812                 }
42813                 if (numericChars.length == 4) {
42814                   break;
42815                 }
42816               }
42817             }
42818             return dialCode;
42819         },
42820         
42821         reset : function()
42822         {
42823             this.setValue(this.defaultDialCode);
42824             this.validate();
42825         },
42826         
42827         hiddenEl : function()
42828         {
42829             return this.el.select('input.hidden-tel-input',true).first();
42830         },
42831         
42832         // after setting val
42833         onKeyUp : function(e){
42834             this.setValue(this.getValue());
42835         },
42836         
42837         onKeyPress : function(e){
42838             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42839                 e.stopEvent();
42840             }
42841         }
42842         
42843 });
42844 /**
42845  * @class Roo.bootstrap.MoneyField
42846  * @extends Roo.bootstrap.ComboBox
42847  * Bootstrap MoneyField class
42848  * 
42849  * @constructor
42850  * Create a new MoneyField.
42851  * @param {Object} config Configuration options
42852  */
42853
42854 Roo.bootstrap.MoneyField = function(config) {
42855     
42856     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42857     
42858 };
42859
42860 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42861     
42862     /**
42863      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42864      */
42865     allowDecimals : true,
42866     /**
42867      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42868      */
42869     decimalSeparator : ".",
42870     /**
42871      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42872      */
42873     decimalPrecision : 0,
42874     /**
42875      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42876      */
42877     allowNegative : true,
42878     /**
42879      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42880      */
42881     allowZero: true,
42882     /**
42883      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42884      */
42885     minValue : Number.NEGATIVE_INFINITY,
42886     /**
42887      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42888      */
42889     maxValue : Number.MAX_VALUE,
42890     /**
42891      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42892      */
42893     minText : "The minimum value for this field is {0}",
42894     /**
42895      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42896      */
42897     maxText : "The maximum value for this field is {0}",
42898     /**
42899      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42900      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42901      */
42902     nanText : "{0} is not a valid number",
42903     /**
42904      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42905      */
42906     castInt : true,
42907     /**
42908      * @cfg {String} defaults currency of the MoneyField
42909      * value should be in lkey
42910      */
42911     defaultCurrency : false,
42912     /**
42913      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42914      */
42915     thousandsDelimiter : false,
42916     /**
42917      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42918      */
42919     max_length: false,
42920     
42921     inputlg : 9,
42922     inputmd : 9,
42923     inputsm : 9,
42924     inputxs : 6,
42925     
42926     store : false,
42927     
42928     getAutoCreate : function()
42929     {
42930         var align = this.labelAlign || this.parentLabelAlign();
42931         
42932         var id = Roo.id();
42933
42934         var cfg = {
42935             cls: 'form-group',
42936             cn: []
42937         };
42938
42939         var input =  {
42940             tag: 'input',
42941             id : id,
42942             cls : 'form-control roo-money-amount-input',
42943             autocomplete: 'new-password'
42944         };
42945         
42946         var hiddenInput = {
42947             tag: 'input',
42948             type: 'hidden',
42949             id: Roo.id(),
42950             cls: 'hidden-number-input'
42951         };
42952         
42953         if(this.max_length) {
42954             input.maxlength = this.max_length; 
42955         }
42956         
42957         if (this.name) {
42958             hiddenInput.name = this.name;
42959         }
42960
42961         if (this.disabled) {
42962             input.disabled = true;
42963         }
42964
42965         var clg = 12 - this.inputlg;
42966         var cmd = 12 - this.inputmd;
42967         var csm = 12 - this.inputsm;
42968         var cxs = 12 - this.inputxs;
42969         
42970         var container = {
42971             tag : 'div',
42972             cls : 'row roo-money-field',
42973             cn : [
42974                 {
42975                     tag : 'div',
42976                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42977                     cn : [
42978                         {
42979                             tag : 'div',
42980                             cls: 'roo-select2-container input-group',
42981                             cn: [
42982                                 {
42983                                     tag : 'input',
42984                                     cls : 'form-control roo-money-currency-input',
42985                                     autocomplete: 'new-password',
42986                                     readOnly : 1,
42987                                     name : this.currencyName
42988                                 },
42989                                 {
42990                                     tag :'span',
42991                                     cls : 'input-group-addon',
42992                                     cn : [
42993                                         {
42994                                             tag: 'span',
42995                                             cls: 'caret'
42996                                         }
42997                                     ]
42998                                 }
42999                             ]
43000                         }
43001                     ]
43002                 },
43003                 {
43004                     tag : 'div',
43005                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43006                     cn : [
43007                         {
43008                             tag: 'div',
43009                             cls: this.hasFeedback ? 'has-feedback' : '',
43010                             cn: [
43011                                 input
43012                             ]
43013                         }
43014                     ]
43015                 }
43016             ]
43017             
43018         };
43019         
43020         if (this.fieldLabel.length) {
43021             var indicator = {
43022                 tag: 'i',
43023                 tooltip: 'This field is required'
43024             };
43025
43026             var label = {
43027                 tag: 'label',
43028                 'for':  id,
43029                 cls: 'control-label',
43030                 cn: []
43031             };
43032
43033             var label_text = {
43034                 tag: 'span',
43035                 html: this.fieldLabel
43036             };
43037
43038             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43039             label.cn = [
43040                 indicator,
43041                 label_text
43042             ];
43043
43044             if(this.indicatorpos == 'right') {
43045                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43046                 label.cn = [
43047                     label_text,
43048                     indicator
43049                 ];
43050             }
43051
43052             if(align == 'left') {
43053                 container = {
43054                     tag: 'div',
43055                     cn: [
43056                         container
43057                     ]
43058                 };
43059
43060                 if(this.labelWidth > 12){
43061                     label.style = "width: " + this.labelWidth + 'px';
43062                 }
43063                 if(this.labelWidth < 13 && this.labelmd == 0){
43064                     this.labelmd = this.labelWidth;
43065                 }
43066                 if(this.labellg > 0){
43067                     label.cls += ' col-lg-' + this.labellg;
43068                     input.cls += ' col-lg-' + (12 - this.labellg);
43069                 }
43070                 if(this.labelmd > 0){
43071                     label.cls += ' col-md-' + this.labelmd;
43072                     container.cls += ' col-md-' + (12 - this.labelmd);
43073                 }
43074                 if(this.labelsm > 0){
43075                     label.cls += ' col-sm-' + this.labelsm;
43076                     container.cls += ' col-sm-' + (12 - this.labelsm);
43077                 }
43078                 if(this.labelxs > 0){
43079                     label.cls += ' col-xs-' + this.labelxs;
43080                     container.cls += ' col-xs-' + (12 - this.labelxs);
43081                 }
43082             }
43083         }
43084
43085         cfg.cn = [
43086             label,
43087             container,
43088             hiddenInput
43089         ];
43090         
43091         var settings = this;
43092
43093         ['xs','sm','md','lg'].map(function(size){
43094             if (settings[size]) {
43095                 cfg.cls += ' col-' + size + '-' + settings[size];
43096             }
43097         });
43098         
43099         return cfg;
43100     },
43101     
43102     initEvents : function()
43103     {
43104         this.indicator = this.indicatorEl();
43105         
43106         this.initCurrencyEvent();
43107         
43108         this.initNumberEvent();
43109     },
43110     
43111     initCurrencyEvent : function()
43112     {
43113         if (!this.store) {
43114             throw "can not find store for combo";
43115         }
43116         
43117         this.store = Roo.factory(this.store, Roo.data);
43118         this.store.parent = this;
43119         
43120         this.createList();
43121         
43122         this.triggerEl = this.el.select('.input-group-addon', true).first();
43123         
43124         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43125         
43126         var _this = this;
43127         
43128         (function(){
43129             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43130             _this.list.setWidth(lw);
43131         }).defer(100);
43132         
43133         this.list.on('mouseover', this.onViewOver, this);
43134         this.list.on('mousemove', this.onViewMove, this);
43135         this.list.on('scroll', this.onViewScroll, this);
43136         
43137         if(!this.tpl){
43138             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43139         }
43140         
43141         this.view = new Roo.View(this.list, this.tpl, {
43142             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43143         });
43144         
43145         this.view.on('click', this.onViewClick, this);
43146         
43147         this.store.on('beforeload', this.onBeforeLoad, this);
43148         this.store.on('load', this.onLoad, this);
43149         this.store.on('loadexception', this.onLoadException, this);
43150         
43151         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43152             "up" : function(e){
43153                 this.inKeyMode = true;
43154                 this.selectPrev();
43155             },
43156
43157             "down" : function(e){
43158                 if(!this.isExpanded()){
43159                     this.onTriggerClick();
43160                 }else{
43161                     this.inKeyMode = true;
43162                     this.selectNext();
43163                 }
43164             },
43165
43166             "enter" : function(e){
43167                 this.collapse();
43168                 
43169                 if(this.fireEvent("specialkey", this, e)){
43170                     this.onViewClick(false);
43171                 }
43172                 
43173                 return true;
43174             },
43175
43176             "esc" : function(e){
43177                 this.collapse();
43178             },
43179
43180             "tab" : function(e){
43181                 this.collapse();
43182                 
43183                 if(this.fireEvent("specialkey", this, e)){
43184                     this.onViewClick(false);
43185                 }
43186                 
43187                 return true;
43188             },
43189
43190             scope : this,
43191
43192             doRelay : function(foo, bar, hname){
43193                 if(hname == 'down' || this.scope.isExpanded()){
43194                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43195                 }
43196                 return true;
43197             },
43198
43199             forceKeyDown: true
43200         });
43201         
43202         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43203         
43204     },
43205     
43206     initNumberEvent : function(e)
43207     {
43208         this.inputEl().on("keydown" , this.fireKey,  this);
43209         this.inputEl().on("focus", this.onFocus,  this);
43210         this.inputEl().on("blur", this.onBlur,  this);
43211         
43212         this.inputEl().relayEvent('keyup', this);
43213         
43214         if(this.indicator){
43215             this.indicator.addClass('invisible');
43216         }
43217  
43218         this.originalValue = this.getValue();
43219         
43220         if(this.validationEvent == 'keyup'){
43221             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43222             this.inputEl().on('keyup', this.filterValidation, this);
43223         }
43224         else if(this.validationEvent !== false){
43225             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43226         }
43227         
43228         if(this.selectOnFocus){
43229             this.on("focus", this.preFocus, this);
43230             
43231         }
43232         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43233             this.inputEl().on("keypress", this.filterKeys, this);
43234         } else {
43235             this.inputEl().relayEvent('keypress', this);
43236         }
43237         
43238         var allowed = "0123456789";
43239         
43240         if(this.allowDecimals){
43241             allowed += this.decimalSeparator;
43242         }
43243         
43244         if(this.allowNegative){
43245             allowed += "-";
43246         }
43247         
43248         if(this.thousandsDelimiter) {
43249             allowed += ",";
43250         }
43251         
43252         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43253         
43254         var keyPress = function(e){
43255             
43256             var k = e.getKey();
43257             
43258             var c = e.getCharCode();
43259             
43260             if(
43261                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43262                     allowed.indexOf(String.fromCharCode(c)) === -1
43263             ){
43264                 e.stopEvent();
43265                 return;
43266             }
43267             
43268             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43269                 return;
43270             }
43271             
43272             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43273                 e.stopEvent();
43274             }
43275         };
43276         
43277         this.inputEl().on("keypress", keyPress, this);
43278         
43279     },
43280     
43281     onTriggerClick : function(e)
43282     {   
43283         if(this.disabled){
43284             return;
43285         }
43286         
43287         this.page = 0;
43288         this.loadNext = false;
43289         
43290         if(this.isExpanded()){
43291             this.collapse();
43292             return;
43293         }
43294         
43295         this.hasFocus = true;
43296         
43297         if(this.triggerAction == 'all') {
43298             this.doQuery(this.allQuery, true);
43299             return;
43300         }
43301         
43302         this.doQuery(this.getRawValue());
43303     },
43304     
43305     getCurrency : function()
43306     {   
43307         var v = this.currencyEl().getValue();
43308         
43309         return v;
43310     },
43311     
43312     restrictHeight : function()
43313     {
43314         this.list.alignTo(this.currencyEl(), this.listAlign);
43315         this.list.alignTo(this.currencyEl(), this.listAlign);
43316     },
43317     
43318     onViewClick : function(view, doFocus, el, e)
43319     {
43320         var index = this.view.getSelectedIndexes()[0];
43321         
43322         var r = this.store.getAt(index);
43323         
43324         if(r){
43325             this.onSelect(r, index);
43326         }
43327     },
43328     
43329     onSelect : function(record, index){
43330         
43331         if(this.fireEvent('beforeselect', this, record, index) !== false){
43332         
43333             this.setFromCurrencyData(index > -1 ? record.data : false);
43334             
43335             this.collapse();
43336             
43337             this.fireEvent('select', this, record, index);
43338         }
43339     },
43340     
43341     setFromCurrencyData : function(o)
43342     {
43343         var currency = '';
43344         
43345         this.lastCurrency = o;
43346         
43347         if (this.currencyField) {
43348             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43349         } else {
43350             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43351         }
43352         
43353         this.lastSelectionText = currency;
43354         
43355         //setting default currency
43356         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43357             this.setCurrency(this.defaultCurrency);
43358             return;
43359         }
43360         
43361         this.setCurrency(currency);
43362     },
43363     
43364     setFromData : function(o)
43365     {
43366         var c = {};
43367         
43368         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43369         
43370         this.setFromCurrencyData(c);
43371         
43372         var value = '';
43373         
43374         if (this.name) {
43375             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43376         } else {
43377             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43378         }
43379         
43380         this.setValue(value);
43381         
43382     },
43383     
43384     setCurrency : function(v)
43385     {   
43386         this.currencyValue = v;
43387         
43388         if(this.rendered){
43389             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43390             this.validate();
43391         }
43392     },
43393     
43394     setValue : function(v)
43395     {
43396         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43397         
43398         this.value = v;
43399         
43400         if(this.rendered){
43401             
43402             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43403             
43404             this.inputEl().dom.value = (v == '') ? '' :
43405                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43406             
43407             if(!this.allowZero && v === '0') {
43408                 this.hiddenEl().dom.value = '';
43409                 this.inputEl().dom.value = '';
43410             }
43411             
43412             this.validate();
43413         }
43414     },
43415     
43416     getRawValue : function()
43417     {
43418         var v = this.inputEl().getValue();
43419         
43420         return v;
43421     },
43422     
43423     getValue : function()
43424     {
43425         return this.fixPrecision(this.parseValue(this.getRawValue()));
43426     },
43427     
43428     parseValue : function(value)
43429     {
43430         if(this.thousandsDelimiter) {
43431             value += "";
43432             r = new RegExp(",", "g");
43433             value = value.replace(r, "");
43434         }
43435         
43436         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43437         return isNaN(value) ? '' : value;
43438         
43439     },
43440     
43441     fixPrecision : function(value)
43442     {
43443         if(this.thousandsDelimiter) {
43444             value += "";
43445             r = new RegExp(",", "g");
43446             value = value.replace(r, "");
43447         }
43448         
43449         var nan = isNaN(value);
43450         
43451         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43452             return nan ? '' : value;
43453         }
43454         return parseFloat(value).toFixed(this.decimalPrecision);
43455     },
43456     
43457     decimalPrecisionFcn : function(v)
43458     {
43459         return Math.floor(v);
43460     },
43461     
43462     validateValue : function(value)
43463     {
43464         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43465             return false;
43466         }
43467         
43468         var num = this.parseValue(value);
43469         
43470         if(isNaN(num)){
43471             this.markInvalid(String.format(this.nanText, value));
43472             return false;
43473         }
43474         
43475         if(num < this.minValue){
43476             this.markInvalid(String.format(this.minText, this.minValue));
43477             return false;
43478         }
43479         
43480         if(num > this.maxValue){
43481             this.markInvalid(String.format(this.maxText, this.maxValue));
43482             return false;
43483         }
43484         
43485         return true;
43486     },
43487     
43488     validate : function()
43489     {
43490         if(this.disabled || this.allowBlank){
43491             this.markValid();
43492             return true;
43493         }
43494         
43495         var currency = this.getCurrency();
43496         
43497         if(this.validateValue(this.getRawValue()) && currency.length){
43498             this.markValid();
43499             return true;
43500         }
43501         
43502         this.markInvalid();
43503         return false;
43504     },
43505     
43506     getName: function()
43507     {
43508         return this.name;
43509     },
43510     
43511     beforeBlur : function()
43512     {
43513         if(!this.castInt){
43514             return;
43515         }
43516         
43517         var v = this.parseValue(this.getRawValue());
43518         
43519         if(v || v == 0){
43520             this.setValue(v);
43521         }
43522     },
43523     
43524     onBlur : function()
43525     {
43526         this.beforeBlur();
43527         
43528         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43529             //this.el.removeClass(this.focusClass);
43530         }
43531         
43532         this.hasFocus = false;
43533         
43534         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43535             this.validate();
43536         }
43537         
43538         var v = this.getValue();
43539         
43540         if(String(v) !== String(this.startValue)){
43541             this.fireEvent('change', this, v, this.startValue);
43542         }
43543         
43544         this.fireEvent("blur", this);
43545     },
43546     
43547     inputEl : function()
43548     {
43549         return this.el.select('.roo-money-amount-input', true).first();
43550     },
43551     
43552     currencyEl : function()
43553     {
43554         return this.el.select('.roo-money-currency-input', true).first();
43555     },
43556     
43557     hiddenEl : function()
43558     {
43559         return this.el.select('input.hidden-number-input',true).first();
43560     }
43561     
43562 });/**
43563  * @class Roo.bootstrap.BezierSignature
43564  * @extends Roo.bootstrap.Component
43565  * Bootstrap BezierSignature class
43566  * This script refer to:
43567  *    Title: Signature Pad
43568  *    Author: szimek
43569  *    Availability: https://github.com/szimek/signature_pad
43570  *
43571  * @constructor
43572  * Create a new BezierSignature
43573  * @param {Object} config The config object
43574  */
43575
43576 Roo.bootstrap.BezierSignature = function(config){
43577     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43578     this.addEvents({
43579         "resize" : true
43580     });
43581 };
43582
43583 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43584 {
43585      
43586     curve_data: [],
43587     
43588     is_empty: true,
43589     
43590     mouse_btn_down: true,
43591     
43592     /**
43593      * @cfg {int} canvas height
43594      */
43595     canvas_height: '200px',
43596     
43597     /**
43598      * @cfg {float|function} Radius of a single dot.
43599      */ 
43600     dot_size: false,
43601     
43602     /**
43603      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43604      */
43605     min_width: 0.5,
43606     
43607     /**
43608      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43609      */
43610     max_width: 2.5,
43611     
43612     /**
43613      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43614      */
43615     throttle: 16,
43616     
43617     /**
43618      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43619      */
43620     min_distance: 5,
43621     
43622     /**
43623      * @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.
43624      */
43625     bg_color: 'rgba(0, 0, 0, 0)',
43626     
43627     /**
43628      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43629      */
43630     dot_color: 'black',
43631     
43632     /**
43633      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43634      */ 
43635     velocity_filter_weight: 0.7,
43636     
43637     /**
43638      * @cfg {function} Callback when stroke begin. 
43639      */
43640     onBegin: false,
43641     
43642     /**
43643      * @cfg {function} Callback when stroke end.
43644      */
43645     onEnd: false,
43646     
43647     getAutoCreate : function()
43648     {
43649         var cls = 'roo-signature column';
43650         
43651         if(this.cls){
43652             cls += ' ' + this.cls;
43653         }
43654         
43655         var col_sizes = [
43656             'lg',
43657             'md',
43658             'sm',
43659             'xs'
43660         ];
43661         
43662         for(var i = 0; i < col_sizes.length; i++) {
43663             if(this[col_sizes[i]]) {
43664                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43665             }
43666         }
43667         
43668         var cfg = {
43669             tag: 'div',
43670             cls: cls,
43671             cn: [
43672                 {
43673                     tag: 'div',
43674                     cls: 'roo-signature-body',
43675                     cn: [
43676                         {
43677                             tag: 'canvas',
43678                             cls: 'roo-signature-body-canvas',
43679                             height: this.canvas_height,
43680                             width: this.canvas_width
43681                         }
43682                     ]
43683                 },
43684                 {
43685                     tag: 'input',
43686                     type: 'file',
43687                     style: 'display: none'
43688                 }
43689             ]
43690         };
43691         
43692         return cfg;
43693     },
43694     
43695     initEvents: function() 
43696     {
43697         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43698         
43699         var canvas = this.canvasEl();
43700         
43701         // mouse && touch event swapping...
43702         canvas.dom.style.touchAction = 'none';
43703         canvas.dom.style.msTouchAction = 'none';
43704         
43705         this.mouse_btn_down = false;
43706         canvas.on('mousedown', this._handleMouseDown, this);
43707         canvas.on('mousemove', this._handleMouseMove, this);
43708         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43709         
43710         if (window.PointerEvent) {
43711             canvas.on('pointerdown', this._handleMouseDown, this);
43712             canvas.on('pointermove', this._handleMouseMove, this);
43713             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43714         }
43715         
43716         if ('ontouchstart' in window) {
43717             canvas.on('touchstart', this._handleTouchStart, this);
43718             canvas.on('touchmove', this._handleTouchMove, this);
43719             canvas.on('touchend', this._handleTouchEnd, this);
43720         }
43721         
43722         Roo.EventManager.onWindowResize(this.resize, this, true);
43723         
43724         // file input event
43725         this.fileEl().on('change', this.uploadImage, this);
43726         
43727         this.clear();
43728         
43729         this.resize();
43730     },
43731     
43732     resize: function(){
43733         
43734         var canvas = this.canvasEl().dom;
43735         var ctx = this.canvasElCtx();
43736         var img_data = false;
43737         
43738         if(canvas.width > 0) {
43739             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43740         }
43741         // setting canvas width will clean img data
43742         canvas.width = 0;
43743         
43744         var style = window.getComputedStyle ? 
43745             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43746             
43747         var padding_left = parseInt(style.paddingLeft) || 0;
43748         var padding_right = parseInt(style.paddingRight) || 0;
43749         
43750         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43751         
43752         if(img_data) {
43753             ctx.putImageData(img_data, 0, 0);
43754         }
43755     },
43756     
43757     _handleMouseDown: function(e)
43758     {
43759         if (e.browserEvent.which === 1) {
43760             this.mouse_btn_down = true;
43761             this.strokeBegin(e);
43762         }
43763     },
43764     
43765     _handleMouseMove: function (e)
43766     {
43767         if (this.mouse_btn_down) {
43768             this.strokeMoveUpdate(e);
43769         }
43770     },
43771     
43772     _handleMouseUp: function (e)
43773     {
43774         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43775             this.mouse_btn_down = false;
43776             this.strokeEnd(e);
43777         }
43778     },
43779     
43780     _handleTouchStart: function (e) {
43781         
43782         e.preventDefault();
43783         if (e.browserEvent.targetTouches.length === 1) {
43784             // var touch = e.browserEvent.changedTouches[0];
43785             // this.strokeBegin(touch);
43786             
43787              this.strokeBegin(e); // assume e catching the correct xy...
43788         }
43789     },
43790     
43791     _handleTouchMove: function (e) {
43792         e.preventDefault();
43793         // var touch = event.targetTouches[0];
43794         // _this._strokeMoveUpdate(touch);
43795         this.strokeMoveUpdate(e);
43796     },
43797     
43798     _handleTouchEnd: function (e) {
43799         var wasCanvasTouched = e.target === this.canvasEl().dom;
43800         if (wasCanvasTouched) {
43801             e.preventDefault();
43802             // var touch = event.changedTouches[0];
43803             // _this._strokeEnd(touch);
43804             this.strokeEnd(e);
43805         }
43806     },
43807     
43808     reset: function () {
43809         this._lastPoints = [];
43810         this._lastVelocity = 0;
43811         this._lastWidth = (this.min_width + this.max_width) / 2;
43812         this.canvasElCtx().fillStyle = this.dot_color;
43813     },
43814     
43815     strokeMoveUpdate: function(e)
43816     {
43817         this.strokeUpdate(e);
43818         
43819         if (this.throttle) {
43820             this.throttleStroke(this.strokeUpdate, this.throttle);
43821         }
43822         else {
43823             this.strokeUpdate(e);
43824         }
43825     },
43826     
43827     strokeBegin: function(e)
43828     {
43829         var newPointGroup = {
43830             color: this.dot_color,
43831             points: []
43832         };
43833         
43834         if (typeof this.onBegin === 'function') {
43835             this.onBegin(e);
43836         }
43837         
43838         this.curve_data.push(newPointGroup);
43839         this.reset();
43840         this.strokeUpdate(e);
43841     },
43842     
43843     strokeUpdate: function(e)
43844     {
43845         var rect = this.canvasEl().dom.getBoundingClientRect();
43846         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43847         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43848         var lastPoints = lastPointGroup.points;
43849         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43850         var isLastPointTooClose = lastPoint
43851             ? point.distanceTo(lastPoint) <= this.min_distance
43852             : false;
43853         var color = lastPointGroup.color;
43854         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43855             var curve = this.addPoint(point);
43856             if (!lastPoint) {
43857                 this.drawDot({color: color, point: point});
43858             }
43859             else if (curve) {
43860                 this.drawCurve({color: color, curve: curve});
43861             }
43862             lastPoints.push({
43863                 time: point.time,
43864                 x: point.x,
43865                 y: point.y
43866             });
43867         }
43868     },
43869     
43870     strokeEnd: function(e)
43871     {
43872         this.strokeUpdate(e);
43873         if (typeof this.onEnd === 'function') {
43874             this.onEnd(e);
43875         }
43876     },
43877     
43878     addPoint:  function (point) {
43879         var _lastPoints = this._lastPoints;
43880         _lastPoints.push(point);
43881         if (_lastPoints.length > 2) {
43882             if (_lastPoints.length === 3) {
43883                 _lastPoints.unshift(_lastPoints[0]);
43884             }
43885             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43886             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43887             _lastPoints.shift();
43888             return curve;
43889         }
43890         return null;
43891     },
43892     
43893     calculateCurveWidths: function (startPoint, endPoint) {
43894         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43895             (1 - this.velocity_filter_weight) * this._lastVelocity;
43896
43897         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43898         var widths = {
43899             end: newWidth,
43900             start: this._lastWidth
43901         };
43902         
43903         this._lastVelocity = velocity;
43904         this._lastWidth = newWidth;
43905         return widths;
43906     },
43907     
43908     drawDot: function (_a) {
43909         var color = _a.color, point = _a.point;
43910         var ctx = this.canvasElCtx();
43911         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43912         ctx.beginPath();
43913         this.drawCurveSegment(point.x, point.y, width);
43914         ctx.closePath();
43915         ctx.fillStyle = color;
43916         ctx.fill();
43917     },
43918     
43919     drawCurve: function (_a) {
43920         var color = _a.color, curve = _a.curve;
43921         var ctx = this.canvasElCtx();
43922         var widthDelta = curve.endWidth - curve.startWidth;
43923         var drawSteps = Math.floor(curve.length()) * 2;
43924         ctx.beginPath();
43925         ctx.fillStyle = color;
43926         for (var i = 0; i < drawSteps; i += 1) {
43927         var t = i / drawSteps;
43928         var tt = t * t;
43929         var ttt = tt * t;
43930         var u = 1 - t;
43931         var uu = u * u;
43932         var uuu = uu * u;
43933         var x = uuu * curve.startPoint.x;
43934         x += 3 * uu * t * curve.control1.x;
43935         x += 3 * u * tt * curve.control2.x;
43936         x += ttt * curve.endPoint.x;
43937         var y = uuu * curve.startPoint.y;
43938         y += 3 * uu * t * curve.control1.y;
43939         y += 3 * u * tt * curve.control2.y;
43940         y += ttt * curve.endPoint.y;
43941         var width = curve.startWidth + ttt * widthDelta;
43942         this.drawCurveSegment(x, y, width);
43943         }
43944         ctx.closePath();
43945         ctx.fill();
43946     },
43947     
43948     drawCurveSegment: function (x, y, width) {
43949         var ctx = this.canvasElCtx();
43950         ctx.moveTo(x, y);
43951         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43952         this.is_empty = false;
43953     },
43954     
43955     clear: function()
43956     {
43957         var ctx = this.canvasElCtx();
43958         var canvas = this.canvasEl().dom;
43959         ctx.fillStyle = this.bg_color;
43960         ctx.clearRect(0, 0, canvas.width, canvas.height);
43961         ctx.fillRect(0, 0, canvas.width, canvas.height);
43962         this.curve_data = [];
43963         this.reset();
43964         this.is_empty = true;
43965     },
43966     
43967     fileEl: function()
43968     {
43969         return  this.el.select('input',true).first();
43970     },
43971     
43972     canvasEl: function()
43973     {
43974         return this.el.select('canvas',true).first();
43975     },
43976     
43977     canvasElCtx: function()
43978     {
43979         return this.el.select('canvas',true).first().dom.getContext('2d');
43980     },
43981     
43982     getImage: function(type)
43983     {
43984         if(this.is_empty) {
43985             return false;
43986         }
43987         
43988         // encryption ?
43989         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43990     },
43991     
43992     drawFromImage: function(img_src)
43993     {
43994         var img = new Image();
43995         
43996         img.onload = function(){
43997             this.canvasElCtx().drawImage(img, 0, 0);
43998         }.bind(this);
43999         
44000         img.src = img_src;
44001         
44002         this.is_empty = false;
44003     },
44004     
44005     selectImage: function()
44006     {
44007         this.fileEl().dom.click();
44008     },
44009     
44010     uploadImage: function(e)
44011     {
44012         var reader = new FileReader();
44013         
44014         reader.onload = function(e){
44015             var img = new Image();
44016             img.onload = function(){
44017                 this.reset();
44018                 this.canvasElCtx().drawImage(img, 0, 0);
44019             }.bind(this);
44020             img.src = e.target.result;
44021         }.bind(this);
44022         
44023         reader.readAsDataURL(e.target.files[0]);
44024     },
44025     
44026     // Bezier Point Constructor
44027     Point: (function () {
44028         function Point(x, y, time) {
44029             this.x = x;
44030             this.y = y;
44031             this.time = time || Date.now();
44032         }
44033         Point.prototype.distanceTo = function (start) {
44034             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44035         };
44036         Point.prototype.equals = function (other) {
44037             return this.x === other.x && this.y === other.y && this.time === other.time;
44038         };
44039         Point.prototype.velocityFrom = function (start) {
44040             return this.time !== start.time
44041             ? this.distanceTo(start) / (this.time - start.time)
44042             : 0;
44043         };
44044         return Point;
44045     }()),
44046     
44047     
44048     // Bezier Constructor
44049     Bezier: (function () {
44050         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44051             this.startPoint = startPoint;
44052             this.control2 = control2;
44053             this.control1 = control1;
44054             this.endPoint = endPoint;
44055             this.startWidth = startWidth;
44056             this.endWidth = endWidth;
44057         }
44058         Bezier.fromPoints = function (points, widths, scope) {
44059             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44060             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44061             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44062         };
44063         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44064             var dx1 = s1.x - s2.x;
44065             var dy1 = s1.y - s2.y;
44066             var dx2 = s2.x - s3.x;
44067             var dy2 = s2.y - s3.y;
44068             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44069             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44070             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44071             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44072             var dxm = m1.x - m2.x;
44073             var dym = m1.y - m2.y;
44074             var k = l2 / (l1 + l2);
44075             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44076             var tx = s2.x - cm.x;
44077             var ty = s2.y - cm.y;
44078             return {
44079                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44080                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44081             };
44082         };
44083         Bezier.prototype.length = function () {
44084             var steps = 10;
44085             var length = 0;
44086             var px;
44087             var py;
44088             for (var i = 0; i <= steps; i += 1) {
44089                 var t = i / steps;
44090                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44091                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44092                 if (i > 0) {
44093                     var xdiff = cx - px;
44094                     var ydiff = cy - py;
44095                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44096                 }
44097                 px = cx;
44098                 py = cy;
44099             }
44100             return length;
44101         };
44102         Bezier.prototype.point = function (t, start, c1, c2, end) {
44103             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44104             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44105             + (3.0 * c2 * (1.0 - t) * t * t)
44106             + (end * t * t * t);
44107         };
44108         return Bezier;
44109     }()),
44110     
44111     throttleStroke: function(fn, wait) {
44112       if (wait === void 0) { wait = 250; }
44113       var previous = 0;
44114       var timeout = null;
44115       var result;
44116       var storedContext;
44117       var storedArgs;
44118       var later = function () {
44119           previous = Date.now();
44120           timeout = null;
44121           result = fn.apply(storedContext, storedArgs);
44122           if (!timeout) {
44123               storedContext = null;
44124               storedArgs = [];
44125           }
44126       };
44127       return function wrapper() {
44128           var args = [];
44129           for (var _i = 0; _i < arguments.length; _i++) {
44130               args[_i] = arguments[_i];
44131           }
44132           var now = Date.now();
44133           var remaining = wait - (now - previous);
44134           storedContext = this;
44135           storedArgs = args;
44136           if (remaining <= 0 || remaining > wait) {
44137               if (timeout) {
44138                   clearTimeout(timeout);
44139                   timeout = null;
44140               }
44141               previous = now;
44142               result = fn.apply(storedContext, storedArgs);
44143               if (!timeout) {
44144                   storedContext = null;
44145                   storedArgs = [];
44146               }
44147           }
44148           else if (!timeout) {
44149               timeout = window.setTimeout(later, remaining);
44150           }
44151           return result;
44152       };
44153   }
44154   
44155 });
44156
44157  
44158
44159