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         };
6709         if (this.href !== false) {
6710             cfg.cn = [{
6711                 tag : 'a',
6712                 href : this.href,
6713                 html : this.html
6714             }];
6715         } else {
6716             cfg.html = this.html;
6717         }
6718         
6719         return cfg;
6720     },
6721     
6722     initEvents: function()
6723     {
6724         if (this.href) {
6725             this.el.select('a', true).first().onClick(this.onClick, this)
6726         }
6727         
6728     },
6729     onClick : function(e)
6730     {
6731         e.preventDefault();
6732         this.fireEvent('click',this,  e);
6733     }
6734     
6735 });
6736
6737  /*
6738  * - LGPL
6739  *
6740  * row
6741  * 
6742  */
6743
6744 /**
6745  * @class Roo.bootstrap.Row
6746  * @extends Roo.bootstrap.Component
6747  * Bootstrap Row class (contains columns...)
6748  * 
6749  * @constructor
6750  * Create a new Row
6751  * @param {Object} config The config object
6752  */
6753
6754 Roo.bootstrap.Row = function(config){
6755     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6756 };
6757
6758 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6759     
6760     getAutoCreate : function(){
6761        return {
6762             cls: 'row clearfix'
6763        };
6764     }
6765     
6766     
6767 });
6768
6769  
6770
6771  /*
6772  * - LGPL
6773  *
6774  * pagination
6775  * 
6776  */
6777
6778 /**
6779  * @class Roo.bootstrap.Pagination
6780  * @extends Roo.bootstrap.Component
6781  * Bootstrap Pagination class
6782  * @cfg {String} size xs | sm | md | lg
6783  * @cfg {Boolean} inverse false | true
6784  * 
6785  * @constructor
6786  * Create a new Pagination
6787  * @param {Object} config The config object
6788  */
6789
6790 Roo.bootstrap.Pagination = function(config){
6791     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6792 };
6793
6794 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6795     
6796     cls: false,
6797     size: false,
6798     inverse: false,
6799     
6800     getAutoCreate : function(){
6801         var cfg = {
6802             tag: 'ul',
6803                 cls: 'pagination'
6804         };
6805         if (this.inverse) {
6806             cfg.cls += ' inverse';
6807         }
6808         if (this.html) {
6809             cfg.html=this.html;
6810         }
6811         if (this.cls) {
6812             cfg.cls += " " + this.cls;
6813         }
6814         return cfg;
6815     }
6816    
6817 });
6818
6819  
6820
6821  /*
6822  * - LGPL
6823  *
6824  * Pagination item
6825  * 
6826  */
6827
6828
6829 /**
6830  * @class Roo.bootstrap.PaginationItem
6831  * @extends Roo.bootstrap.Component
6832  * Bootstrap PaginationItem class
6833  * @cfg {String} html text
6834  * @cfg {String} href the link
6835  * @cfg {Boolean} preventDefault (true | false) default true
6836  * @cfg {Boolean} active (true | false) default false
6837  * @cfg {Boolean} disabled default false
6838  * 
6839  * 
6840  * @constructor
6841  * Create a new PaginationItem
6842  * @param {Object} config The config object
6843  */
6844
6845
6846 Roo.bootstrap.PaginationItem = function(config){
6847     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6848     this.addEvents({
6849         // raw events
6850         /**
6851          * @event click
6852          * The raw click event for the entire grid.
6853          * @param {Roo.EventObject} e
6854          */
6855         "click" : true
6856     });
6857 };
6858
6859 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6860     
6861     href : false,
6862     html : false,
6863     preventDefault: true,
6864     active : false,
6865     cls : false,
6866     disabled: false,
6867     
6868     getAutoCreate : function(){
6869         var cfg= {
6870             tag: 'li',
6871             cn: [
6872                 {
6873                     tag : 'a',
6874                     href : this.href ? this.href : '#',
6875                     html : this.html ? this.html : ''
6876                 }
6877             ]
6878         };
6879         
6880         if(this.cls){
6881             cfg.cls = this.cls;
6882         }
6883         
6884         if(this.disabled){
6885             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6886         }
6887         
6888         if(this.active){
6889             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6890         }
6891         
6892         return cfg;
6893     },
6894     
6895     initEvents: function() {
6896         
6897         this.el.on('click', this.onClick, this);
6898         
6899     },
6900     onClick : function(e)
6901     {
6902         Roo.log('PaginationItem on click ');
6903         if(this.preventDefault){
6904             e.preventDefault();
6905         }
6906         
6907         if(this.disabled){
6908             return;
6909         }
6910         
6911         this.fireEvent('click', this, e);
6912     }
6913    
6914 });
6915
6916  
6917
6918  /*
6919  * - LGPL
6920  *
6921  * slider
6922  * 
6923  */
6924
6925
6926 /**
6927  * @class Roo.bootstrap.Slider
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap Slider class
6930  *    
6931  * @constructor
6932  * Create a new Slider
6933  * @param {Object} config The config object
6934  */
6935
6936 Roo.bootstrap.Slider = function(config){
6937     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6938 };
6939
6940 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6941     
6942     getAutoCreate : function(){
6943         
6944         var cfg = {
6945             tag: 'div',
6946             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6947             cn: [
6948                 {
6949                     tag: 'a',
6950                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6951                 }
6952             ]
6953         };
6954         
6955         return cfg;
6956     }
6957    
6958 });
6959
6960  /*
6961  * Based on:
6962  * Ext JS Library 1.1.1
6963  * Copyright(c) 2006-2007, Ext JS, LLC.
6964  *
6965  * Originally Released Under LGPL - original licence link has changed is not relivant.
6966  *
6967  * Fork - LGPL
6968  * <script type="text/javascript">
6969  */
6970  
6971
6972 /**
6973  * @class Roo.grid.ColumnModel
6974  * @extends Roo.util.Observable
6975  * This is the default implementation of a ColumnModel used by the Grid. It defines
6976  * the columns in the grid.
6977  * <br>Usage:<br>
6978  <pre><code>
6979  var colModel = new Roo.grid.ColumnModel([
6980         {header: "Ticker", width: 60, sortable: true, locked: true},
6981         {header: "Company Name", width: 150, sortable: true},
6982         {header: "Market Cap.", width: 100, sortable: true},
6983         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6984         {header: "Employees", width: 100, sortable: true, resizable: false}
6985  ]);
6986  </code></pre>
6987  * <p>
6988  
6989  * The config options listed for this class are options which may appear in each
6990  * individual column definition.
6991  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6992  * @constructor
6993  * @param {Object} config An Array of column config objects. See this class's
6994  * config objects for details.
6995 */
6996 Roo.grid.ColumnModel = function(config){
6997         /**
6998      * The config passed into the constructor
6999      */
7000     this.config = config;
7001     this.lookup = {};
7002
7003     // if no id, create one
7004     // if the column does not have a dataIndex mapping,
7005     // map it to the order it is in the config
7006     for(var i = 0, len = config.length; i < len; i++){
7007         var c = config[i];
7008         if(typeof c.dataIndex == "undefined"){
7009             c.dataIndex = i;
7010         }
7011         if(typeof c.renderer == "string"){
7012             c.renderer = Roo.util.Format[c.renderer];
7013         }
7014         if(typeof c.id == "undefined"){
7015             c.id = Roo.id();
7016         }
7017         if(c.editor && c.editor.xtype){
7018             c.editor  = Roo.factory(c.editor, Roo.grid);
7019         }
7020         if(c.editor && c.editor.isFormField){
7021             c.editor = new Roo.grid.GridEditor(c.editor);
7022         }
7023         this.lookup[c.id] = c;
7024     }
7025
7026     /**
7027      * The width of columns which have no width specified (defaults to 100)
7028      * @type Number
7029      */
7030     this.defaultWidth = 100;
7031
7032     /**
7033      * Default sortable of columns which have no sortable specified (defaults to false)
7034      * @type Boolean
7035      */
7036     this.defaultSortable = false;
7037
7038     this.addEvents({
7039         /**
7040              * @event widthchange
7041              * Fires when the width of a column changes.
7042              * @param {ColumnModel} this
7043              * @param {Number} columnIndex The column index
7044              * @param {Number} newWidth The new width
7045              */
7046             "widthchange": true,
7047         /**
7048              * @event headerchange
7049              * Fires when the text of a header changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newText The new header text
7053              */
7054             "headerchange": true,
7055         /**
7056              * @event hiddenchange
7057              * Fires when a column is hidden or "unhidden".
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Boolean} hidden true if hidden, false otherwise
7061              */
7062             "hiddenchange": true,
7063             /**
7064          * @event columnmoved
7065          * Fires when a column is moved.
7066          * @param {ColumnModel} this
7067          * @param {Number} oldIndex
7068          * @param {Number} newIndex
7069          */
7070         "columnmoved" : true,
7071         /**
7072          * @event columlockchange
7073          * Fires when a column's locked state is changed
7074          * @param {ColumnModel} this
7075          * @param {Number} colIndex
7076          * @param {Boolean} locked true if locked
7077          */
7078         "columnlockchange" : true
7079     });
7080     Roo.grid.ColumnModel.superclass.constructor.call(this);
7081 };
7082 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7083     /**
7084      * @cfg {String} header The header text to display in the Grid view.
7085      */
7086     /**
7087      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7088      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7089      * specified, the column's index is used as an index into the Record's data Array.
7090      */
7091     /**
7092      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7093      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7094      */
7095     /**
7096      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7097      * Defaults to the value of the {@link #defaultSortable} property.
7098      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7099      */
7100     /**
7101      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7102      */
7103     /**
7104      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7105      */
7106     /**
7107      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7108      */
7109     /**
7110      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7111      */
7112     /**
7113      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7114      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7115      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7116      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7117      */
7118        /**
7119      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7120      */
7121     /**
7122      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7123      */
7124     /**
7125      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7126      */
7127     /**
7128      * @cfg {String} cursor (Optional)
7129      */
7130     /**
7131      * @cfg {String} tooltip (Optional)
7132      */
7133     /**
7134      * @cfg {Number} xs (Optional)
7135      */
7136     /**
7137      * @cfg {Number} sm (Optional)
7138      */
7139     /**
7140      * @cfg {Number} md (Optional)
7141      */
7142     /**
7143      * @cfg {Number} lg (Optional)
7144      */
7145     /**
7146      * Returns the id of the column at the specified index.
7147      * @param {Number} index The column index
7148      * @return {String} the id
7149      */
7150     getColumnId : function(index){
7151         return this.config[index].id;
7152     },
7153
7154     /**
7155      * Returns the column for a specified id.
7156      * @param {String} id The column id
7157      * @return {Object} the column
7158      */
7159     getColumnById : function(id){
7160         return this.lookup[id];
7161     },
7162
7163     
7164     /**
7165      * Returns the column for a specified dataIndex.
7166      * @param {String} dataIndex The column dataIndex
7167      * @return {Object|Boolean} the column or false if not found
7168      */
7169     getColumnByDataIndex: function(dataIndex){
7170         var index = this.findColumnIndex(dataIndex);
7171         return index > -1 ? this.config[index] : false;
7172     },
7173     
7174     /**
7175      * Returns the index for a specified column id.
7176      * @param {String} id The column id
7177      * @return {Number} the index, or -1 if not found
7178      */
7179     getIndexById : function(id){
7180         for(var i = 0, len = this.config.length; i < len; i++){
7181             if(this.config[i].id == id){
7182                 return i;
7183             }
7184         }
7185         return -1;
7186     },
7187     
7188     /**
7189      * Returns the index for a specified column dataIndex.
7190      * @param {String} dataIndex The column dataIndex
7191      * @return {Number} the index, or -1 if not found
7192      */
7193     
7194     findColumnIndex : function(dataIndex){
7195         for(var i = 0, len = this.config.length; i < len; i++){
7196             if(this.config[i].dataIndex == dataIndex){
7197                 return i;
7198             }
7199         }
7200         return -1;
7201     },
7202     
7203     
7204     moveColumn : function(oldIndex, newIndex){
7205         var c = this.config[oldIndex];
7206         this.config.splice(oldIndex, 1);
7207         this.config.splice(newIndex, 0, c);
7208         this.dataMap = null;
7209         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7210     },
7211
7212     isLocked : function(colIndex){
7213         return this.config[colIndex].locked === true;
7214     },
7215
7216     setLocked : function(colIndex, value, suppressEvent){
7217         if(this.isLocked(colIndex) == value){
7218             return;
7219         }
7220         this.config[colIndex].locked = value;
7221         if(!suppressEvent){
7222             this.fireEvent("columnlockchange", this, colIndex, value);
7223         }
7224     },
7225
7226     getTotalLockedWidth : function(){
7227         var totalWidth = 0;
7228         for(var i = 0; i < this.config.length; i++){
7229             if(this.isLocked(i) && !this.isHidden(i)){
7230                 this.totalWidth += this.getColumnWidth(i);
7231             }
7232         }
7233         return totalWidth;
7234     },
7235
7236     getLockedCount : function(){
7237         for(var i = 0, len = this.config.length; i < len; i++){
7238             if(!this.isLocked(i)){
7239                 return i;
7240             }
7241         }
7242         
7243         return this.config.length;
7244     },
7245
7246     /**
7247      * Returns the number of columns.
7248      * @return {Number}
7249      */
7250     getColumnCount : function(visibleOnly){
7251         if(visibleOnly === true){
7252             var c = 0;
7253             for(var i = 0, len = this.config.length; i < len; i++){
7254                 if(!this.isHidden(i)){
7255                     c++;
7256                 }
7257             }
7258             return c;
7259         }
7260         return this.config.length;
7261     },
7262
7263     /**
7264      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7265      * @param {Function} fn
7266      * @param {Object} scope (optional)
7267      * @return {Array} result
7268      */
7269     getColumnsBy : function(fn, scope){
7270         var r = [];
7271         for(var i = 0, len = this.config.length; i < len; i++){
7272             var c = this.config[i];
7273             if(fn.call(scope||this, c, i) === true){
7274                 r[r.length] = c;
7275             }
7276         }
7277         return r;
7278     },
7279
7280     /**
7281      * Returns true if the specified column is sortable.
7282      * @param {Number} col The column index
7283      * @return {Boolean}
7284      */
7285     isSortable : function(col){
7286         if(typeof this.config[col].sortable == "undefined"){
7287             return this.defaultSortable;
7288         }
7289         return this.config[col].sortable;
7290     },
7291
7292     /**
7293      * Returns the rendering (formatting) function defined for the column.
7294      * @param {Number} col The column index.
7295      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7296      */
7297     getRenderer : function(col){
7298         if(!this.config[col].renderer){
7299             return Roo.grid.ColumnModel.defaultRenderer;
7300         }
7301         return this.config[col].renderer;
7302     },
7303
7304     /**
7305      * Sets the rendering (formatting) function for a column.
7306      * @param {Number} col The column index
7307      * @param {Function} fn The function to use to process the cell's raw data
7308      * to return HTML markup for the grid view. The render function is called with
7309      * the following parameters:<ul>
7310      * <li>Data value.</li>
7311      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7312      * <li>css A CSS style string to apply to the table cell.</li>
7313      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7314      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7315      * <li>Row index</li>
7316      * <li>Column index</li>
7317      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7318      */
7319     setRenderer : function(col, fn){
7320         this.config[col].renderer = fn;
7321     },
7322
7323     /**
7324      * Returns the width for the specified column.
7325      * @param {Number} col The column index
7326      * @return {Number}
7327      */
7328     getColumnWidth : function(col){
7329         return this.config[col].width * 1 || this.defaultWidth;
7330     },
7331
7332     /**
7333      * Sets the width for a column.
7334      * @param {Number} col The column index
7335      * @param {Number} width The new width
7336      */
7337     setColumnWidth : function(col, width, suppressEvent){
7338         this.config[col].width = width;
7339         this.totalWidth = null;
7340         if(!suppressEvent){
7341              this.fireEvent("widthchange", this, col, width);
7342         }
7343     },
7344
7345     /**
7346      * Returns the total width of all columns.
7347      * @param {Boolean} includeHidden True to include hidden column widths
7348      * @return {Number}
7349      */
7350     getTotalWidth : function(includeHidden){
7351         if(!this.totalWidth){
7352             this.totalWidth = 0;
7353             for(var i = 0, len = this.config.length; i < len; i++){
7354                 if(includeHidden || !this.isHidden(i)){
7355                     this.totalWidth += this.getColumnWidth(i);
7356                 }
7357             }
7358         }
7359         return this.totalWidth;
7360     },
7361
7362     /**
7363      * Returns the header for the specified column.
7364      * @param {Number} col The column index
7365      * @return {String}
7366      */
7367     getColumnHeader : function(col){
7368         return this.config[col].header;
7369     },
7370
7371     /**
7372      * Sets the header for a column.
7373      * @param {Number} col The column index
7374      * @param {String} header The new header
7375      */
7376     setColumnHeader : function(col, header){
7377         this.config[col].header = header;
7378         this.fireEvent("headerchange", this, col, header);
7379     },
7380
7381     /**
7382      * Returns the tooltip for the specified column.
7383      * @param {Number} col The column index
7384      * @return {String}
7385      */
7386     getColumnTooltip : function(col){
7387             return this.config[col].tooltip;
7388     },
7389     /**
7390      * Sets the tooltip for a column.
7391      * @param {Number} col The column index
7392      * @param {String} tooltip The new tooltip
7393      */
7394     setColumnTooltip : function(col, tooltip){
7395             this.config[col].tooltip = tooltip;
7396     },
7397
7398     /**
7399      * Returns the dataIndex for the specified column.
7400      * @param {Number} col The column index
7401      * @return {Number}
7402      */
7403     getDataIndex : function(col){
7404         return this.config[col].dataIndex;
7405     },
7406
7407     /**
7408      * Sets the dataIndex for a column.
7409      * @param {Number} col The column index
7410      * @param {Number} dataIndex The new dataIndex
7411      */
7412     setDataIndex : function(col, dataIndex){
7413         this.config[col].dataIndex = dataIndex;
7414     },
7415
7416     
7417     
7418     /**
7419      * Returns true if the cell is editable.
7420      * @param {Number} colIndex The column index
7421      * @param {Number} rowIndex The row index - this is nto actually used..?
7422      * @return {Boolean}
7423      */
7424     isCellEditable : function(colIndex, rowIndex){
7425         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7426     },
7427
7428     /**
7429      * Returns the editor defined for the cell/column.
7430      * return false or null to disable editing.
7431      * @param {Number} colIndex The column index
7432      * @param {Number} rowIndex The row index
7433      * @return {Object}
7434      */
7435     getCellEditor : function(colIndex, rowIndex){
7436         return this.config[colIndex].editor;
7437     },
7438
7439     /**
7440      * Sets if a column is editable.
7441      * @param {Number} col The column index
7442      * @param {Boolean} editable True if the column is editable
7443      */
7444     setEditable : function(col, editable){
7445         this.config[col].editable = editable;
7446     },
7447
7448
7449     /**
7450      * Returns true if the column is hidden.
7451      * @param {Number} colIndex The column index
7452      * @return {Boolean}
7453      */
7454     isHidden : function(colIndex){
7455         return this.config[colIndex].hidden;
7456     },
7457
7458
7459     /**
7460      * Returns true if the column width cannot be changed
7461      */
7462     isFixed : function(colIndex){
7463         return this.config[colIndex].fixed;
7464     },
7465
7466     /**
7467      * Returns true if the column can be resized
7468      * @return {Boolean}
7469      */
7470     isResizable : function(colIndex){
7471         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7472     },
7473     /**
7474      * Sets if a column is hidden.
7475      * @param {Number} colIndex The column index
7476      * @param {Boolean} hidden True if the column is hidden
7477      */
7478     setHidden : function(colIndex, hidden){
7479         this.config[colIndex].hidden = hidden;
7480         this.totalWidth = null;
7481         this.fireEvent("hiddenchange", this, colIndex, hidden);
7482     },
7483
7484     /**
7485      * Sets the editor for a column.
7486      * @param {Number} col The column index
7487      * @param {Object} editor The editor object
7488      */
7489     setEditor : function(col, editor){
7490         this.config[col].editor = editor;
7491     }
7492 });
7493
7494 Roo.grid.ColumnModel.defaultRenderer = function(value)
7495 {
7496     if(typeof value == "object") {
7497         return value;
7498     }
7499         if(typeof value == "string" && value.length < 1){
7500             return "&#160;";
7501         }
7502     
7503         return String.format("{0}", value);
7504 };
7505
7506 // Alias for backwards compatibility
7507 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7508 /*
7509  * Based on:
7510  * Ext JS Library 1.1.1
7511  * Copyright(c) 2006-2007, Ext JS, LLC.
7512  *
7513  * Originally Released Under LGPL - original licence link has changed is not relivant.
7514  *
7515  * Fork - LGPL
7516  * <script type="text/javascript">
7517  */
7518  
7519 /**
7520  * @class Roo.LoadMask
7521  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7522  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7523  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7524  * element's UpdateManager load indicator and will be destroyed after the initial load.
7525  * @constructor
7526  * Create a new LoadMask
7527  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7528  * @param {Object} config The config object
7529  */
7530 Roo.LoadMask = function(el, config){
7531     this.el = Roo.get(el);
7532     Roo.apply(this, config);
7533     if(this.store){
7534         this.store.on('beforeload', this.onBeforeLoad, this);
7535         this.store.on('load', this.onLoad, this);
7536         this.store.on('loadexception', this.onLoadException, this);
7537         this.removeMask = false;
7538     }else{
7539         var um = this.el.getUpdateManager();
7540         um.showLoadIndicator = false; // disable the default indicator
7541         um.on('beforeupdate', this.onBeforeLoad, this);
7542         um.on('update', this.onLoad, this);
7543         um.on('failure', this.onLoad, this);
7544         this.removeMask = true;
7545     }
7546 };
7547
7548 Roo.LoadMask.prototype = {
7549     /**
7550      * @cfg {Boolean} removeMask
7551      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7552      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7553      */
7554     /**
7555      * @cfg {String} msg
7556      * The text to display in a centered loading message box (defaults to 'Loading...')
7557      */
7558     msg : 'Loading...',
7559     /**
7560      * @cfg {String} msgCls
7561      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7562      */
7563     msgCls : 'x-mask-loading',
7564
7565     /**
7566      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7567      * @type Boolean
7568      */
7569     disabled: false,
7570
7571     /**
7572      * Disables the mask to prevent it from being displayed
7573      */
7574     disable : function(){
7575        this.disabled = true;
7576     },
7577
7578     /**
7579      * Enables the mask so that it can be displayed
7580      */
7581     enable : function(){
7582         this.disabled = false;
7583     },
7584     
7585     onLoadException : function()
7586     {
7587         Roo.log(arguments);
7588         
7589         if (typeof(arguments[3]) != 'undefined') {
7590             Roo.MessageBox.alert("Error loading",arguments[3]);
7591         } 
7592         /*
7593         try {
7594             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7595                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7596             }   
7597         } catch(e) {
7598             
7599         }
7600         */
7601     
7602         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7603     },
7604     // private
7605     onLoad : function()
7606     {
7607         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7608     },
7609
7610     // private
7611     onBeforeLoad : function(){
7612         if(!this.disabled){
7613             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7614         }
7615     },
7616
7617     // private
7618     destroy : function(){
7619         if(this.store){
7620             this.store.un('beforeload', this.onBeforeLoad, this);
7621             this.store.un('load', this.onLoad, this);
7622             this.store.un('loadexception', this.onLoadException, this);
7623         }else{
7624             var um = this.el.getUpdateManager();
7625             um.un('beforeupdate', this.onBeforeLoad, this);
7626             um.un('update', this.onLoad, this);
7627             um.un('failure', this.onLoad, this);
7628         }
7629     }
7630 };/*
7631  * - LGPL
7632  *
7633  * table
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Table
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Table class
7641  * @cfg {String} cls table class
7642  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7643  * @cfg {String} bgcolor Specifies the background color for a table
7644  * @cfg {Number} border Specifies whether the table cells should have borders or not
7645  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7646  * @cfg {Number} cellspacing Specifies the space between cells
7647  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7648  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7649  * @cfg {String} sortable Specifies that the table should be sortable
7650  * @cfg {String} summary Specifies a summary of the content of a table
7651  * @cfg {Number} width Specifies the width of a table
7652  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7653  * 
7654  * @cfg {boolean} striped Should the rows be alternative striped
7655  * @cfg {boolean} bordered Add borders to the table
7656  * @cfg {boolean} hover Add hover highlighting
7657  * @cfg {boolean} condensed Format condensed
7658  * @cfg {boolean} responsive Format condensed
7659  * @cfg {Boolean} loadMask (true|false) default false
7660  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7661  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7662  * @cfg {Boolean} rowSelection (true|false) default false
7663  * @cfg {Boolean} cellSelection (true|false) default false
7664  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7665  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7666  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7667  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7668  
7669  * 
7670  * @constructor
7671  * Create a new Table
7672  * @param {Object} config The config object
7673  */
7674
7675 Roo.bootstrap.Table = function(config){
7676     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7677     
7678   
7679     
7680     // BC...
7681     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7682     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7683     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7684     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7685     
7686     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7687     if (this.sm) {
7688         this.sm.grid = this;
7689         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7690         this.sm = this.selModel;
7691         this.sm.xmodule = this.xmodule || false;
7692     }
7693     
7694     if (this.cm && typeof(this.cm.config) == 'undefined') {
7695         this.colModel = new Roo.grid.ColumnModel(this.cm);
7696         this.cm = this.colModel;
7697         this.cm.xmodule = this.xmodule || false;
7698     }
7699     if (this.store) {
7700         this.store= Roo.factory(this.store, Roo.data);
7701         this.ds = this.store;
7702         this.ds.xmodule = this.xmodule || false;
7703          
7704     }
7705     if (this.footer && this.store) {
7706         this.footer.dataSource = this.ds;
7707         this.footer = Roo.factory(this.footer);
7708     }
7709     
7710     /** @private */
7711     this.addEvents({
7712         /**
7713          * @event cellclick
7714          * Fires when a cell is clicked
7715          * @param {Roo.bootstrap.Table} this
7716          * @param {Roo.Element} el
7717          * @param {Number} rowIndex
7718          * @param {Number} columnIndex
7719          * @param {Roo.EventObject} e
7720          */
7721         "cellclick" : true,
7722         /**
7723          * @event celldblclick
7724          * Fires when a cell is double clicked
7725          * @param {Roo.bootstrap.Table} this
7726          * @param {Roo.Element} el
7727          * @param {Number} rowIndex
7728          * @param {Number} columnIndex
7729          * @param {Roo.EventObject} e
7730          */
7731         "celldblclick" : true,
7732         /**
7733          * @event rowclick
7734          * Fires when a row is clicked
7735          * @param {Roo.bootstrap.Table} this
7736          * @param {Roo.Element} el
7737          * @param {Number} rowIndex
7738          * @param {Roo.EventObject} e
7739          */
7740         "rowclick" : true,
7741         /**
7742          * @event rowdblclick
7743          * Fires when a row is double clicked
7744          * @param {Roo.bootstrap.Table} this
7745          * @param {Roo.Element} el
7746          * @param {Number} rowIndex
7747          * @param {Roo.EventObject} e
7748          */
7749         "rowdblclick" : true,
7750         /**
7751          * @event mouseover
7752          * Fires when a mouseover occur
7753          * @param {Roo.bootstrap.Table} this
7754          * @param {Roo.Element} el
7755          * @param {Number} rowIndex
7756          * @param {Number} columnIndex
7757          * @param {Roo.EventObject} e
7758          */
7759         "mouseover" : true,
7760         /**
7761          * @event mouseout
7762          * Fires when a mouseout occur
7763          * @param {Roo.bootstrap.Table} this
7764          * @param {Roo.Element} el
7765          * @param {Number} rowIndex
7766          * @param {Number} columnIndex
7767          * @param {Roo.EventObject} e
7768          */
7769         "mouseout" : true,
7770         /**
7771          * @event rowclass
7772          * Fires when a row is rendered, so you can change add a style to it.
7773          * @param {Roo.bootstrap.Table} this
7774          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7775          */
7776         'rowclass' : true,
7777           /**
7778          * @event rowsrendered
7779          * Fires when all the  rows have been rendered
7780          * @param {Roo.bootstrap.Table} this
7781          */
7782         'rowsrendered' : true,
7783         /**
7784          * @event contextmenu
7785          * The raw contextmenu event for the entire grid.
7786          * @param {Roo.EventObject} e
7787          */
7788         "contextmenu" : true,
7789         /**
7790          * @event rowcontextmenu
7791          * Fires when a row is right clicked
7792          * @param {Roo.bootstrap.Table} this
7793          * @param {Number} rowIndex
7794          * @param {Roo.EventObject} e
7795          */
7796         "rowcontextmenu" : true,
7797         /**
7798          * @event cellcontextmenu
7799          * Fires when a cell is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Number} cellIndex
7803          * @param {Roo.EventObject} e
7804          */
7805          "cellcontextmenu" : true,
7806          /**
7807          * @event headercontextmenu
7808          * Fires when a header is right clicked
7809          * @param {Roo.bootstrap.Table} this
7810          * @param {Number} columnIndex
7811          * @param {Roo.EventObject} e
7812          */
7813         "headercontextmenu" : true
7814     });
7815 };
7816
7817 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7818     
7819     cls: false,
7820     align: false,
7821     bgcolor: false,
7822     border: false,
7823     cellpadding: false,
7824     cellspacing: false,
7825     frame: false,
7826     rules: false,
7827     sortable: false,
7828     summary: false,
7829     width: false,
7830     striped : false,
7831     scrollBody : false,
7832     bordered: false,
7833     hover:  false,
7834     condensed : false,
7835     responsive : false,
7836     sm : false,
7837     cm : false,
7838     store : false,
7839     loadMask : false,
7840     footerShow : true,
7841     headerShow : true,
7842   
7843     rowSelection : false,
7844     cellSelection : false,
7845     layout : false,
7846     
7847     // Roo.Element - the tbody
7848     mainBody: false,
7849     // Roo.Element - thead element
7850     mainHead: false,
7851     
7852     container: false, // used by gridpanel...
7853     
7854     lazyLoad : false,
7855     
7856     CSS : Roo.util.CSS,
7857     
7858     auto_hide_footer : false,
7859     
7860     getAutoCreate : function()
7861     {
7862         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7863         
7864         cfg = {
7865             tag: 'table',
7866             cls : 'table',
7867             cn : []
7868         };
7869         if (this.scrollBody) {
7870             cfg.cls += ' table-body-fixed';
7871         }    
7872         if (this.striped) {
7873             cfg.cls += ' table-striped';
7874         }
7875         
7876         if (this.hover) {
7877             cfg.cls += ' table-hover';
7878         }
7879         if (this.bordered) {
7880             cfg.cls += ' table-bordered';
7881         }
7882         if (this.condensed) {
7883             cfg.cls += ' table-condensed';
7884         }
7885         if (this.responsive) {
7886             cfg.cls += ' table-responsive';
7887         }
7888         
7889         if (this.cls) {
7890             cfg.cls+=  ' ' +this.cls;
7891         }
7892         
7893         // this lot should be simplifed...
7894         var _t = this;
7895         var cp = [
7896             'align',
7897             'bgcolor',
7898             'border',
7899             'cellpadding',
7900             'cellspacing',
7901             'frame',
7902             'rules',
7903             'sortable',
7904             'summary',
7905             'width'
7906         ].forEach(function(k) {
7907             if (_t[k]) {
7908                 cfg[k] = _t[k];
7909             }
7910         });
7911         
7912         
7913         if (this.layout) {
7914             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7915         }
7916         
7917         if(this.store || this.cm){
7918             if(this.headerShow){
7919                 cfg.cn.push(this.renderHeader());
7920             }
7921             
7922             cfg.cn.push(this.renderBody());
7923             
7924             if(this.footerShow){
7925                 cfg.cn.push(this.renderFooter());
7926             }
7927             // where does this come from?
7928             //cfg.cls+=  ' TableGrid';
7929         }
7930         
7931         return { cn : [ cfg ] };
7932     },
7933     
7934     initEvents : function()
7935     {   
7936         if(!this.store || !this.cm){
7937             return;
7938         }
7939         if (this.selModel) {
7940             this.selModel.initEvents();
7941         }
7942         
7943         
7944         //Roo.log('initEvents with ds!!!!');
7945         
7946         this.mainBody = this.el.select('tbody', true).first();
7947         this.mainHead = this.el.select('thead', true).first();
7948         this.mainFoot = this.el.select('tfoot', true).first();
7949         
7950         
7951         
7952         var _this = this;
7953         
7954         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7955             e.on('click', _this.sort, _this);
7956         });
7957         
7958         this.mainBody.on("click", this.onClick, this);
7959         this.mainBody.on("dblclick", this.onDblClick, this);
7960         
7961         // why is this done????? = it breaks dialogs??
7962         //this.parent().el.setStyle('position', 'relative');
7963         
7964         
7965         if (this.footer) {
7966             this.footer.parentId = this.id;
7967             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7968             
7969             if(this.lazyLoad){
7970                 this.el.select('tfoot tr td').first().addClass('hide');
7971             }
7972         } 
7973         
7974         if(this.loadMask) {
7975             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7976         }
7977         
7978         this.store.on('load', this.onLoad, this);
7979         this.store.on('beforeload', this.onBeforeLoad, this);
7980         this.store.on('update', this.onUpdate, this);
7981         this.store.on('add', this.onAdd, this);
7982         this.store.on("clear", this.clear, this);
7983         
7984         this.el.on("contextmenu", this.onContextMenu, this);
7985         
7986         this.mainBody.on('scroll', this.onBodyScroll, this);
7987         
7988         this.cm.on("headerchange", this.onHeaderChange, this);
7989         
7990         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7991         
7992     },
7993     
7994     onContextMenu : function(e, t)
7995     {
7996         this.processEvent("contextmenu", e);
7997     },
7998     
7999     processEvent : function(name, e)
8000     {
8001         if (name != 'touchstart' ) {
8002             this.fireEvent(name, e);    
8003         }
8004         
8005         var t = e.getTarget();
8006         
8007         var cell = Roo.get(t);
8008         
8009         if(!cell){
8010             return;
8011         }
8012         
8013         if(cell.findParent('tfoot', false, true)){
8014             return;
8015         }
8016         
8017         if(cell.findParent('thead', false, true)){
8018             
8019             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8020                 cell = Roo.get(t).findParent('th', false, true);
8021                 if (!cell) {
8022                     Roo.log("failed to find th in thead?");
8023                     Roo.log(e.getTarget());
8024                     return;
8025                 }
8026             }
8027             
8028             var cellIndex = cell.dom.cellIndex;
8029             
8030             var ename = name == 'touchstart' ? 'click' : name;
8031             this.fireEvent("header" + ename, this, cellIndex, e);
8032             
8033             return;
8034         }
8035         
8036         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8037             cell = Roo.get(t).findParent('td', false, true);
8038             if (!cell) {
8039                 Roo.log("failed to find th in tbody?");
8040                 Roo.log(e.getTarget());
8041                 return;
8042             }
8043         }
8044         
8045         var row = cell.findParent('tr', false, true);
8046         var cellIndex = cell.dom.cellIndex;
8047         var rowIndex = row.dom.rowIndex - 1;
8048         
8049         if(row !== false){
8050             
8051             this.fireEvent("row" + name, this, rowIndex, e);
8052             
8053             if(cell !== false){
8054             
8055                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8056             }
8057         }
8058         
8059     },
8060     
8061     onMouseover : function(e, el)
8062     {
8063         var cell = Roo.get(el);
8064         
8065         if(!cell){
8066             return;
8067         }
8068         
8069         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8070             cell = cell.findParent('td', false, true);
8071         }
8072         
8073         var row = cell.findParent('tr', false, true);
8074         var cellIndex = cell.dom.cellIndex;
8075         var rowIndex = row.dom.rowIndex - 1; // start from 0
8076         
8077         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8078         
8079     },
8080     
8081     onMouseout : function(e, el)
8082     {
8083         var cell = Roo.get(el);
8084         
8085         if(!cell){
8086             return;
8087         }
8088         
8089         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8090             cell = cell.findParent('td', false, true);
8091         }
8092         
8093         var row = cell.findParent('tr', false, true);
8094         var cellIndex = cell.dom.cellIndex;
8095         var rowIndex = row.dom.rowIndex - 1; // start from 0
8096         
8097         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8098         
8099     },
8100     
8101     onClick : function(e, el)
8102     {
8103         var cell = Roo.get(el);
8104         
8105         if(!cell || (!this.cellSelection && !this.rowSelection)){
8106             return;
8107         }
8108         
8109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8110             cell = cell.findParent('td', false, true);
8111         }
8112         
8113         if(!cell || typeof(cell) == 'undefined'){
8114             return;
8115         }
8116         
8117         var row = cell.findParent('tr', false, true);
8118         
8119         if(!row || typeof(row) == 'undefined'){
8120             return;
8121         }
8122         
8123         var cellIndex = cell.dom.cellIndex;
8124         var rowIndex = this.getRowIndex(row);
8125         
8126         // why??? - should these not be based on SelectionModel?
8127         if(this.cellSelection){
8128             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8129         }
8130         
8131         if(this.rowSelection){
8132             this.fireEvent('rowclick', this, row, rowIndex, e);
8133         }
8134         
8135         
8136     },
8137         
8138     onDblClick : function(e,el)
8139     {
8140         var cell = Roo.get(el);
8141         
8142         if(!cell || (!this.cellSelection && !this.rowSelection)){
8143             return;
8144         }
8145         
8146         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8147             cell = cell.findParent('td', false, true);
8148         }
8149         
8150         if(!cell || typeof(cell) == 'undefined'){
8151             return;
8152         }
8153         
8154         var row = cell.findParent('tr', false, true);
8155         
8156         if(!row || typeof(row) == 'undefined'){
8157             return;
8158         }
8159         
8160         var cellIndex = cell.dom.cellIndex;
8161         var rowIndex = this.getRowIndex(row);
8162         
8163         if(this.cellSelection){
8164             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8165         }
8166         
8167         if(this.rowSelection){
8168             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8169         }
8170     },
8171     
8172     sort : function(e,el)
8173     {
8174         var col = Roo.get(el);
8175         
8176         if(!col.hasClass('sortable')){
8177             return;
8178         }
8179         
8180         var sort = col.attr('sort');
8181         var dir = 'ASC';
8182         
8183         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8184             dir = 'DESC';
8185         }
8186         
8187         this.store.sortInfo = {field : sort, direction : dir};
8188         
8189         if (this.footer) {
8190             Roo.log("calling footer first");
8191             this.footer.onClick('first');
8192         } else {
8193         
8194             this.store.load({ params : { start : 0 } });
8195         }
8196     },
8197     
8198     renderHeader : function()
8199     {
8200         var header = {
8201             tag: 'thead',
8202             cn : []
8203         };
8204         
8205         var cm = this.cm;
8206         this.totalWidth = 0;
8207         
8208         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8209             
8210             var config = cm.config[i];
8211             
8212             var c = {
8213                 tag: 'th',
8214                 cls : 'x-hcol-' + i,
8215                 style : '',
8216                 html: cm.getColumnHeader(i)
8217             };
8218             
8219             var hh = '';
8220             
8221             if(typeof(config.sortable) != 'undefined' && config.sortable){
8222                 c.cls = 'sortable';
8223                 c.html = '<i class="glyphicon"></i>' + c.html;
8224             }
8225             
8226             // could use BS4 hidden-..-down 
8227             
8228             if(typeof(config.lgHeader) != 'undefined'){
8229                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8230             }
8231             
8232             if(typeof(config.mdHeader) != 'undefined'){
8233                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8234             }
8235             
8236             if(typeof(config.smHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.xsHeader) != 'undefined'){
8241                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8242             }
8243             
8244             if(hh.length){
8245                 c.html = hh;
8246             }
8247             
8248             if(typeof(config.tooltip) != 'undefined'){
8249                 c.tooltip = config.tooltip;
8250             }
8251             
8252             if(typeof(config.colspan) != 'undefined'){
8253                 c.colspan = config.colspan;
8254             }
8255             
8256             if(typeof(config.hidden) != 'undefined' && config.hidden){
8257                 c.style += ' display:none;';
8258             }
8259             
8260             if(typeof(config.dataIndex) != 'undefined'){
8261                 c.sort = config.dataIndex;
8262             }
8263             
8264            
8265             
8266             if(typeof(config.align) != 'undefined' && config.align.length){
8267                 c.style += ' text-align:' + config.align + ';';
8268             }
8269             
8270             if(typeof(config.width) != 'undefined'){
8271                 c.style += ' width:' + config.width + 'px;';
8272                 this.totalWidth += config.width;
8273             } else {
8274                 this.totalWidth += 100; // assume minimum of 100 per column?
8275             }
8276             
8277             if(typeof(config.cls) != 'undefined'){
8278                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8279             }
8280             
8281             ['xs','sm','md','lg'].map(function(size){
8282                 
8283                 if(typeof(config[size]) == 'undefined'){
8284                     return;
8285                 }
8286                  
8287                 if (!config[size]) { // 0 = hidden
8288                     // BS 4 '0' is treated as hide that column and below.
8289                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8290                     return;
8291                 }
8292                 
8293                 c.cls += ' col-' + size + '-' + config[size] + (
8294                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8295                 );
8296                 
8297                 
8298             });
8299             
8300             header.cn.push(c)
8301         }
8302         
8303         return header;
8304     },
8305     
8306     renderBody : function()
8307     {
8308         var body = {
8309             tag: 'tbody',
8310             cn : [
8311                 {
8312                     tag: 'tr',
8313                     cn : [
8314                         {
8315                             tag : 'td',
8316                             colspan :  this.cm.getColumnCount()
8317                         }
8318                     ]
8319                 }
8320             ]
8321         };
8322         
8323         return body;
8324     },
8325     
8326     renderFooter : function()
8327     {
8328         var footer = {
8329             tag: 'tfoot',
8330             cn : [
8331                 {
8332                     tag: 'tr',
8333                     cn : [
8334                         {
8335                             tag : 'td',
8336                             colspan :  this.cm.getColumnCount()
8337                         }
8338                     ]
8339                 }
8340             ]
8341         };
8342         
8343         return footer;
8344     },
8345     
8346     
8347     
8348     onLoad : function()
8349     {
8350 //        Roo.log('ds onload');
8351         this.clear();
8352         
8353         var _this = this;
8354         var cm = this.cm;
8355         var ds = this.store;
8356         
8357         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8358             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8359             if (_this.store.sortInfo) {
8360                     
8361                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8362                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8363                 }
8364                 
8365                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8366                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8367                 }
8368             }
8369         });
8370         
8371         var tbody =  this.mainBody;
8372               
8373         if(ds.getCount() > 0){
8374             ds.data.each(function(d,rowIndex){
8375                 var row =  this.renderRow(cm, ds, rowIndex);
8376                 
8377                 tbody.createChild(row);
8378                 
8379                 var _this = this;
8380                 
8381                 if(row.cellObjects.length){
8382                     Roo.each(row.cellObjects, function(r){
8383                         _this.renderCellObject(r);
8384                     })
8385                 }
8386                 
8387             }, this);
8388         }
8389         
8390         var tfoot = this.el.select('tfoot', true).first();
8391         
8392         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8393             
8394             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8395             
8396             var total = this.ds.getTotalCount();
8397             
8398             if(this.footer.pageSize < total){
8399                 this.mainFoot.show();
8400             }
8401         }
8402         
8403         Roo.each(this.el.select('tbody td', true).elements, function(e){
8404             e.on('mouseover', _this.onMouseover, _this);
8405         });
8406         
8407         Roo.each(this.el.select('tbody td', true).elements, function(e){
8408             e.on('mouseout', _this.onMouseout, _this);
8409         });
8410         this.fireEvent('rowsrendered', this);
8411         
8412         this.autoSize();
8413     },
8414     
8415     
8416     onUpdate : function(ds,record)
8417     {
8418         this.refreshRow(record);
8419         this.autoSize();
8420     },
8421     
8422     onRemove : function(ds, record, index, isUpdate){
8423         if(isUpdate !== true){
8424             this.fireEvent("beforerowremoved", this, index, record);
8425         }
8426         var bt = this.mainBody.dom;
8427         
8428         var rows = this.el.select('tbody > tr', true).elements;
8429         
8430         if(typeof(rows[index]) != 'undefined'){
8431             bt.removeChild(rows[index].dom);
8432         }
8433         
8434 //        if(bt.rows[index]){
8435 //            bt.removeChild(bt.rows[index]);
8436 //        }
8437         
8438         if(isUpdate !== true){
8439             //this.stripeRows(index);
8440             //this.syncRowHeights(index, index);
8441             //this.layout();
8442             this.fireEvent("rowremoved", this, index, record);
8443         }
8444     },
8445     
8446     onAdd : function(ds, records, rowIndex)
8447     {
8448         //Roo.log('on Add called');
8449         // - note this does not handle multiple adding very well..
8450         var bt = this.mainBody.dom;
8451         for (var i =0 ; i < records.length;i++) {
8452             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8453             //Roo.log(records[i]);
8454             //Roo.log(this.store.getAt(rowIndex+i));
8455             this.insertRow(this.store, rowIndex + i, false);
8456             return;
8457         }
8458         
8459     },
8460     
8461     
8462     refreshRow : function(record){
8463         var ds = this.store, index;
8464         if(typeof record == 'number'){
8465             index = record;
8466             record = ds.getAt(index);
8467         }else{
8468             index = ds.indexOf(record);
8469             if (index < 0) {
8470                 return; // should not happen - but seems to 
8471             }
8472         }
8473         this.insertRow(ds, index, true);
8474         this.autoSize();
8475         this.onRemove(ds, record, index+1, true);
8476         this.autoSize();
8477         //this.syncRowHeights(index, index);
8478         //this.layout();
8479         this.fireEvent("rowupdated", this, index, record);
8480     },
8481     
8482     insertRow : function(dm, rowIndex, isUpdate){
8483         
8484         if(!isUpdate){
8485             this.fireEvent("beforerowsinserted", this, rowIndex);
8486         }
8487             //var s = this.getScrollState();
8488         var row = this.renderRow(this.cm, this.store, rowIndex);
8489         // insert before rowIndex..
8490         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8491         
8492         var _this = this;
8493                 
8494         if(row.cellObjects.length){
8495             Roo.each(row.cellObjects, function(r){
8496                 _this.renderCellObject(r);
8497             })
8498         }
8499             
8500         if(!isUpdate){
8501             this.fireEvent("rowsinserted", this, rowIndex);
8502             //this.syncRowHeights(firstRow, lastRow);
8503             //this.stripeRows(firstRow);
8504             //this.layout();
8505         }
8506         
8507     },
8508     
8509     
8510     getRowDom : function(rowIndex)
8511     {
8512         var rows = this.el.select('tbody > tr', true).elements;
8513         
8514         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8515         
8516     },
8517     // returns the object tree for a tr..
8518   
8519     
8520     renderRow : function(cm, ds, rowIndex) 
8521     {
8522         var d = ds.getAt(rowIndex);
8523         
8524         var row = {
8525             tag : 'tr',
8526             cls : 'x-row-' + rowIndex,
8527             cn : []
8528         };
8529             
8530         var cellObjects = [];
8531         
8532         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8533             var config = cm.config[i];
8534             
8535             var renderer = cm.getRenderer(i);
8536             var value = '';
8537             var id = false;
8538             
8539             if(typeof(renderer) !== 'undefined'){
8540                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8541             }
8542             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8543             // and are rendered into the cells after the row is rendered - using the id for the element.
8544             
8545             if(typeof(value) === 'object'){
8546                 id = Roo.id();
8547                 cellObjects.push({
8548                     container : id,
8549                     cfg : value 
8550                 })
8551             }
8552             
8553             var rowcfg = {
8554                 record: d,
8555                 rowIndex : rowIndex,
8556                 colIndex : i,
8557                 rowClass : ''
8558             };
8559
8560             this.fireEvent('rowclass', this, rowcfg);
8561             
8562             var td = {
8563                 tag: 'td',
8564                 cls : rowcfg.rowClass + ' x-col-' + i,
8565                 style: '',
8566                 html: (typeof(value) === 'object') ? '' : value
8567             };
8568             
8569             if (id) {
8570                 td.id = id;
8571             }
8572             
8573             if(typeof(config.colspan) != 'undefined'){
8574                 td.colspan = config.colspan;
8575             }
8576             
8577             if(typeof(config.hidden) != 'undefined' && config.hidden){
8578                 td.style += ' display:none;';
8579             }
8580             
8581             if(typeof(config.align) != 'undefined' && config.align.length){
8582                 td.style += ' text-align:' + config.align + ';';
8583             }
8584             if(typeof(config.valign) != 'undefined' && config.valign.length){
8585                 td.style += ' vertical-align:' + config.valign + ';';
8586             }
8587             
8588             if(typeof(config.width) != 'undefined'){
8589                 td.style += ' width:' +  config.width + 'px;';
8590             }
8591             
8592             if(typeof(config.cursor) != 'undefined'){
8593                 td.style += ' cursor:' +  config.cursor + ';';
8594             }
8595             
8596             if(typeof(config.cls) != 'undefined'){
8597                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8598             }
8599             
8600             ['xs','sm','md','lg'].map(function(size){
8601                 
8602                 if(typeof(config[size]) == 'undefined'){
8603                     return;
8604                 }
8605                 
8606                 
8607                   
8608                 if (!config[size]) { // 0 = hidden
8609                     // BS 4 '0' is treated as hide that column and below.
8610                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8611                     return;
8612                 }
8613                 
8614                 td.cls += ' col-' + size + '-' + config[size] + (
8615                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8616                 );
8617                  
8618
8619             });
8620             
8621             row.cn.push(td);
8622            
8623         }
8624         
8625         row.cellObjects = cellObjects;
8626         
8627         return row;
8628           
8629     },
8630     
8631     
8632     
8633     onBeforeLoad : function()
8634     {
8635         
8636     },
8637      /**
8638      * Remove all rows
8639      */
8640     clear : function()
8641     {
8642         this.el.select('tbody', true).first().dom.innerHTML = '';
8643     },
8644     /**
8645      * Show or hide a row.
8646      * @param {Number} rowIndex to show or hide
8647      * @param {Boolean} state hide
8648      */
8649     setRowVisibility : function(rowIndex, state)
8650     {
8651         var bt = this.mainBody.dom;
8652         
8653         var rows = this.el.select('tbody > tr', true).elements;
8654         
8655         if(typeof(rows[rowIndex]) == 'undefined'){
8656             return;
8657         }
8658         rows[rowIndex].dom.style.display = state ? '' : 'none';
8659     },
8660     
8661     
8662     getSelectionModel : function(){
8663         if(!this.selModel){
8664             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8665         }
8666         return this.selModel;
8667     },
8668     /*
8669      * Render the Roo.bootstrap object from renderder
8670      */
8671     renderCellObject : function(r)
8672     {
8673         var _this = this;
8674         
8675         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8676         
8677         var t = r.cfg.render(r.container);
8678         
8679         if(r.cfg.cn){
8680             Roo.each(r.cfg.cn, function(c){
8681                 var child = {
8682                     container: t.getChildContainer(),
8683                     cfg: c
8684                 };
8685                 _this.renderCellObject(child);
8686             })
8687         }
8688     },
8689     
8690     getRowIndex : function(row)
8691     {
8692         var rowIndex = -1;
8693         
8694         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8695             if(el != row){
8696                 return;
8697             }
8698             
8699             rowIndex = index;
8700         });
8701         
8702         return rowIndex;
8703     },
8704      /**
8705      * Returns the grid's underlying element = used by panel.Grid
8706      * @return {Element} The element
8707      */
8708     getGridEl : function(){
8709         return this.el;
8710     },
8711      /**
8712      * Forces a resize - used by panel.Grid
8713      * @return {Element} The element
8714      */
8715     autoSize : function()
8716     {
8717         //var ctr = Roo.get(this.container.dom.parentElement);
8718         var ctr = Roo.get(this.el.dom);
8719         
8720         var thd = this.getGridEl().select('thead',true).first();
8721         var tbd = this.getGridEl().select('tbody', true).first();
8722         var tfd = this.getGridEl().select('tfoot', true).first();
8723         
8724         var cw = ctr.getWidth();
8725         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8726         
8727         if (tbd) {
8728             
8729             tbd.setWidth(ctr.getWidth());
8730             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8731             // this needs fixing for various usage - currently only hydra job advers I think..
8732             //tdb.setHeight(
8733             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8734             //); 
8735             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8736             cw -= barsize;
8737         }
8738         cw = Math.max(cw, this.totalWidth);
8739         this.getGridEl().select('tbody tr',true).setWidth(cw);
8740         
8741         // resize 'expandable coloumn?
8742         
8743         return; // we doe not have a view in this design..
8744         
8745     },
8746     onBodyScroll: function()
8747     {
8748         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8749         if(this.mainHead){
8750             this.mainHead.setStyle({
8751                 'position' : 'relative',
8752                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8753             });
8754         }
8755         
8756         if(this.lazyLoad){
8757             
8758             var scrollHeight = this.mainBody.dom.scrollHeight;
8759             
8760             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8761             
8762             var height = this.mainBody.getHeight();
8763             
8764             if(scrollHeight - height == scrollTop) {
8765                 
8766                 var total = this.ds.getTotalCount();
8767                 
8768                 if(this.footer.cursor + this.footer.pageSize < total){
8769                     
8770                     this.footer.ds.load({
8771                         params : {
8772                             start : this.footer.cursor + this.footer.pageSize,
8773                             limit : this.footer.pageSize
8774                         },
8775                         add : true
8776                     });
8777                 }
8778             }
8779             
8780         }
8781     },
8782     
8783     onHeaderChange : function()
8784     {
8785         var header = this.renderHeader();
8786         var table = this.el.select('table', true).first();
8787         
8788         this.mainHead.remove();
8789         this.mainHead = table.createChild(header, this.mainBody, false);
8790     },
8791     
8792     onHiddenChange : function(colModel, colIndex, hidden)
8793     {
8794         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8795         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8796         
8797         this.CSS.updateRule(thSelector, "display", "");
8798         this.CSS.updateRule(tdSelector, "display", "");
8799         
8800         if(hidden){
8801             this.CSS.updateRule(thSelector, "display", "none");
8802             this.CSS.updateRule(tdSelector, "display", "none");
8803         }
8804         
8805         this.onHeaderChange();
8806         this.onLoad();
8807     },
8808     
8809     setColumnWidth: function(col_index, width)
8810     {
8811         // width = "md-2 xs-2..."
8812         if(!this.colModel.config[col_index]) {
8813             return;
8814         }
8815         
8816         var w = width.split(" ");
8817         
8818         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8819         
8820         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8821         
8822         
8823         for(var j = 0; j < w.length; j++) {
8824             
8825             if(!w[j]) {
8826                 continue;
8827             }
8828             
8829             var size_cls = w[j].split("-");
8830             
8831             if(!Number.isInteger(size_cls[1] * 1)) {
8832                 continue;
8833             }
8834             
8835             if(!this.colModel.config[col_index][size_cls[0]]) {
8836                 continue;
8837             }
8838             
8839             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8840                 continue;
8841             }
8842             
8843             h_row[0].classList.replace(
8844                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8845                 "col-"+size_cls[0]+"-"+size_cls[1]
8846             );
8847             
8848             for(var i = 0; i < rows.length; i++) {
8849                 
8850                 var size_cls = w[j].split("-");
8851                 
8852                 if(!Number.isInteger(size_cls[1] * 1)) {
8853                     continue;
8854                 }
8855                 
8856                 if(!this.colModel.config[col_index][size_cls[0]]) {
8857                     continue;
8858                 }
8859                 
8860                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8861                     continue;
8862                 }
8863                 
8864                 rows[i].classList.replace(
8865                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8866                     "col-"+size_cls[0]+"-"+size_cls[1]
8867                 );
8868             }
8869             
8870             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8871         }
8872     }
8873 });
8874
8875  
8876
8877  /*
8878  * - LGPL
8879  *
8880  * table cell
8881  * 
8882  */
8883
8884 /**
8885  * @class Roo.bootstrap.TableCell
8886  * @extends Roo.bootstrap.Component
8887  * Bootstrap TableCell class
8888  * @cfg {String} html cell contain text
8889  * @cfg {String} cls cell class
8890  * @cfg {String} tag cell tag (td|th) default td
8891  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8892  * @cfg {String} align Aligns the content in a cell
8893  * @cfg {String} axis Categorizes cells
8894  * @cfg {String} bgcolor Specifies the background color of a cell
8895  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8896  * @cfg {Number} colspan Specifies the number of columns a cell should span
8897  * @cfg {String} headers Specifies one or more header cells a cell is related to
8898  * @cfg {Number} height Sets the height of a cell
8899  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8900  * @cfg {Number} rowspan Sets the number of rows a cell should span
8901  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8902  * @cfg {String} valign Vertical aligns the content in a cell
8903  * @cfg {Number} width Specifies the width of a cell
8904  * 
8905  * @constructor
8906  * Create a new TableCell
8907  * @param {Object} config The config object
8908  */
8909
8910 Roo.bootstrap.TableCell = function(config){
8911     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8912 };
8913
8914 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8915     
8916     html: false,
8917     cls: false,
8918     tag: false,
8919     abbr: false,
8920     align: false,
8921     axis: false,
8922     bgcolor: false,
8923     charoff: false,
8924     colspan: false,
8925     headers: false,
8926     height: false,
8927     nowrap: false,
8928     rowspan: false,
8929     scope: false,
8930     valign: false,
8931     width: false,
8932     
8933     
8934     getAutoCreate : function(){
8935         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8936         
8937         cfg = {
8938             tag: 'td'
8939         };
8940         
8941         if(this.tag){
8942             cfg.tag = this.tag;
8943         }
8944         
8945         if (this.html) {
8946             cfg.html=this.html
8947         }
8948         if (this.cls) {
8949             cfg.cls=this.cls
8950         }
8951         if (this.abbr) {
8952             cfg.abbr=this.abbr
8953         }
8954         if (this.align) {
8955             cfg.align=this.align
8956         }
8957         if (this.axis) {
8958             cfg.axis=this.axis
8959         }
8960         if (this.bgcolor) {
8961             cfg.bgcolor=this.bgcolor
8962         }
8963         if (this.charoff) {
8964             cfg.charoff=this.charoff
8965         }
8966         if (this.colspan) {
8967             cfg.colspan=this.colspan
8968         }
8969         if (this.headers) {
8970             cfg.headers=this.headers
8971         }
8972         if (this.height) {
8973             cfg.height=this.height
8974         }
8975         if (this.nowrap) {
8976             cfg.nowrap=this.nowrap
8977         }
8978         if (this.rowspan) {
8979             cfg.rowspan=this.rowspan
8980         }
8981         if (this.scope) {
8982             cfg.scope=this.scope
8983         }
8984         if (this.valign) {
8985             cfg.valign=this.valign
8986         }
8987         if (this.width) {
8988             cfg.width=this.width
8989         }
8990         
8991         
8992         return cfg;
8993     }
8994    
8995 });
8996
8997  
8998
8999  /*
9000  * - LGPL
9001  *
9002  * table row
9003  * 
9004  */
9005
9006 /**
9007  * @class Roo.bootstrap.TableRow
9008  * @extends Roo.bootstrap.Component
9009  * Bootstrap TableRow class
9010  * @cfg {String} cls row class
9011  * @cfg {String} align Aligns the content in a table row
9012  * @cfg {String} bgcolor Specifies a background color for a table row
9013  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9014  * @cfg {String} valign Vertical aligns the content in a table row
9015  * 
9016  * @constructor
9017  * Create a new TableRow
9018  * @param {Object} config The config object
9019  */
9020
9021 Roo.bootstrap.TableRow = function(config){
9022     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9023 };
9024
9025 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9026     
9027     cls: false,
9028     align: false,
9029     bgcolor: false,
9030     charoff: false,
9031     valign: false,
9032     
9033     getAutoCreate : function(){
9034         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9035         
9036         cfg = {
9037             tag: 'tr'
9038         };
9039             
9040         if(this.cls){
9041             cfg.cls = this.cls;
9042         }
9043         if(this.align){
9044             cfg.align = this.align;
9045         }
9046         if(this.bgcolor){
9047             cfg.bgcolor = this.bgcolor;
9048         }
9049         if(this.charoff){
9050             cfg.charoff = this.charoff;
9051         }
9052         if(this.valign){
9053             cfg.valign = this.valign;
9054         }
9055         
9056         return cfg;
9057     }
9058    
9059 });
9060
9061  
9062
9063  /*
9064  * - LGPL
9065  *
9066  * table body
9067  * 
9068  */
9069
9070 /**
9071  * @class Roo.bootstrap.TableBody
9072  * @extends Roo.bootstrap.Component
9073  * Bootstrap TableBody class
9074  * @cfg {String} cls element class
9075  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9076  * @cfg {String} align Aligns the content inside the element
9077  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9078  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9079  * 
9080  * @constructor
9081  * Create a new TableBody
9082  * @param {Object} config The config object
9083  */
9084
9085 Roo.bootstrap.TableBody = function(config){
9086     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9087 };
9088
9089 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9090     
9091     cls: false,
9092     tag: false,
9093     align: false,
9094     charoff: false,
9095     valign: false,
9096     
9097     getAutoCreate : function(){
9098         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9099         
9100         cfg = {
9101             tag: 'tbody'
9102         };
9103             
9104         if (this.cls) {
9105             cfg.cls=this.cls
9106         }
9107         if(this.tag){
9108             cfg.tag = this.tag;
9109         }
9110         
9111         if(this.align){
9112             cfg.align = this.align;
9113         }
9114         if(this.charoff){
9115             cfg.charoff = this.charoff;
9116         }
9117         if(this.valign){
9118             cfg.valign = this.valign;
9119         }
9120         
9121         return cfg;
9122     }
9123     
9124     
9125 //    initEvents : function()
9126 //    {
9127 //        
9128 //        if(!this.store){
9129 //            return;
9130 //        }
9131 //        
9132 //        this.store = Roo.factory(this.store, Roo.data);
9133 //        this.store.on('load', this.onLoad, this);
9134 //        
9135 //        this.store.load();
9136 //        
9137 //    },
9138 //    
9139 //    onLoad: function () 
9140 //    {   
9141 //        this.fireEvent('load', this);
9142 //    }
9143 //    
9144 //   
9145 });
9146
9147  
9148
9149  /*
9150  * Based on:
9151  * Ext JS Library 1.1.1
9152  * Copyright(c) 2006-2007, Ext JS, LLC.
9153  *
9154  * Originally Released Under LGPL - original licence link has changed is not relivant.
9155  *
9156  * Fork - LGPL
9157  * <script type="text/javascript">
9158  */
9159
9160 // as we use this in bootstrap.
9161 Roo.namespace('Roo.form');
9162  /**
9163  * @class Roo.form.Action
9164  * Internal Class used to handle form actions
9165  * @constructor
9166  * @param {Roo.form.BasicForm} el The form element or its id
9167  * @param {Object} config Configuration options
9168  */
9169
9170  
9171  
9172 // define the action interface
9173 Roo.form.Action = function(form, options){
9174     this.form = form;
9175     this.options = options || {};
9176 };
9177 /**
9178  * Client Validation Failed
9179  * @const 
9180  */
9181 Roo.form.Action.CLIENT_INVALID = 'client';
9182 /**
9183  * Server Validation Failed
9184  * @const 
9185  */
9186 Roo.form.Action.SERVER_INVALID = 'server';
9187  /**
9188  * Connect to Server Failed
9189  * @const 
9190  */
9191 Roo.form.Action.CONNECT_FAILURE = 'connect';
9192 /**
9193  * Reading Data from Server Failed
9194  * @const 
9195  */
9196 Roo.form.Action.LOAD_FAILURE = 'load';
9197
9198 Roo.form.Action.prototype = {
9199     type : 'default',
9200     failureType : undefined,
9201     response : undefined,
9202     result : undefined,
9203
9204     // interface method
9205     run : function(options){
9206
9207     },
9208
9209     // interface method
9210     success : function(response){
9211
9212     },
9213
9214     // interface method
9215     handleResponse : function(response){
9216
9217     },
9218
9219     // default connection failure
9220     failure : function(response){
9221         
9222         this.response = response;
9223         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9224         this.form.afterAction(this, false);
9225     },
9226
9227     processResponse : function(response){
9228         this.response = response;
9229         if(!response.responseText){
9230             return true;
9231         }
9232         this.result = this.handleResponse(response);
9233         return this.result;
9234     },
9235
9236     // utility functions used internally
9237     getUrl : function(appendParams){
9238         var url = this.options.url || this.form.url || this.form.el.dom.action;
9239         if(appendParams){
9240             var p = this.getParams();
9241             if(p){
9242                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9243             }
9244         }
9245         return url;
9246     },
9247
9248     getMethod : function(){
9249         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9250     },
9251
9252     getParams : function(){
9253         var bp = this.form.baseParams;
9254         var p = this.options.params;
9255         if(p){
9256             if(typeof p == "object"){
9257                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9258             }else if(typeof p == 'string' && bp){
9259                 p += '&' + Roo.urlEncode(bp);
9260             }
9261         }else if(bp){
9262             p = Roo.urlEncode(bp);
9263         }
9264         return p;
9265     },
9266
9267     createCallback : function(){
9268         return {
9269             success: this.success,
9270             failure: this.failure,
9271             scope: this,
9272             timeout: (this.form.timeout*1000),
9273             upload: this.form.fileUpload ? this.success : undefined
9274         };
9275     }
9276 };
9277
9278 Roo.form.Action.Submit = function(form, options){
9279     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9280 };
9281
9282 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9283     type : 'submit',
9284
9285     haveProgress : false,
9286     uploadComplete : false,
9287     
9288     // uploadProgress indicator.
9289     uploadProgress : function()
9290     {
9291         if (!this.form.progressUrl) {
9292             return;
9293         }
9294         
9295         if (!this.haveProgress) {
9296             Roo.MessageBox.progress("Uploading", "Uploading");
9297         }
9298         if (this.uploadComplete) {
9299            Roo.MessageBox.hide();
9300            return;
9301         }
9302         
9303         this.haveProgress = true;
9304    
9305         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9306         
9307         var c = new Roo.data.Connection();
9308         c.request({
9309             url : this.form.progressUrl,
9310             params: {
9311                 id : uid
9312             },
9313             method: 'GET',
9314             success : function(req){
9315                //console.log(data);
9316                 var rdata = false;
9317                 var edata;
9318                 try  {
9319                    rdata = Roo.decode(req.responseText)
9320                 } catch (e) {
9321                     Roo.log("Invalid data from server..");
9322                     Roo.log(edata);
9323                     return;
9324                 }
9325                 if (!rdata || !rdata.success) {
9326                     Roo.log(rdata);
9327                     Roo.MessageBox.alert(Roo.encode(rdata));
9328                     return;
9329                 }
9330                 var data = rdata.data;
9331                 
9332                 if (this.uploadComplete) {
9333                    Roo.MessageBox.hide();
9334                    return;
9335                 }
9336                    
9337                 if (data){
9338                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9339                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9340                     );
9341                 }
9342                 this.uploadProgress.defer(2000,this);
9343             },
9344        
9345             failure: function(data) {
9346                 Roo.log('progress url failed ');
9347                 Roo.log(data);
9348             },
9349             scope : this
9350         });
9351            
9352     },
9353     
9354     
9355     run : function()
9356     {
9357         // run get Values on the form, so it syncs any secondary forms.
9358         this.form.getValues();
9359         
9360         var o = this.options;
9361         var method = this.getMethod();
9362         var isPost = method == 'POST';
9363         if(o.clientValidation === false || this.form.isValid()){
9364             
9365             if (this.form.progressUrl) {
9366                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9367                     (new Date() * 1) + '' + Math.random());
9368                     
9369             } 
9370             
9371             
9372             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9373                 form:this.form.el.dom,
9374                 url:this.getUrl(!isPost),
9375                 method: method,
9376                 params:isPost ? this.getParams() : null,
9377                 isUpload: this.form.fileUpload,
9378                 formData : this.form.formData
9379             }));
9380             
9381             this.uploadProgress();
9382
9383         }else if (o.clientValidation !== false){ // client validation failed
9384             this.failureType = Roo.form.Action.CLIENT_INVALID;
9385             this.form.afterAction(this, false);
9386         }
9387     },
9388
9389     success : function(response)
9390     {
9391         this.uploadComplete= true;
9392         if (this.haveProgress) {
9393             Roo.MessageBox.hide();
9394         }
9395         
9396         
9397         var result = this.processResponse(response);
9398         if(result === true || result.success){
9399             this.form.afterAction(this, true);
9400             return;
9401         }
9402         if(result.errors){
9403             this.form.markInvalid(result.errors);
9404             this.failureType = Roo.form.Action.SERVER_INVALID;
9405         }
9406         this.form.afterAction(this, false);
9407     },
9408     failure : function(response)
9409     {
9410         this.uploadComplete= true;
9411         if (this.haveProgress) {
9412             Roo.MessageBox.hide();
9413         }
9414         
9415         this.response = response;
9416         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9417         this.form.afterAction(this, false);
9418     },
9419     
9420     handleResponse : function(response){
9421         if(this.form.errorReader){
9422             var rs = this.form.errorReader.read(response);
9423             var errors = [];
9424             if(rs.records){
9425                 for(var i = 0, len = rs.records.length; i < len; i++) {
9426                     var r = rs.records[i];
9427                     errors[i] = r.data;
9428                 }
9429             }
9430             if(errors.length < 1){
9431                 errors = null;
9432             }
9433             return {
9434                 success : rs.success,
9435                 errors : errors
9436             };
9437         }
9438         var ret = false;
9439         try {
9440             ret = Roo.decode(response.responseText);
9441         } catch (e) {
9442             ret = {
9443                 success: false,
9444                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9445                 errors : []
9446             };
9447         }
9448         return ret;
9449         
9450     }
9451 });
9452
9453
9454 Roo.form.Action.Load = function(form, options){
9455     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9456     this.reader = this.form.reader;
9457 };
9458
9459 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9460     type : 'load',
9461
9462     run : function(){
9463         
9464         Roo.Ajax.request(Roo.apply(
9465                 this.createCallback(), {
9466                     method:this.getMethod(),
9467                     url:this.getUrl(false),
9468                     params:this.getParams()
9469         }));
9470     },
9471
9472     success : function(response){
9473         
9474         var result = this.processResponse(response);
9475         if(result === true || !result.success || !result.data){
9476             this.failureType = Roo.form.Action.LOAD_FAILURE;
9477             this.form.afterAction(this, false);
9478             return;
9479         }
9480         this.form.clearInvalid();
9481         this.form.setValues(result.data);
9482         this.form.afterAction(this, true);
9483     },
9484
9485     handleResponse : function(response){
9486         if(this.form.reader){
9487             var rs = this.form.reader.read(response);
9488             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9489             return {
9490                 success : rs.success,
9491                 data : data
9492             };
9493         }
9494         return Roo.decode(response.responseText);
9495     }
9496 });
9497
9498 Roo.form.Action.ACTION_TYPES = {
9499     'load' : Roo.form.Action.Load,
9500     'submit' : Roo.form.Action.Submit
9501 };/*
9502  * - LGPL
9503  *
9504  * form
9505  *
9506  */
9507
9508 /**
9509  * @class Roo.bootstrap.Form
9510  * @extends Roo.bootstrap.Component
9511  * Bootstrap Form class
9512  * @cfg {String} method  GET | POST (default POST)
9513  * @cfg {String} labelAlign top | left (default top)
9514  * @cfg {String} align left  | right - for navbars
9515  * @cfg {Boolean} loadMask load mask when submit (default true)
9516
9517  *
9518  * @constructor
9519  * Create a new Form
9520  * @param {Object} config The config object
9521  */
9522
9523
9524 Roo.bootstrap.Form = function(config){
9525     
9526     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9527     
9528     Roo.bootstrap.Form.popover.apply();
9529     
9530     this.addEvents({
9531         /**
9532          * @event clientvalidation
9533          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9534          * @param {Form} this
9535          * @param {Boolean} valid true if the form has passed client-side validation
9536          */
9537         clientvalidation: true,
9538         /**
9539          * @event beforeaction
9540          * Fires before any action is performed. Return false to cancel the action.
9541          * @param {Form} this
9542          * @param {Action} action The action to be performed
9543          */
9544         beforeaction: true,
9545         /**
9546          * @event actionfailed
9547          * Fires when an action fails.
9548          * @param {Form} this
9549          * @param {Action} action The action that failed
9550          */
9551         actionfailed : true,
9552         /**
9553          * @event actioncomplete
9554          * Fires when an action is completed.
9555          * @param {Form} this
9556          * @param {Action} action The action that completed
9557          */
9558         actioncomplete : true
9559     });
9560 };
9561
9562 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9563
9564      /**
9565      * @cfg {String} method
9566      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9567      */
9568     method : 'POST',
9569     /**
9570      * @cfg {String} url
9571      * The URL to use for form actions if one isn't supplied in the action options.
9572      */
9573     /**
9574      * @cfg {Boolean} fileUpload
9575      * Set to true if this form is a file upload.
9576      */
9577
9578     /**
9579      * @cfg {Object} baseParams
9580      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9581      */
9582
9583     /**
9584      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9585      */
9586     timeout: 30,
9587     /**
9588      * @cfg {Sting} align (left|right) for navbar forms
9589      */
9590     align : 'left',
9591
9592     // private
9593     activeAction : null,
9594
9595     /**
9596      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9597      * element by passing it or its id or mask the form itself by passing in true.
9598      * @type Mixed
9599      */
9600     waitMsgTarget : false,
9601
9602     loadMask : true,
9603     
9604     /**
9605      * @cfg {Boolean} errorMask (true|false) default false
9606      */
9607     errorMask : false,
9608     
9609     /**
9610      * @cfg {Number} maskOffset Default 100
9611      */
9612     maskOffset : 100,
9613     
9614     /**
9615      * @cfg {Boolean} maskBody
9616      */
9617     maskBody : false,
9618
9619     getAutoCreate : function(){
9620
9621         var cfg = {
9622             tag: 'form',
9623             method : this.method || 'POST',
9624             id : this.id || Roo.id(),
9625             cls : ''
9626         };
9627         if (this.parent().xtype.match(/^Nav/)) {
9628             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9629
9630         }
9631
9632         if (this.labelAlign == 'left' ) {
9633             cfg.cls += ' form-horizontal';
9634         }
9635
9636
9637         return cfg;
9638     },
9639     initEvents : function()
9640     {
9641         this.el.on('submit', this.onSubmit, this);
9642         // this was added as random key presses on the form where triggering form submit.
9643         this.el.on('keypress', function(e) {
9644             if (e.getCharCode() != 13) {
9645                 return true;
9646             }
9647             // we might need to allow it for textareas.. and some other items.
9648             // check e.getTarget().
9649
9650             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9651                 return true;
9652             }
9653
9654             Roo.log("keypress blocked");
9655
9656             e.preventDefault();
9657             return false;
9658         });
9659         
9660     },
9661     // private
9662     onSubmit : function(e){
9663         e.stopEvent();
9664     },
9665
9666      /**
9667      * Returns true if client-side validation on the form is successful.
9668      * @return Boolean
9669      */
9670     isValid : function(){
9671         var items = this.getItems();
9672         var valid = true;
9673         var target = false;
9674         
9675         items.each(function(f){
9676             
9677             if(f.validate()){
9678                 return;
9679             }
9680             
9681             Roo.log('invalid field: ' + f.name);
9682             
9683             valid = false;
9684
9685             if(!target && f.el.isVisible(true)){
9686                 target = f;
9687             }
9688            
9689         });
9690         
9691         if(this.errorMask && !valid){
9692             Roo.bootstrap.Form.popover.mask(this, target);
9693         }
9694         
9695         return valid;
9696     },
9697     
9698     /**
9699      * Returns true if any fields in this form have changed since their original load.
9700      * @return Boolean
9701      */
9702     isDirty : function(){
9703         var dirty = false;
9704         var items = this.getItems();
9705         items.each(function(f){
9706            if(f.isDirty()){
9707                dirty = true;
9708                return false;
9709            }
9710            return true;
9711         });
9712         return dirty;
9713     },
9714      /**
9715      * Performs a predefined action (submit or load) or custom actions you define on this form.
9716      * @param {String} actionName The name of the action type
9717      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9718      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9719      * accept other config options):
9720      * <pre>
9721 Property          Type             Description
9722 ----------------  ---------------  ----------------------------------------------------------------------------------
9723 url               String           The url for the action (defaults to the form's url)
9724 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9725 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9726 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9727                                    validate the form on the client (defaults to false)
9728      * </pre>
9729      * @return {BasicForm} this
9730      */
9731     doAction : function(action, options){
9732         if(typeof action == 'string'){
9733             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9734         }
9735         if(this.fireEvent('beforeaction', this, action) !== false){
9736             this.beforeAction(action);
9737             action.run.defer(100, action);
9738         }
9739         return this;
9740     },
9741
9742     // private
9743     beforeAction : function(action){
9744         var o = action.options;
9745         
9746         if(this.loadMask){
9747             
9748             if(this.maskBody){
9749                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9750             } else {
9751                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9752             }
9753         }
9754         // not really supported yet.. ??
9755
9756         //if(this.waitMsgTarget === true){
9757         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9758         //}else if(this.waitMsgTarget){
9759         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9760         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9761         //}else {
9762         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9763        // }
9764
9765     },
9766
9767     // private
9768     afterAction : function(action, success){
9769         this.activeAction = null;
9770         var o = action.options;
9771
9772         if(this.loadMask){
9773             
9774             if(this.maskBody){
9775                 Roo.get(document.body).unmask();
9776             } else {
9777                 this.el.unmask();
9778             }
9779         }
9780         
9781         //if(this.waitMsgTarget === true){
9782 //            this.el.unmask();
9783         //}else if(this.waitMsgTarget){
9784         //    this.waitMsgTarget.unmask();
9785         //}else{
9786         //    Roo.MessageBox.updateProgress(1);
9787         //    Roo.MessageBox.hide();
9788        // }
9789         //
9790         if(success){
9791             if(o.reset){
9792                 this.reset();
9793             }
9794             Roo.callback(o.success, o.scope, [this, action]);
9795             this.fireEvent('actioncomplete', this, action);
9796
9797         }else{
9798
9799             // failure condition..
9800             // we have a scenario where updates need confirming.
9801             // eg. if a locking scenario exists..
9802             // we look for { errors : { needs_confirm : true }} in the response.
9803             if (
9804                 (typeof(action.result) != 'undefined')  &&
9805                 (typeof(action.result.errors) != 'undefined')  &&
9806                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9807            ){
9808                 var _t = this;
9809                 Roo.log("not supported yet");
9810                  /*
9811
9812                 Roo.MessageBox.confirm(
9813                     "Change requires confirmation",
9814                     action.result.errorMsg,
9815                     function(r) {
9816                         if (r != 'yes') {
9817                             return;
9818                         }
9819                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9820                     }
9821
9822                 );
9823                 */
9824
9825
9826                 return;
9827             }
9828
9829             Roo.callback(o.failure, o.scope, [this, action]);
9830             // show an error message if no failed handler is set..
9831             if (!this.hasListener('actionfailed')) {
9832                 Roo.log("need to add dialog support");
9833                 /*
9834                 Roo.MessageBox.alert("Error",
9835                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9836                         action.result.errorMsg :
9837                         "Saving Failed, please check your entries or try again"
9838                 );
9839                 */
9840             }
9841
9842             this.fireEvent('actionfailed', this, action);
9843         }
9844
9845     },
9846     /**
9847      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9848      * @param {String} id The value to search for
9849      * @return Field
9850      */
9851     findField : function(id){
9852         var items = this.getItems();
9853         var field = items.get(id);
9854         if(!field){
9855              items.each(function(f){
9856                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9857                     field = f;
9858                     return false;
9859                 }
9860                 return true;
9861             });
9862         }
9863         return field || null;
9864     },
9865      /**
9866      * Mark fields in this form invalid in bulk.
9867      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9868      * @return {BasicForm} this
9869      */
9870     markInvalid : function(errors){
9871         if(errors instanceof Array){
9872             for(var i = 0, len = errors.length; i < len; i++){
9873                 var fieldError = errors[i];
9874                 var f = this.findField(fieldError.id);
9875                 if(f){
9876                     f.markInvalid(fieldError.msg);
9877                 }
9878             }
9879         }else{
9880             var field, id;
9881             for(id in errors){
9882                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9883                     field.markInvalid(errors[id]);
9884                 }
9885             }
9886         }
9887         //Roo.each(this.childForms || [], function (f) {
9888         //    f.markInvalid(errors);
9889         //});
9890
9891         return this;
9892     },
9893
9894     /**
9895      * Set values for fields in this form in bulk.
9896      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9897      * @return {BasicForm} this
9898      */
9899     setValues : function(values){
9900         if(values instanceof Array){ // array of objects
9901             for(var i = 0, len = values.length; i < len; i++){
9902                 var v = values[i];
9903                 var f = this.findField(v.id);
9904                 if(f){
9905                     f.setValue(v.value);
9906                     if(this.trackResetOnLoad){
9907                         f.originalValue = f.getValue();
9908                     }
9909                 }
9910             }
9911         }else{ // object hash
9912             var field, id;
9913             for(id in values){
9914                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9915
9916                     if (field.setFromData &&
9917                         field.valueField &&
9918                         field.displayField &&
9919                         // combos' with local stores can
9920                         // be queried via setValue()
9921                         // to set their value..
9922                         (field.store && !field.store.isLocal)
9923                         ) {
9924                         // it's a combo
9925                         var sd = { };
9926                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9927                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9928                         field.setFromData(sd);
9929
9930                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9931                         
9932                         field.setFromData(values);
9933                         
9934                     } else {
9935                         field.setValue(values[id]);
9936                     }
9937
9938
9939                     if(this.trackResetOnLoad){
9940                         field.originalValue = field.getValue();
9941                     }
9942                 }
9943             }
9944         }
9945
9946         //Roo.each(this.childForms || [], function (f) {
9947         //    f.setValues(values);
9948         //});
9949
9950         return this;
9951     },
9952
9953     /**
9954      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9955      * they are returned as an array.
9956      * @param {Boolean} asString
9957      * @return {Object}
9958      */
9959     getValues : function(asString){
9960         //if (this.childForms) {
9961             // copy values from the child forms
9962         //    Roo.each(this.childForms, function (f) {
9963         //        this.setValues(f.getValues());
9964         //    }, this);
9965         //}
9966
9967
9968
9969         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9970         if(asString === true){
9971             return fs;
9972         }
9973         return Roo.urlDecode(fs);
9974     },
9975
9976     /**
9977      * Returns the fields in this form as an object with key/value pairs.
9978      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9979      * @return {Object}
9980      */
9981     getFieldValues : function(with_hidden)
9982     {
9983         var items = this.getItems();
9984         var ret = {};
9985         items.each(function(f){
9986             
9987             if (!f.getName()) {
9988                 return;
9989             }
9990             
9991             var v = f.getValue();
9992             
9993             if (f.inputType =='radio') {
9994                 if (typeof(ret[f.getName()]) == 'undefined') {
9995                     ret[f.getName()] = ''; // empty..
9996                 }
9997
9998                 if (!f.el.dom.checked) {
9999                     return;
10000
10001                 }
10002                 v = f.el.dom.value;
10003
10004             }
10005             
10006             if(f.xtype == 'MoneyField'){
10007                 ret[f.currencyName] = f.getCurrency();
10008             }
10009
10010             // not sure if this supported any more..
10011             if ((typeof(v) == 'object') && f.getRawValue) {
10012                 v = f.getRawValue() ; // dates..
10013             }
10014             // combo boxes where name != hiddenName...
10015             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10016                 ret[f.name] = f.getRawValue();
10017             }
10018             ret[f.getName()] = v;
10019         });
10020
10021         return ret;
10022     },
10023
10024     /**
10025      * Clears all invalid messages in this form.
10026      * @return {BasicForm} this
10027      */
10028     clearInvalid : function(){
10029         var items = this.getItems();
10030
10031         items.each(function(f){
10032            f.clearInvalid();
10033         });
10034
10035         return this;
10036     },
10037
10038     /**
10039      * Resets this form.
10040      * @return {BasicForm} this
10041      */
10042     reset : function(){
10043         var items = this.getItems();
10044         items.each(function(f){
10045             f.reset();
10046         });
10047
10048         Roo.each(this.childForms || [], function (f) {
10049             f.reset();
10050         });
10051
10052
10053         return this;
10054     },
10055     
10056     getItems : function()
10057     {
10058         var r=new Roo.util.MixedCollection(false, function(o){
10059             return o.id || (o.id = Roo.id());
10060         });
10061         var iter = function(el) {
10062             if (el.inputEl) {
10063                 r.add(el);
10064             }
10065             if (!el.items) {
10066                 return;
10067             }
10068             Roo.each(el.items,function(e) {
10069                 iter(e);
10070             });
10071         };
10072
10073         iter(this);
10074         return r;
10075     },
10076     
10077     hideFields : function(items)
10078     {
10079         Roo.each(items, function(i){
10080             
10081             var f = this.findField(i);
10082             
10083             if(!f){
10084                 return;
10085             }
10086             
10087             f.hide();
10088             
10089         }, this);
10090     },
10091     
10092     showFields : function(items)
10093     {
10094         Roo.each(items, function(i){
10095             
10096             var f = this.findField(i);
10097             
10098             if(!f){
10099                 return;
10100             }
10101             
10102             f.show();
10103             
10104         }, this);
10105     }
10106
10107 });
10108
10109 Roo.apply(Roo.bootstrap.Form, {
10110     
10111     popover : {
10112         
10113         padding : 5,
10114         
10115         isApplied : false,
10116         
10117         isMasked : false,
10118         
10119         form : false,
10120         
10121         target : false,
10122         
10123         toolTip : false,
10124         
10125         intervalID : false,
10126         
10127         maskEl : false,
10128         
10129         apply : function()
10130         {
10131             if(this.isApplied){
10132                 return;
10133             }
10134             
10135             this.maskEl = {
10136                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10137                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10138                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10139                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10140             };
10141             
10142             this.maskEl.top.enableDisplayMode("block");
10143             this.maskEl.left.enableDisplayMode("block");
10144             this.maskEl.bottom.enableDisplayMode("block");
10145             this.maskEl.right.enableDisplayMode("block");
10146             
10147             this.toolTip = new Roo.bootstrap.Tooltip({
10148                 cls : 'roo-form-error-popover',
10149                 alignment : {
10150                     'left' : ['r-l', [-2,0], 'right'],
10151                     'right' : ['l-r', [2,0], 'left'],
10152                     'bottom' : ['tl-bl', [0,2], 'top'],
10153                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10154                 }
10155             });
10156             
10157             this.toolTip.render(Roo.get(document.body));
10158
10159             this.toolTip.el.enableDisplayMode("block");
10160             
10161             Roo.get(document.body).on('click', function(){
10162                 this.unmask();
10163             }, this);
10164             
10165             Roo.get(document.body).on('touchstart', function(){
10166                 this.unmask();
10167             }, this);
10168             
10169             this.isApplied = true
10170         },
10171         
10172         mask : function(form, target)
10173         {
10174             this.form = form;
10175             
10176             this.target = target;
10177             
10178             if(!this.form.errorMask || !target.el){
10179                 return;
10180             }
10181             
10182             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10183             
10184             Roo.log(scrollable);
10185             
10186             var ot = this.target.el.calcOffsetsTo(scrollable);
10187             
10188             var scrollTo = ot[1] - this.form.maskOffset;
10189             
10190             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10191             
10192             scrollable.scrollTo('top', scrollTo);
10193             
10194             var box = this.target.el.getBox();
10195             Roo.log(box);
10196             var zIndex = Roo.bootstrap.Modal.zIndex++;
10197
10198             
10199             this.maskEl.top.setStyle('position', 'absolute');
10200             this.maskEl.top.setStyle('z-index', zIndex);
10201             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10202             this.maskEl.top.setLeft(0);
10203             this.maskEl.top.setTop(0);
10204             this.maskEl.top.show();
10205             
10206             this.maskEl.left.setStyle('position', 'absolute');
10207             this.maskEl.left.setStyle('z-index', zIndex);
10208             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10209             this.maskEl.left.setLeft(0);
10210             this.maskEl.left.setTop(box.y - this.padding);
10211             this.maskEl.left.show();
10212
10213             this.maskEl.bottom.setStyle('position', 'absolute');
10214             this.maskEl.bottom.setStyle('z-index', zIndex);
10215             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10216             this.maskEl.bottom.setLeft(0);
10217             this.maskEl.bottom.setTop(box.bottom + this.padding);
10218             this.maskEl.bottom.show();
10219
10220             this.maskEl.right.setStyle('position', 'absolute');
10221             this.maskEl.right.setStyle('z-index', zIndex);
10222             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10223             this.maskEl.right.setLeft(box.right + this.padding);
10224             this.maskEl.right.setTop(box.y - this.padding);
10225             this.maskEl.right.show();
10226
10227             this.toolTip.bindEl = this.target.el;
10228
10229             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10230
10231             var tip = this.target.blankText;
10232
10233             if(this.target.getValue() !== '' ) {
10234                 
10235                 if (this.target.invalidText.length) {
10236                     tip = this.target.invalidText;
10237                 } else if (this.target.regexText.length){
10238                     tip = this.target.regexText;
10239                 }
10240             }
10241
10242             this.toolTip.show(tip);
10243
10244             this.intervalID = window.setInterval(function() {
10245                 Roo.bootstrap.Form.popover.unmask();
10246             }, 10000);
10247
10248             window.onwheel = function(){ return false;};
10249             
10250             (function(){ this.isMasked = true; }).defer(500, this);
10251             
10252         },
10253         
10254         unmask : function()
10255         {
10256             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10257                 return;
10258             }
10259             
10260             this.maskEl.top.setStyle('position', 'absolute');
10261             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10262             this.maskEl.top.hide();
10263
10264             this.maskEl.left.setStyle('position', 'absolute');
10265             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10266             this.maskEl.left.hide();
10267
10268             this.maskEl.bottom.setStyle('position', 'absolute');
10269             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.bottom.hide();
10271
10272             this.maskEl.right.setStyle('position', 'absolute');
10273             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.right.hide();
10275             
10276             this.toolTip.hide();
10277             
10278             this.toolTip.el.hide();
10279             
10280             window.onwheel = function(){ return true;};
10281             
10282             if(this.intervalID){
10283                 window.clearInterval(this.intervalID);
10284                 this.intervalID = false;
10285             }
10286             
10287             this.isMasked = false;
10288             
10289         }
10290         
10291     }
10292     
10293 });
10294
10295 /*
10296  * Based on:
10297  * Ext JS Library 1.1.1
10298  * Copyright(c) 2006-2007, Ext JS, LLC.
10299  *
10300  * Originally Released Under LGPL - original licence link has changed is not relivant.
10301  *
10302  * Fork - LGPL
10303  * <script type="text/javascript">
10304  */
10305 /**
10306  * @class Roo.form.VTypes
10307  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10308  * @singleton
10309  */
10310 Roo.form.VTypes = function(){
10311     // closure these in so they are only created once.
10312     var alpha = /^[a-zA-Z_]+$/;
10313     var alphanum = /^[a-zA-Z0-9_]+$/;
10314     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10315     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10316
10317     // All these messages and functions are configurable
10318     return {
10319         /**
10320          * The function used to validate email addresses
10321          * @param {String} value The email address
10322          */
10323         'email' : function(v){
10324             return email.test(v);
10325         },
10326         /**
10327          * The error text to display when the email validation function returns false
10328          * @type String
10329          */
10330         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10331         /**
10332          * The keystroke filter mask to be applied on email input
10333          * @type RegExp
10334          */
10335         'emailMask' : /[a-z0-9_\.\-@]/i,
10336
10337         /**
10338          * The function used to validate URLs
10339          * @param {String} value The URL
10340          */
10341         'url' : function(v){
10342             return url.test(v);
10343         },
10344         /**
10345          * The error text to display when the url validation function returns false
10346          * @type String
10347          */
10348         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10349         
10350         /**
10351          * The function used to validate alpha values
10352          * @param {String} value The value
10353          */
10354         'alpha' : function(v){
10355             return alpha.test(v);
10356         },
10357         /**
10358          * The error text to display when the alpha validation function returns false
10359          * @type String
10360          */
10361         'alphaText' : 'This field should only contain letters and _',
10362         /**
10363          * The keystroke filter mask to be applied on alpha input
10364          * @type RegExp
10365          */
10366         'alphaMask' : /[a-z_]/i,
10367
10368         /**
10369          * The function used to validate alphanumeric values
10370          * @param {String} value The value
10371          */
10372         'alphanum' : function(v){
10373             return alphanum.test(v);
10374         },
10375         /**
10376          * The error text to display when the alphanumeric validation function returns false
10377          * @type String
10378          */
10379         'alphanumText' : 'This field should only contain letters, numbers and _',
10380         /**
10381          * The keystroke filter mask to be applied on alphanumeric input
10382          * @type RegExp
10383          */
10384         'alphanumMask' : /[a-z0-9_]/i
10385     };
10386 }();/*
10387  * - LGPL
10388  *
10389  * Input
10390  * 
10391  */
10392
10393 /**
10394  * @class Roo.bootstrap.Input
10395  * @extends Roo.bootstrap.Component
10396  * Bootstrap Input class
10397  * @cfg {Boolean} disabled is it disabled
10398  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10399  * @cfg {String} name name of the input
10400  * @cfg {string} fieldLabel - the label associated
10401  * @cfg {string} placeholder - placeholder to put in text.
10402  * @cfg {string}  before - input group add on before
10403  * @cfg {string} after - input group add on after
10404  * @cfg {string} size - (lg|sm) or leave empty..
10405  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10406  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10407  * @cfg {Number} md colspan out of 12 for computer-sized screens
10408  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10409  * @cfg {string} value default value of the input
10410  * @cfg {Number} labelWidth set the width of label 
10411  * @cfg {Number} labellg set the width of label (1-12)
10412  * @cfg {Number} labelmd set the width of label (1-12)
10413  * @cfg {Number} labelsm set the width of label (1-12)
10414  * @cfg {Number} labelxs set the width of label (1-12)
10415  * @cfg {String} labelAlign (top|left)
10416  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10417  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10418  * @cfg {String} indicatorpos (left|right) default left
10419  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10420  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10421  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10422
10423  * @cfg {String} align (left|center|right) Default left
10424  * @cfg {Boolean} forceFeedback (true|false) Default false
10425  * 
10426  * @constructor
10427  * Create a new Input
10428  * @param {Object} config The config object
10429  */
10430
10431 Roo.bootstrap.Input = function(config){
10432     
10433     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10434     
10435     this.addEvents({
10436         /**
10437          * @event focus
10438          * Fires when this field receives input focus.
10439          * @param {Roo.form.Field} this
10440          */
10441         focus : true,
10442         /**
10443          * @event blur
10444          * Fires when this field loses input focus.
10445          * @param {Roo.form.Field} this
10446          */
10447         blur : true,
10448         /**
10449          * @event specialkey
10450          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10451          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10452          * @param {Roo.form.Field} this
10453          * @param {Roo.EventObject} e The event object
10454          */
10455         specialkey : true,
10456         /**
10457          * @event change
10458          * Fires just before the field blurs if the field value has changed.
10459          * @param {Roo.form.Field} this
10460          * @param {Mixed} newValue The new value
10461          * @param {Mixed} oldValue The original value
10462          */
10463         change : true,
10464         /**
10465          * @event invalid
10466          * Fires after the field has been marked as invalid.
10467          * @param {Roo.form.Field} this
10468          * @param {String} msg The validation message
10469          */
10470         invalid : true,
10471         /**
10472          * @event valid
10473          * Fires after the field has been validated with no errors.
10474          * @param {Roo.form.Field} this
10475          */
10476         valid : true,
10477          /**
10478          * @event keyup
10479          * Fires after the key up
10480          * @param {Roo.form.Field} this
10481          * @param {Roo.EventObject}  e The event Object
10482          */
10483         keyup : true
10484     });
10485 };
10486
10487 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10488      /**
10489      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10490       automatic validation (defaults to "keyup").
10491      */
10492     validationEvent : "keyup",
10493      /**
10494      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10495      */
10496     validateOnBlur : true,
10497     /**
10498      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10499      */
10500     validationDelay : 250,
10501      /**
10502      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10503      */
10504     focusClass : "x-form-focus",  // not needed???
10505     
10506        
10507     /**
10508      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10509      */
10510     invalidClass : "has-warning",
10511     
10512     /**
10513      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10514      */
10515     validClass : "has-success",
10516     
10517     /**
10518      * @cfg {Boolean} hasFeedback (true|false) default true
10519      */
10520     hasFeedback : true,
10521     
10522     /**
10523      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10524      */
10525     invalidFeedbackClass : "glyphicon-warning-sign",
10526     
10527     /**
10528      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10529      */
10530     validFeedbackClass : "glyphicon-ok",
10531     
10532     /**
10533      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10534      */
10535     selectOnFocus : false,
10536     
10537      /**
10538      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10539      */
10540     maskRe : null,
10541        /**
10542      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10543      */
10544     vtype : null,
10545     
10546       /**
10547      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10548      */
10549     disableKeyFilter : false,
10550     
10551        /**
10552      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10553      */
10554     disabled : false,
10555      /**
10556      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10557      */
10558     allowBlank : true,
10559     /**
10560      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10561      */
10562     blankText : "Please complete this mandatory field",
10563     
10564      /**
10565      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10566      */
10567     minLength : 0,
10568     /**
10569      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10570      */
10571     maxLength : Number.MAX_VALUE,
10572     /**
10573      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10574      */
10575     minLengthText : "The minimum length for this field is {0}",
10576     /**
10577      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10578      */
10579     maxLengthText : "The maximum length for this field is {0}",
10580   
10581     
10582     /**
10583      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10584      * If available, this function will be called only after the basic validators all return true, and will be passed the
10585      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10586      */
10587     validator : null,
10588     /**
10589      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10590      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10591      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10592      */
10593     regex : null,
10594     /**
10595      * @cfg {String} regexText -- Depricated - use Invalid Text
10596      */
10597     regexText : "",
10598     
10599     /**
10600      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10601      */
10602     invalidText : "",
10603     
10604     
10605     
10606     autocomplete: false,
10607     
10608     
10609     fieldLabel : '',
10610     inputType : 'text',
10611     
10612     name : false,
10613     placeholder: false,
10614     before : false,
10615     after : false,
10616     size : false,
10617     hasFocus : false,
10618     preventMark: false,
10619     isFormField : true,
10620     value : '',
10621     labelWidth : 2,
10622     labelAlign : false,
10623     readOnly : false,
10624     align : false,
10625     formatedValue : false,
10626     forceFeedback : false,
10627     
10628     indicatorpos : 'left',
10629     
10630     labellg : 0,
10631     labelmd : 0,
10632     labelsm : 0,
10633     labelxs : 0,
10634     
10635     capture : '',
10636     accept : '',
10637     
10638     parentLabelAlign : function()
10639     {
10640         var parent = this;
10641         while (parent.parent()) {
10642             parent = parent.parent();
10643             if (typeof(parent.labelAlign) !='undefined') {
10644                 return parent.labelAlign;
10645             }
10646         }
10647         return 'left';
10648         
10649     },
10650     
10651     getAutoCreate : function()
10652     {
10653         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10654         
10655         var id = Roo.id();
10656         
10657         var cfg = {};
10658         
10659         if(this.inputType != 'hidden'){
10660             cfg.cls = 'form-group' //input-group
10661         }
10662         
10663         var input =  {
10664             tag: 'input',
10665             id : id,
10666             type : this.inputType,
10667             value : this.value,
10668             cls : 'form-control',
10669             placeholder : this.placeholder || '',
10670             autocomplete : this.autocomplete || 'new-password'
10671         };
10672         if (this.inputType == 'file') {
10673             input.style = 'overflow:hidden'; // why not in CSS?
10674         }
10675         
10676         if(this.capture.length){
10677             input.capture = this.capture;
10678         }
10679         
10680         if(this.accept.length){
10681             input.accept = this.accept + "/*";
10682         }
10683         
10684         if(this.align){
10685             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10686         }
10687         
10688         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10689             input.maxLength = this.maxLength;
10690         }
10691         
10692         if (this.disabled) {
10693             input.disabled=true;
10694         }
10695         
10696         if (this.readOnly) {
10697             input.readonly=true;
10698         }
10699         
10700         if (this.name) {
10701             input.name = this.name;
10702         }
10703         
10704         if (this.size) {
10705             input.cls += ' input-' + this.size;
10706         }
10707         
10708         var settings=this;
10709         ['xs','sm','md','lg'].map(function(size){
10710             if (settings[size]) {
10711                 cfg.cls += ' col-' + size + '-' + settings[size];
10712             }
10713         });
10714         
10715         var inputblock = input;
10716         
10717         var feedback = {
10718             tag: 'span',
10719             cls: 'glyphicon form-control-feedback'
10720         };
10721             
10722         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10723             
10724             inputblock = {
10725                 cls : 'has-feedback',
10726                 cn :  [
10727                     input,
10728                     feedback
10729                 ] 
10730             };  
10731         }
10732         
10733         if (this.before || this.after) {
10734             
10735             inputblock = {
10736                 cls : 'input-group',
10737                 cn :  [] 
10738             };
10739             
10740             if (this.before && typeof(this.before) == 'string') {
10741                 
10742                 inputblock.cn.push({
10743                     tag :'span',
10744                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10745                     html : this.before
10746                 });
10747             }
10748             if (this.before && typeof(this.before) == 'object') {
10749                 this.before = Roo.factory(this.before);
10750                 
10751                 inputblock.cn.push({
10752                     tag :'span',
10753                     cls : 'roo-input-before input-group-prepend   input-group-' +
10754                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10755                 });
10756             }
10757             
10758             inputblock.cn.push(input);
10759             
10760             if (this.after && typeof(this.after) == 'string') {
10761                 inputblock.cn.push({
10762                     tag :'span',
10763                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10764                     html : this.after
10765                 });
10766             }
10767             if (this.after && typeof(this.after) == 'object') {
10768                 this.after = Roo.factory(this.after);
10769                 
10770                 inputblock.cn.push({
10771                     tag :'span',
10772                     cls : 'roo-input-after input-group-append  input-group-' +
10773                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10774                 });
10775             }
10776             
10777             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10778                 inputblock.cls += ' has-feedback';
10779                 inputblock.cn.push(feedback);
10780             }
10781         };
10782         var indicator = {
10783             tag : 'i',
10784             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10785             tooltip : 'This field is required'
10786         };
10787         if (this.allowBlank ) {
10788             indicator.style = this.allowBlank ? ' display:none' : '';
10789         }
10790         if (align ==='left' && this.fieldLabel.length) {
10791             
10792             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10793             
10794             cfg.cn = [
10795                 indicator,
10796                 {
10797                     tag: 'label',
10798                     'for' :  id,
10799                     cls : 'control-label col-form-label',
10800                     html : this.fieldLabel
10801
10802                 },
10803                 {
10804                     cls : "", 
10805                     cn: [
10806                         inputblock
10807                     ]
10808                 }
10809             ];
10810             
10811             var labelCfg = cfg.cn[1];
10812             var contentCfg = cfg.cn[2];
10813             
10814             if(this.indicatorpos == 'right'){
10815                 cfg.cn = [
10816                     {
10817                         tag: 'label',
10818                         'for' :  id,
10819                         cls : 'control-label col-form-label',
10820                         cn : [
10821                             {
10822                                 tag : 'span',
10823                                 html : this.fieldLabel
10824                             },
10825                             indicator
10826                         ]
10827                     },
10828                     {
10829                         cls : "",
10830                         cn: [
10831                             inputblock
10832                         ]
10833                     }
10834
10835                 ];
10836                 
10837                 labelCfg = cfg.cn[0];
10838                 contentCfg = cfg.cn[1];
10839             
10840             }
10841             
10842             if(this.labelWidth > 12){
10843                 labelCfg.style = "width: " + this.labelWidth + 'px';
10844             }
10845             
10846             if(this.labelWidth < 13 && this.labelmd == 0){
10847                 this.labelmd = this.labelWidth;
10848             }
10849             
10850             if(this.labellg > 0){
10851                 labelCfg.cls += ' col-lg-' + this.labellg;
10852                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10853             }
10854             
10855             if(this.labelmd > 0){
10856                 labelCfg.cls += ' col-md-' + this.labelmd;
10857                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10858             }
10859             
10860             if(this.labelsm > 0){
10861                 labelCfg.cls += ' col-sm-' + this.labelsm;
10862                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10863             }
10864             
10865             if(this.labelxs > 0){
10866                 labelCfg.cls += ' col-xs-' + this.labelxs;
10867                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10868             }
10869             
10870             
10871         } else if ( this.fieldLabel.length) {
10872                 
10873             
10874             
10875             cfg.cn = [
10876                 {
10877                     tag : 'i',
10878                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10879                     tooltip : 'This field is required',
10880                     style : this.allowBlank ? ' display:none' : '' 
10881                 },
10882                 {
10883                     tag: 'label',
10884                    //cls : 'input-group-addon',
10885                     html : this.fieldLabel
10886
10887                 },
10888
10889                inputblock
10890
10891            ];
10892            
10893            if(this.indicatorpos == 'right'){
10894        
10895                 cfg.cn = [
10896                     {
10897                         tag: 'label',
10898                        //cls : 'input-group-addon',
10899                         html : this.fieldLabel
10900
10901                     },
10902                     {
10903                         tag : 'i',
10904                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10905                         tooltip : 'This field is required',
10906                         style : this.allowBlank ? ' display:none' : '' 
10907                     },
10908
10909                    inputblock
10910
10911                ];
10912
10913             }
10914
10915         } else {
10916             
10917             cfg.cn = [
10918
10919                     inputblock
10920
10921             ];
10922                 
10923                 
10924         };
10925         
10926         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10927            cfg.cls += ' navbar-form';
10928         }
10929         
10930         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10931             // on BS4 we do this only if not form 
10932             cfg.cls += ' navbar-form';
10933             cfg.tag = 'li';
10934         }
10935         
10936         return cfg;
10937         
10938     },
10939     /**
10940      * return the real input element.
10941      */
10942     inputEl: function ()
10943     {
10944         return this.el.select('input.form-control',true).first();
10945     },
10946     
10947     tooltipEl : function()
10948     {
10949         return this.inputEl();
10950     },
10951     
10952     indicatorEl : function()
10953     {
10954         if (Roo.bootstrap.version == 4) {
10955             return false; // not enabled in v4 yet.
10956         }
10957         
10958         var indicator = this.el.select('i.roo-required-indicator',true).first();
10959         
10960         if(!indicator){
10961             return false;
10962         }
10963         
10964         return indicator;
10965         
10966     },
10967     
10968     setDisabled : function(v)
10969     {
10970         var i  = this.inputEl().dom;
10971         if (!v) {
10972             i.removeAttribute('disabled');
10973             return;
10974             
10975         }
10976         i.setAttribute('disabled','true');
10977     },
10978     initEvents : function()
10979     {
10980           
10981         this.inputEl().on("keydown" , this.fireKey,  this);
10982         this.inputEl().on("focus", this.onFocus,  this);
10983         this.inputEl().on("blur", this.onBlur,  this);
10984         
10985         this.inputEl().relayEvent('keyup', this);
10986         
10987         this.indicator = this.indicatorEl();
10988         
10989         if(this.indicator){
10990             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10991         }
10992  
10993         // reference to original value for reset
10994         this.originalValue = this.getValue();
10995         //Roo.form.TextField.superclass.initEvents.call(this);
10996         if(this.validationEvent == 'keyup'){
10997             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10998             this.inputEl().on('keyup', this.filterValidation, this);
10999         }
11000         else if(this.validationEvent !== false){
11001             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11002         }
11003         
11004         if(this.selectOnFocus){
11005             this.on("focus", this.preFocus, this);
11006             
11007         }
11008         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11009             this.inputEl().on("keypress", this.filterKeys, this);
11010         } else {
11011             this.inputEl().relayEvent('keypress', this);
11012         }
11013        /* if(this.grow){
11014             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11015             this.el.on("click", this.autoSize,  this);
11016         }
11017         */
11018         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11019             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11020         }
11021         
11022         if (typeof(this.before) == 'object') {
11023             this.before.render(this.el.select('.roo-input-before',true).first());
11024         }
11025         if (typeof(this.after) == 'object') {
11026             this.after.render(this.el.select('.roo-input-after',true).first());
11027         }
11028         
11029         this.inputEl().on('change', this.onChange, this);
11030         
11031     },
11032     filterValidation : function(e){
11033         if(!e.isNavKeyPress()){
11034             this.validationTask.delay(this.validationDelay);
11035         }
11036     },
11037      /**
11038      * Validates the field value
11039      * @return {Boolean} True if the value is valid, else false
11040      */
11041     validate : function(){
11042         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11043         if(this.disabled || this.validateValue(this.getRawValue())){
11044             this.markValid();
11045             return true;
11046         }
11047         
11048         this.markInvalid();
11049         return false;
11050     },
11051     
11052     
11053     /**
11054      * Validates a value according to the field's validation rules and marks the field as invalid
11055      * if the validation fails
11056      * @param {Mixed} value The value to validate
11057      * @return {Boolean} True if the value is valid, else false
11058      */
11059     validateValue : function(value)
11060     {
11061         if(this.getVisibilityEl().hasClass('hidden')){
11062             return true;
11063         }
11064         
11065         if(value.length < 1)  { // if it's blank
11066             if(this.allowBlank){
11067                 return true;
11068             }
11069             return false;
11070         }
11071         
11072         if(value.length < this.minLength){
11073             return false;
11074         }
11075         if(value.length > this.maxLength){
11076             return false;
11077         }
11078         if(this.vtype){
11079             var vt = Roo.form.VTypes;
11080             if(!vt[this.vtype](value, this)){
11081                 return false;
11082             }
11083         }
11084         if(typeof this.validator == "function"){
11085             var msg = this.validator(value);
11086             if(msg !== true){
11087                 return false;
11088             }
11089             if (typeof(msg) == 'string') {
11090                 this.invalidText = msg;
11091             }
11092         }
11093         
11094         if(this.regex && !this.regex.test(value)){
11095             return false;
11096         }
11097         
11098         return true;
11099     },
11100     
11101      // private
11102     fireKey : function(e){
11103         //Roo.log('field ' + e.getKey());
11104         if(e.isNavKeyPress()){
11105             this.fireEvent("specialkey", this, e);
11106         }
11107     },
11108     focus : function (selectText){
11109         if(this.rendered){
11110             this.inputEl().focus();
11111             if(selectText === true){
11112                 this.inputEl().dom.select();
11113             }
11114         }
11115         return this;
11116     } ,
11117     
11118     onFocus : function(){
11119         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11120            // this.el.addClass(this.focusClass);
11121         }
11122         if(!this.hasFocus){
11123             this.hasFocus = true;
11124             this.startValue = this.getValue();
11125             this.fireEvent("focus", this);
11126         }
11127     },
11128     
11129     beforeBlur : Roo.emptyFn,
11130
11131     
11132     // private
11133     onBlur : function(){
11134         this.beforeBlur();
11135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136             //this.el.removeClass(this.focusClass);
11137         }
11138         this.hasFocus = false;
11139         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11140             this.validate();
11141         }
11142         var v = this.getValue();
11143         if(String(v) !== String(this.startValue)){
11144             this.fireEvent('change', this, v, this.startValue);
11145         }
11146         this.fireEvent("blur", this);
11147     },
11148     
11149     onChange : function(e)
11150     {
11151         var v = this.getValue();
11152         if(String(v) !== String(this.startValue)){
11153             this.fireEvent('change', this, v, this.startValue);
11154         }
11155         
11156     },
11157     
11158     /**
11159      * Resets the current field value to the originally loaded value and clears any validation messages
11160      */
11161     reset : function(){
11162         this.setValue(this.originalValue);
11163         this.validate();
11164     },
11165      /**
11166      * Returns the name of the field
11167      * @return {Mixed} name The name field
11168      */
11169     getName: function(){
11170         return this.name;
11171     },
11172      /**
11173      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11174      * @return {Mixed} value The field value
11175      */
11176     getValue : function(){
11177         
11178         var v = this.inputEl().getValue();
11179         
11180         return v;
11181     },
11182     /**
11183      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11184      * @return {Mixed} value The field value
11185      */
11186     getRawValue : function(){
11187         var v = this.inputEl().getValue();
11188         
11189         return v;
11190     },
11191     
11192     /**
11193      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11194      * @param {Mixed} value The value to set
11195      */
11196     setRawValue : function(v){
11197         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11198     },
11199     
11200     selectText : function(start, end){
11201         var v = this.getRawValue();
11202         if(v.length > 0){
11203             start = start === undefined ? 0 : start;
11204             end = end === undefined ? v.length : end;
11205             var d = this.inputEl().dom;
11206             if(d.setSelectionRange){
11207                 d.setSelectionRange(start, end);
11208             }else if(d.createTextRange){
11209                 var range = d.createTextRange();
11210                 range.moveStart("character", start);
11211                 range.moveEnd("character", v.length-end);
11212                 range.select();
11213             }
11214         }
11215     },
11216     
11217     /**
11218      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11219      * @param {Mixed} value The value to set
11220      */
11221     setValue : function(v){
11222         this.value = v;
11223         if(this.rendered){
11224             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11225             this.validate();
11226         }
11227     },
11228     
11229     /*
11230     processValue : function(value){
11231         if(this.stripCharsRe){
11232             var newValue = value.replace(this.stripCharsRe, '');
11233             if(newValue !== value){
11234                 this.setRawValue(newValue);
11235                 return newValue;
11236             }
11237         }
11238         return value;
11239     },
11240   */
11241     preFocus : function(){
11242         
11243         if(this.selectOnFocus){
11244             this.inputEl().dom.select();
11245         }
11246     },
11247     filterKeys : function(e){
11248         var k = e.getKey();
11249         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11250             return;
11251         }
11252         var c = e.getCharCode(), cc = String.fromCharCode(c);
11253         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11254             return;
11255         }
11256         if(!this.maskRe.test(cc)){
11257             e.stopEvent();
11258         }
11259     },
11260      /**
11261      * Clear any invalid styles/messages for this field
11262      */
11263     clearInvalid : function(){
11264         
11265         if(!this.el || this.preventMark){ // not rendered
11266             return;
11267         }
11268         
11269         
11270         this.el.removeClass([this.invalidClass, 'is-invalid']);
11271         
11272         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11273             
11274             var feedback = this.el.select('.form-control-feedback', true).first();
11275             
11276             if(feedback){
11277                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11278             }
11279             
11280         }
11281         
11282         if(this.indicator){
11283             this.indicator.removeClass('visible');
11284             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11285         }
11286         
11287         this.fireEvent('valid', this);
11288     },
11289     
11290      /**
11291      * Mark this field as valid
11292      */
11293     markValid : function()
11294     {
11295         if(!this.el  || this.preventMark){ // not rendered...
11296             return;
11297         }
11298         
11299         this.el.removeClass([this.invalidClass, this.validClass]);
11300         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11301
11302         var feedback = this.el.select('.form-control-feedback', true).first();
11303             
11304         if(feedback){
11305             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11306         }
11307         
11308         if(this.indicator){
11309             this.indicator.removeClass('visible');
11310             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11311         }
11312         
11313         if(this.disabled){
11314             return;
11315         }
11316         
11317            
11318         if(this.allowBlank && !this.getRawValue().length){
11319             return;
11320         }
11321         if (Roo.bootstrap.version == 3) {
11322             this.el.addClass(this.validClass);
11323         } else {
11324             this.inputEl().addClass('is-valid');
11325         }
11326
11327         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11328             
11329             var feedback = this.el.select('.form-control-feedback', true).first();
11330             
11331             if(feedback){
11332                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11333                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11334             }
11335             
11336         }
11337         
11338         this.fireEvent('valid', this);
11339     },
11340     
11341      /**
11342      * Mark this field as invalid
11343      * @param {String} msg The validation message
11344      */
11345     markInvalid : function(msg)
11346     {
11347         if(!this.el  || this.preventMark){ // not rendered
11348             return;
11349         }
11350         
11351         this.el.removeClass([this.invalidClass, this.validClass]);
11352         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11353         
11354         var feedback = this.el.select('.form-control-feedback', true).first();
11355             
11356         if(feedback){
11357             this.el.select('.form-control-feedback', true).first().removeClass(
11358                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11359         }
11360
11361         if(this.disabled){
11362             return;
11363         }
11364         
11365         if(this.allowBlank && !this.getRawValue().length){
11366             return;
11367         }
11368         
11369         if(this.indicator){
11370             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11371             this.indicator.addClass('visible');
11372         }
11373         if (Roo.bootstrap.version == 3) {
11374             this.el.addClass(this.invalidClass);
11375         } else {
11376             this.inputEl().addClass('is-invalid');
11377         }
11378         
11379         
11380         
11381         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11382             
11383             var feedback = this.el.select('.form-control-feedback', true).first();
11384             
11385             if(feedback){
11386                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11387                 
11388                 if(this.getValue().length || this.forceFeedback){
11389                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11390                 }
11391                 
11392             }
11393             
11394         }
11395         
11396         this.fireEvent('invalid', this, msg);
11397     },
11398     // private
11399     SafariOnKeyDown : function(event)
11400     {
11401         // this is a workaround for a password hang bug on chrome/ webkit.
11402         if (this.inputEl().dom.type != 'password') {
11403             return;
11404         }
11405         
11406         var isSelectAll = false;
11407         
11408         if(this.inputEl().dom.selectionEnd > 0){
11409             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11410         }
11411         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11412             event.preventDefault();
11413             this.setValue('');
11414             return;
11415         }
11416         
11417         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11418             
11419             event.preventDefault();
11420             // this is very hacky as keydown always get's upper case.
11421             //
11422             var cc = String.fromCharCode(event.getCharCode());
11423             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11424             
11425         }
11426     },
11427     adjustWidth : function(tag, w){
11428         tag = tag.toLowerCase();
11429         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11430             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11431                 if(tag == 'input'){
11432                     return w + 2;
11433                 }
11434                 if(tag == 'textarea'){
11435                     return w-2;
11436                 }
11437             }else if(Roo.isOpera){
11438                 if(tag == 'input'){
11439                     return w + 2;
11440                 }
11441                 if(tag == 'textarea'){
11442                     return w-2;
11443                 }
11444             }
11445         }
11446         return w;
11447     },
11448     
11449     setFieldLabel : function(v)
11450     {
11451         if(!this.rendered){
11452             return;
11453         }
11454         
11455         if(this.indicatorEl()){
11456             var ar = this.el.select('label > span',true);
11457             
11458             if (ar.elements.length) {
11459                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11460                 this.fieldLabel = v;
11461                 return;
11462             }
11463             
11464             var br = this.el.select('label',true);
11465             
11466             if(br.elements.length) {
11467                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             Roo.log('Cannot Found any of label > span || label in input');
11473             return;
11474         }
11475         
11476         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11477         this.fieldLabel = v;
11478         
11479         
11480     }
11481 });
11482
11483  
11484 /*
11485  * - LGPL
11486  *
11487  * Input
11488  * 
11489  */
11490
11491 /**
11492  * @class Roo.bootstrap.TextArea
11493  * @extends Roo.bootstrap.Input
11494  * Bootstrap TextArea class
11495  * @cfg {Number} cols Specifies the visible width of a text area
11496  * @cfg {Number} rows Specifies the visible number of lines in a text area
11497  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11498  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11499  * @cfg {string} html text
11500  * 
11501  * @constructor
11502  * Create a new TextArea
11503  * @param {Object} config The config object
11504  */
11505
11506 Roo.bootstrap.TextArea = function(config){
11507     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11508    
11509 };
11510
11511 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11512      
11513     cols : false,
11514     rows : 5,
11515     readOnly : false,
11516     warp : 'soft',
11517     resize : false,
11518     value: false,
11519     html: false,
11520     
11521     getAutoCreate : function(){
11522         
11523         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11524         
11525         var id = Roo.id();
11526         
11527         var cfg = {};
11528         
11529         if(this.inputType != 'hidden'){
11530             cfg.cls = 'form-group' //input-group
11531         }
11532         
11533         var input =  {
11534             tag: 'textarea',
11535             id : id,
11536             warp : this.warp,
11537             rows : this.rows,
11538             value : this.value || '',
11539             html: this.html || '',
11540             cls : 'form-control',
11541             placeholder : this.placeholder || '' 
11542             
11543         };
11544         
11545         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11546             input.maxLength = this.maxLength;
11547         }
11548         
11549         if(this.resize){
11550             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11551         }
11552         
11553         if(this.cols){
11554             input.cols = this.cols;
11555         }
11556         
11557         if (this.readOnly) {
11558             input.readonly = true;
11559         }
11560         
11561         if (this.name) {
11562             input.name = this.name;
11563         }
11564         
11565         if (this.size) {
11566             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11567         }
11568         
11569         var settings=this;
11570         ['xs','sm','md','lg'].map(function(size){
11571             if (settings[size]) {
11572                 cfg.cls += ' col-' + size + '-' + settings[size];
11573             }
11574         });
11575         
11576         var inputblock = input;
11577         
11578         if(this.hasFeedback && !this.allowBlank){
11579             
11580             var feedback = {
11581                 tag: 'span',
11582                 cls: 'glyphicon form-control-feedback'
11583             };
11584
11585             inputblock = {
11586                 cls : 'has-feedback',
11587                 cn :  [
11588                     input,
11589                     feedback
11590                 ] 
11591             };  
11592         }
11593         
11594         
11595         if (this.before || this.after) {
11596             
11597             inputblock = {
11598                 cls : 'input-group',
11599                 cn :  [] 
11600             };
11601             if (this.before) {
11602                 inputblock.cn.push({
11603                     tag :'span',
11604                     cls : 'input-group-addon',
11605                     html : this.before
11606                 });
11607             }
11608             
11609             inputblock.cn.push(input);
11610             
11611             if(this.hasFeedback && !this.allowBlank){
11612                 inputblock.cls += ' has-feedback';
11613                 inputblock.cn.push(feedback);
11614             }
11615             
11616             if (this.after) {
11617                 inputblock.cn.push({
11618                     tag :'span',
11619                     cls : 'input-group-addon',
11620                     html : this.after
11621                 });
11622             }
11623             
11624         }
11625         
11626         if (align ==='left' && this.fieldLabel.length) {
11627             cfg.cn = [
11628                 {
11629                     tag: 'label',
11630                     'for' :  id,
11631                     cls : 'control-label',
11632                     html : this.fieldLabel
11633                 },
11634                 {
11635                     cls : "",
11636                     cn: [
11637                         inputblock
11638                     ]
11639                 }
11640
11641             ];
11642             
11643             if(this.labelWidth > 12){
11644                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11645             }
11646
11647             if(this.labelWidth < 13 && this.labelmd == 0){
11648                 this.labelmd = this.labelWidth;
11649             }
11650
11651             if(this.labellg > 0){
11652                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11653                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11654             }
11655
11656             if(this.labelmd > 0){
11657                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11658                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11659             }
11660
11661             if(this.labelsm > 0){
11662                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11663                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11664             }
11665
11666             if(this.labelxs > 0){
11667                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11668                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11669             }
11670             
11671         } else if ( this.fieldLabel.length) {
11672             cfg.cn = [
11673
11674                {
11675                    tag: 'label',
11676                    //cls : 'input-group-addon',
11677                    html : this.fieldLabel
11678
11679                },
11680
11681                inputblock
11682
11683            ];
11684
11685         } else {
11686
11687             cfg.cn = [
11688
11689                 inputblock
11690
11691             ];
11692                 
11693         }
11694         
11695         if (this.disabled) {
11696             input.disabled=true;
11697         }
11698         
11699         return cfg;
11700         
11701     },
11702     /**
11703      * return the real textarea element.
11704      */
11705     inputEl: function ()
11706     {
11707         return this.el.select('textarea.form-control',true).first();
11708     },
11709     
11710     /**
11711      * Clear any invalid styles/messages for this field
11712      */
11713     clearInvalid : function()
11714     {
11715         
11716         if(!this.el || this.preventMark){ // not rendered
11717             return;
11718         }
11719         
11720         var label = this.el.select('label', true).first();
11721         var icon = this.el.select('i.fa-star', true).first();
11722         
11723         if(label && icon){
11724             icon.remove();
11725         }
11726         this.el.removeClass( this.validClass);
11727         this.inputEl().removeClass('is-invalid');
11728          
11729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11730             
11731             var feedback = this.el.select('.form-control-feedback', true).first();
11732             
11733             if(feedback){
11734                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11735             }
11736             
11737         }
11738         
11739         this.fireEvent('valid', this);
11740     },
11741     
11742      /**
11743      * Mark this field as valid
11744      */
11745     markValid : function()
11746     {
11747         if(!this.el  || this.preventMark){ // not rendered
11748             return;
11749         }
11750         
11751         this.el.removeClass([this.invalidClass, this.validClass]);
11752         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11753         
11754         var feedback = this.el.select('.form-control-feedback', true).first();
11755             
11756         if(feedback){
11757             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11758         }
11759
11760         if(this.disabled || this.allowBlank){
11761             return;
11762         }
11763         
11764         var label = this.el.select('label', true).first();
11765         var icon = this.el.select('i.fa-star', true).first();
11766         
11767         if(label && icon){
11768             icon.remove();
11769         }
11770         if (Roo.bootstrap.version == 3) {
11771             this.el.addClass(this.validClass);
11772         } else {
11773             this.inputEl().addClass('is-valid');
11774         }
11775         
11776         
11777         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11778             
11779             var feedback = this.el.select('.form-control-feedback', true).first();
11780             
11781             if(feedback){
11782                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11783                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11784             }
11785             
11786         }
11787         
11788         this.fireEvent('valid', this);
11789     },
11790     
11791      /**
11792      * Mark this field as invalid
11793      * @param {String} msg The validation message
11794      */
11795     markInvalid : function(msg)
11796     {
11797         if(!this.el  || this.preventMark){ // not rendered
11798             return;
11799         }
11800         
11801         this.el.removeClass([this.invalidClass, this.validClass]);
11802         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11803         
11804         var feedback = this.el.select('.form-control-feedback', true).first();
11805             
11806         if(feedback){
11807             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11808         }
11809
11810         if(this.disabled || this.allowBlank){
11811             return;
11812         }
11813         
11814         var label = this.el.select('label', true).first();
11815         var icon = this.el.select('i.fa-star', true).first();
11816         
11817         if(!this.getValue().length && label && !icon){
11818             this.el.createChild({
11819                 tag : 'i',
11820                 cls : 'text-danger fa fa-lg fa-star',
11821                 tooltip : 'This field is required',
11822                 style : 'margin-right:5px;'
11823             }, label, true);
11824         }
11825         
11826         if (Roo.bootstrap.version == 3) {
11827             this.el.addClass(this.invalidClass);
11828         } else {
11829             this.inputEl().addClass('is-invalid');
11830         }
11831         
11832         // fixme ... this may be depricated need to test..
11833         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11834             
11835             var feedback = this.el.select('.form-control-feedback', true).first();
11836             
11837             if(feedback){
11838                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11839                 
11840                 if(this.getValue().length || this.forceFeedback){
11841                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11842                 }
11843                 
11844             }
11845             
11846         }
11847         
11848         this.fireEvent('invalid', this, msg);
11849     }
11850 });
11851
11852  
11853 /*
11854  * - LGPL
11855  *
11856  * trigger field - base class for combo..
11857  * 
11858  */
11859  
11860 /**
11861  * @class Roo.bootstrap.TriggerField
11862  * @extends Roo.bootstrap.Input
11863  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11864  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11865  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11866  * for which you can provide a custom implementation.  For example:
11867  * <pre><code>
11868 var trigger = new Roo.bootstrap.TriggerField();
11869 trigger.onTriggerClick = myTriggerFn;
11870 trigger.applyTo('my-field');
11871 </code></pre>
11872  *
11873  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11874  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11875  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11876  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11877  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11878
11879  * @constructor
11880  * Create a new TriggerField.
11881  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11882  * to the base TextField)
11883  */
11884 Roo.bootstrap.TriggerField = function(config){
11885     this.mimicing = false;
11886     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11887 };
11888
11889 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11890     /**
11891      * @cfg {String} triggerClass A CSS class to apply to the trigger
11892      */
11893      /**
11894      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11895      */
11896     hideTrigger:false,
11897
11898     /**
11899      * @cfg {Boolean} removable (true|false) special filter default false
11900      */
11901     removable : false,
11902     
11903     /** @cfg {Boolean} grow @hide */
11904     /** @cfg {Number} growMin @hide */
11905     /** @cfg {Number} growMax @hide */
11906
11907     /**
11908      * @hide 
11909      * @method
11910      */
11911     autoSize: Roo.emptyFn,
11912     // private
11913     monitorTab : true,
11914     // private
11915     deferHeight : true,
11916
11917     
11918     actionMode : 'wrap',
11919     
11920     caret : false,
11921     
11922     
11923     getAutoCreate : function(){
11924        
11925         var align = this.labelAlign || this.parentLabelAlign();
11926         
11927         var id = Roo.id();
11928         
11929         var cfg = {
11930             cls: 'form-group' //input-group
11931         };
11932         
11933         
11934         var input =  {
11935             tag: 'input',
11936             id : id,
11937             type : this.inputType,
11938             cls : 'form-control',
11939             autocomplete: 'new-password',
11940             placeholder : this.placeholder || '' 
11941             
11942         };
11943         if (this.name) {
11944             input.name = this.name;
11945         }
11946         if (this.size) {
11947             input.cls += ' input-' + this.size;
11948         }
11949         
11950         if (this.disabled) {
11951             input.disabled=true;
11952         }
11953         
11954         var inputblock = input;
11955         
11956         if(this.hasFeedback && !this.allowBlank){
11957             
11958             var feedback = {
11959                 tag: 'span',
11960                 cls: 'glyphicon form-control-feedback'
11961             };
11962             
11963             if(this.removable && !this.editable  ){
11964                 inputblock = {
11965                     cls : 'has-feedback',
11966                     cn :  [
11967                         inputblock,
11968                         {
11969                             tag: 'button',
11970                             html : 'x',
11971                             cls : 'roo-combo-removable-btn close'
11972                         },
11973                         feedback
11974                     ] 
11975                 };
11976             } else {
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         feedback
11982                     ] 
11983                 };
11984             }
11985
11986         } else {
11987             if(this.removable && !this.editable ){
11988                 inputblock = {
11989                     cls : 'roo-removable',
11990                     cn :  [
11991                         inputblock,
11992                         {
11993                             tag: 'button',
11994                             html : 'x',
11995                             cls : 'roo-combo-removable-btn close'
11996                         }
11997                     ] 
11998                 };
11999             }
12000         }
12001         
12002         if (this.before || this.after) {
12003             
12004             inputblock = {
12005                 cls : 'input-group',
12006                 cn :  [] 
12007             };
12008             if (this.before) {
12009                 inputblock.cn.push({
12010                     tag :'span',
12011                     cls : 'input-group-addon input-group-prepend input-group-text',
12012                     html : this.before
12013                 });
12014             }
12015             
12016             inputblock.cn.push(input);
12017             
12018             if(this.hasFeedback && !this.allowBlank){
12019                 inputblock.cls += ' has-feedback';
12020                 inputblock.cn.push(feedback);
12021             }
12022             
12023             if (this.after) {
12024                 inputblock.cn.push({
12025                     tag :'span',
12026                     cls : 'input-group-addon input-group-append input-group-text',
12027                     html : this.after
12028                 });
12029             }
12030             
12031         };
12032         
12033       
12034         
12035         var ibwrap = inputblock;
12036         
12037         if(this.multiple){
12038             ibwrap = {
12039                 tag: 'ul',
12040                 cls: 'roo-select2-choices',
12041                 cn:[
12042                     {
12043                         tag: 'li',
12044                         cls: 'roo-select2-search-field',
12045                         cn: [
12046
12047                             inputblock
12048                         ]
12049                     }
12050                 ]
12051             };
12052                 
12053         }
12054         
12055         var combobox = {
12056             cls: 'roo-select2-container input-group',
12057             cn: [
12058                  {
12059                     tag: 'input',
12060                     type : 'hidden',
12061                     cls: 'form-hidden-field'
12062                 },
12063                 ibwrap
12064             ]
12065         };
12066         
12067         if(!this.multiple && this.showToggleBtn){
12068             
12069             var caret = {
12070                         tag: 'span',
12071                         cls: 'caret'
12072              };
12073             if (this.caret != false) {
12074                 caret = {
12075                      tag: 'i',
12076                      cls: 'fa fa-' + this.caret
12077                 };
12078                 
12079             }
12080             
12081             combobox.cn.push({
12082                 tag :'span',
12083                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12084                 cn : [
12085                     Roo.bootstrap.version == 3 ? caret : '',
12086                     {
12087                         tag: 'span',
12088                         cls: 'combobox-clear',
12089                         cn  : [
12090                             {
12091                                 tag : 'i',
12092                                 cls: 'icon-remove'
12093                             }
12094                         ]
12095                     }
12096                 ]
12097
12098             })
12099         }
12100         
12101         if(this.multiple){
12102             combobox.cls += ' roo-select2-container-multi';
12103         }
12104          var indicator = {
12105             tag : 'i',
12106             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12107             tooltip : 'This field is required'
12108         };
12109         if (Roo.bootstrap.version == 4) {
12110             indicator = {
12111                 tag : 'i',
12112                 style : 'display:none'
12113             };
12114         }
12115         
12116         
12117         if (align ==='left' && this.fieldLabel.length) {
12118             
12119             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12120
12121             cfg.cn = [
12122                 indicator,
12123                 {
12124                     tag: 'label',
12125                     'for' :  id,
12126                     cls : 'control-label',
12127                     html : this.fieldLabel
12128
12129                 },
12130                 {
12131                     cls : "", 
12132                     cn: [
12133                         combobox
12134                     ]
12135                 }
12136
12137             ];
12138             
12139             var labelCfg = cfg.cn[1];
12140             var contentCfg = cfg.cn[2];
12141             
12142             if(this.indicatorpos == 'right'){
12143                 cfg.cn = [
12144                     {
12145                         tag: 'label',
12146                         'for' :  id,
12147                         cls : 'control-label',
12148                         cn : [
12149                             {
12150                                 tag : 'span',
12151                                 html : this.fieldLabel
12152                             },
12153                             indicator
12154                         ]
12155                     },
12156                     {
12157                         cls : "", 
12158                         cn: [
12159                             combobox
12160                         ]
12161                     }
12162
12163                 ];
12164                 
12165                 labelCfg = cfg.cn[0];
12166                 contentCfg = cfg.cn[1];
12167             }
12168             
12169             if(this.labelWidth > 12){
12170                 labelCfg.style = "width: " + this.labelWidth + 'px';
12171             }
12172             
12173             if(this.labelWidth < 13 && this.labelmd == 0){
12174                 this.labelmd = this.labelWidth;
12175             }
12176             
12177             if(this.labellg > 0){
12178                 labelCfg.cls += ' col-lg-' + this.labellg;
12179                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12180             }
12181             
12182             if(this.labelmd > 0){
12183                 labelCfg.cls += ' col-md-' + this.labelmd;
12184                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12185             }
12186             
12187             if(this.labelsm > 0){
12188                 labelCfg.cls += ' col-sm-' + this.labelsm;
12189                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12190             }
12191             
12192             if(this.labelxs > 0){
12193                 labelCfg.cls += ' col-xs-' + this.labelxs;
12194                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12195             }
12196             
12197         } else if ( this.fieldLabel.length) {
12198 //                Roo.log(" label");
12199             cfg.cn = [
12200                 indicator,
12201                {
12202                    tag: 'label',
12203                    //cls : 'input-group-addon',
12204                    html : this.fieldLabel
12205
12206                },
12207
12208                combobox
12209
12210             ];
12211             
12212             if(this.indicatorpos == 'right'){
12213                 
12214                 cfg.cn = [
12215                     {
12216                        tag: 'label',
12217                        cn : [
12218                            {
12219                                tag : 'span',
12220                                html : this.fieldLabel
12221                            },
12222                            indicator
12223                        ]
12224
12225                     },
12226                     combobox
12227
12228                 ];
12229
12230             }
12231
12232         } else {
12233             
12234 //                Roo.log(" no label && no align");
12235                 cfg = combobox
12236                      
12237                 
12238         }
12239         
12240         var settings=this;
12241         ['xs','sm','md','lg'].map(function(size){
12242             if (settings[size]) {
12243                 cfg.cls += ' col-' + size + '-' + settings[size];
12244             }
12245         });
12246         
12247         return cfg;
12248         
12249     },
12250     
12251     
12252     
12253     // private
12254     onResize : function(w, h){
12255 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12256 //        if(typeof w == 'number'){
12257 //            var x = w - this.trigger.getWidth();
12258 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12259 //            this.trigger.setStyle('left', x+'px');
12260 //        }
12261     },
12262
12263     // private
12264     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12265
12266     // private
12267     getResizeEl : function(){
12268         return this.inputEl();
12269     },
12270
12271     // private
12272     getPositionEl : function(){
12273         return this.inputEl();
12274     },
12275
12276     // private
12277     alignErrorIcon : function(){
12278         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12279     },
12280
12281     // private
12282     initEvents : function(){
12283         
12284         this.createList();
12285         
12286         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12287         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12288         if(!this.multiple && this.showToggleBtn){
12289             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12290             if(this.hideTrigger){
12291                 this.trigger.setDisplayed(false);
12292             }
12293             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12294         }
12295         
12296         if(this.multiple){
12297             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12298         }
12299         
12300         if(this.removable && !this.editable && !this.tickable){
12301             var close = this.closeTriggerEl();
12302             
12303             if(close){
12304                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12305                 close.on('click', this.removeBtnClick, this, close);
12306             }
12307         }
12308         
12309         //this.trigger.addClassOnOver('x-form-trigger-over');
12310         //this.trigger.addClassOnClick('x-form-trigger-click');
12311         
12312         //if(!this.width){
12313         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12314         //}
12315     },
12316     
12317     closeTriggerEl : function()
12318     {
12319         var close = this.el.select('.roo-combo-removable-btn', true).first();
12320         return close ? close : false;
12321     },
12322     
12323     removeBtnClick : function(e, h, el)
12324     {
12325         e.preventDefault();
12326         
12327         if(this.fireEvent("remove", this) !== false){
12328             this.reset();
12329             this.fireEvent("afterremove", this)
12330         }
12331     },
12332     
12333     createList : function()
12334     {
12335         this.list = Roo.get(document.body).createChild({
12336             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12337             cls: 'typeahead typeahead-long dropdown-menu',
12338             style: 'display:none'
12339         });
12340         
12341         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12342         
12343     },
12344
12345     // private
12346     initTrigger : function(){
12347        
12348     },
12349
12350     // private
12351     onDestroy : function(){
12352         if(this.trigger){
12353             this.trigger.removeAllListeners();
12354           //  this.trigger.remove();
12355         }
12356         //if(this.wrap){
12357         //    this.wrap.remove();
12358         //}
12359         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12360     },
12361
12362     // private
12363     onFocus : function(){
12364         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12365         /*
12366         if(!this.mimicing){
12367             this.wrap.addClass('x-trigger-wrap-focus');
12368             this.mimicing = true;
12369             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12370             if(this.monitorTab){
12371                 this.el.on("keydown", this.checkTab, this);
12372             }
12373         }
12374         */
12375     },
12376
12377     // private
12378     checkTab : function(e){
12379         if(e.getKey() == e.TAB){
12380             this.triggerBlur();
12381         }
12382     },
12383
12384     // private
12385     onBlur : function(){
12386         // do nothing
12387     },
12388
12389     // private
12390     mimicBlur : function(e, t){
12391         /*
12392         if(!this.wrap.contains(t) && this.validateBlur()){
12393             this.triggerBlur();
12394         }
12395         */
12396     },
12397
12398     // private
12399     triggerBlur : function(){
12400         this.mimicing = false;
12401         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12402         if(this.monitorTab){
12403             this.el.un("keydown", this.checkTab, this);
12404         }
12405         //this.wrap.removeClass('x-trigger-wrap-focus');
12406         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12407     },
12408
12409     // private
12410     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12411     validateBlur : function(e, t){
12412         return true;
12413     },
12414
12415     // private
12416     onDisable : function(){
12417         this.inputEl().dom.disabled = true;
12418         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12419         //if(this.wrap){
12420         //    this.wrap.addClass('x-item-disabled');
12421         //}
12422     },
12423
12424     // private
12425     onEnable : function(){
12426         this.inputEl().dom.disabled = false;
12427         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12428         //if(this.wrap){
12429         //    this.el.removeClass('x-item-disabled');
12430         //}
12431     },
12432
12433     // private
12434     onShow : function(){
12435         var ae = this.getActionEl();
12436         
12437         if(ae){
12438             ae.dom.style.display = '';
12439             ae.dom.style.visibility = 'visible';
12440         }
12441     },
12442
12443     // private
12444     
12445     onHide : function(){
12446         var ae = this.getActionEl();
12447         ae.dom.style.display = 'none';
12448     },
12449
12450     /**
12451      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12452      * by an implementing function.
12453      * @method
12454      * @param {EventObject} e
12455      */
12456     onTriggerClick : Roo.emptyFn
12457 });
12458  
12459 /*
12460 * Licence: LGPL
12461 */
12462
12463 /**
12464  * @class Roo.bootstrap.CardUploader
12465  * @extends Roo.bootstrap.Button
12466  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12467  * @cfg {Number} errorTimeout default 3000
12468  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12469  * @cfg {Array}  html The button text.
12470
12471  *
12472  * @constructor
12473  * Create a new CardUploader
12474  * @param {Object} config The config object
12475  */
12476
12477 Roo.bootstrap.CardUploader = function(config){
12478     
12479  
12480     
12481     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12482     
12483     
12484     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12485         return r.data.id
12486         });
12487     
12488     
12489 };
12490
12491 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12492     
12493      
12494     errorTimeout : 3000,
12495      
12496     images : false,
12497    
12498     fileCollection : false,
12499     allowBlank : true,
12500     
12501     getAutoCreate : function()
12502     {
12503         
12504         var cfg =  {
12505             cls :'form-group' ,
12506             cn : [
12507                
12508                 {
12509                     tag: 'label',
12510                    //cls : 'input-group-addon',
12511                     html : this.fieldLabel
12512
12513                 },
12514
12515                 {
12516                     tag: 'input',
12517                     type : 'hidden',
12518                     value : this.value,
12519                     cls : 'd-none  form-control'
12520                 },
12521                 
12522                 {
12523                     tag: 'input',
12524                     multiple : 'multiple',
12525                     type : 'file',
12526                     cls : 'd-none  roo-card-upload-selector'
12527                 },
12528                 
12529                 {
12530                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12531                 },
12532                 {
12533                     cls : 'card-columns roo-card-uploader-container'
12534                 }
12535
12536             ]
12537         };
12538            
12539          
12540         return cfg;
12541     },
12542     
12543     getChildContainer : function() /// what children are added to.
12544     {
12545         return this.containerEl;
12546     },
12547    
12548     getButtonContainer : function() /// what children are added to.
12549     {
12550         return this.el.select(".roo-card-uploader-button-container").first();
12551     },
12552    
12553     initEvents : function()
12554     {
12555         
12556         Roo.bootstrap.Input.prototype.initEvents.call(this);
12557         
12558         var t = this;
12559         this.addxtype({
12560             xns: Roo.bootstrap,
12561
12562             xtype : 'Button',
12563             container_method : 'getButtonContainer' ,            
12564             html :  this.html, // fix changable?
12565             cls : 'w-100 ',
12566             listeners : {
12567                 'click' : function(btn, e) {
12568                     t.onClick(e);
12569                 }
12570             }
12571         });
12572         
12573         
12574         
12575         
12576         this.urlAPI = (window.createObjectURL && window) || 
12577                                 (window.URL && URL.revokeObjectURL && URL) || 
12578                                 (window.webkitURL && webkitURL);
12579                         
12580          
12581          
12582          
12583         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12584         
12585         this.selectorEl.on('change', this.onFileSelected, this);
12586         if (this.images) {
12587             var t = this;
12588             this.images.forEach(function(img) {
12589                 t.addCard(img)
12590             });
12591             this.images = false;
12592         }
12593         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12594          
12595        
12596     },
12597     
12598    
12599     onClick : function(e)
12600     {
12601         e.preventDefault();
12602          
12603         this.selectorEl.dom.click();
12604          
12605     },
12606     
12607     onFileSelected : function(e)
12608     {
12609         e.preventDefault();
12610         
12611         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12612             return;
12613         }
12614         
12615         Roo.each(this.selectorEl.dom.files, function(file){    
12616             this.addFile(file);
12617         }, this);
12618          
12619     },
12620     
12621       
12622     
12623       
12624     
12625     addFile : function(file)
12626     {
12627            
12628         if(typeof(file) === 'string'){
12629             throw "Add file by name?"; // should not happen
12630             return;
12631         }
12632         
12633         if(!file || !this.urlAPI){
12634             return;
12635         }
12636         
12637         // file;
12638         // file.type;
12639         
12640         var _this = this;
12641         
12642         
12643         var url = _this.urlAPI.createObjectURL( file);
12644            
12645         this.addCard({
12646             id : Roo.bootstrap.CardUploader.ID--,
12647             is_uploaded : false,
12648             src : url,
12649             title : file.name,
12650             mimetype : file.type,
12651             preview : false,
12652             is_deleted : 0
12653         })
12654         
12655     },
12656     
12657     addCard : function (data)
12658     {
12659         // hidden input element?
12660         // if the file is not an image...
12661         //then we need to use something other that and header_image
12662         var t = this;
12663         //   remove.....
12664         var footer = [
12665             {
12666                 xns : Roo.bootstrap,
12667                 xtype : 'CardFooter',
12668                 items: [
12669                     {
12670                         xns : Roo.bootstrap,
12671                         xtype : 'Element',
12672                         cls : 'd-flex',
12673                         items : [
12674                             
12675                             {
12676                                 xns : Roo.bootstrap,
12677                                 xtype : 'Button',
12678                                 html : String.format("<small>{0}</small>", data.title),
12679                                 cls : 'col-11 text-left',
12680                                 size: 'sm',
12681                                 weight: 'link',
12682                                 fa : 'download',
12683                                 listeners : {
12684                                     click : function() {
12685                                         this.downloadCard(data.id)
12686                                     }
12687                                 }
12688                             },
12689                           
12690                             {
12691                                 xns : Roo.bootstrap,
12692                                 xtype : 'Button',
12693                                 
12694                                 size : 'sm',
12695                                 weight: 'danger',
12696                                 cls : 'col-1',
12697                                 fa : 'times',
12698                                 listeners : {
12699                                     click : function() {
12700                                         t.removeCard(data.id)
12701                                     }
12702                                 }
12703                             }
12704                         ]
12705                     }
12706                     
12707                 ] 
12708             }
12709             
12710         ];
12711
12712         var cn = this.addxtype(
12713             {
12714                  
12715                 xns : Roo.bootstrap,
12716                 xtype : 'Card',
12717                 closeable : true,
12718                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12719                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12720                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12721                 data : data,
12722                 html : false,
12723                  
12724                 items : footer,
12725                 initEvents : function() {
12726                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12727                     this.imgEl = this.el.select('.card-img-top').first();
12728                     if (this.imgEl) {
12729                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12730                         this.imgEl.set({ 'pointer' : 'cursor' });
12731                                   
12732                     }
12733                     
12734                   
12735                 }
12736                 
12737             }
12738         );
12739         // dont' really need ot update items.
12740         // this.items.push(cn);
12741         this.fileCollection.add(cn);
12742         this.updateInput();
12743         
12744     },
12745     removeCard : function(id)
12746     {
12747         
12748         var card  = this.fileCollection.get(id);
12749         card.data.is_deleted = 1;
12750         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12751         this.fileCollection.remove(card);
12752         //this.items = this.items.filter(function(e) { return e != card });
12753         // dont' really need ot update items.
12754         card.el.dom.parentNode.removeChild(card.el.dom);
12755         
12756     },
12757     reset: function()
12758     {
12759         this.fileCollection.each(function(card) {
12760             card.el.dom.parentNode.removeChild(card.el.dom);    
12761         });
12762         this.fileCollection.clear();
12763         this.updateInput();
12764     },
12765     
12766     updateInput : function()
12767     {
12768         var data = [];
12769         this.fileCollection.each(function(e) {
12770             data.push(e.data);
12771         });
12772         
12773         this.inputEl().dom.value = JSON.stringify(data);
12774     }
12775     
12776     
12777 });
12778
12779
12780 Roo.bootstrap.CardUploader.ID = -1;/*
12781  * Based on:
12782  * Ext JS Library 1.1.1
12783  * Copyright(c) 2006-2007, Ext JS, LLC.
12784  *
12785  * Originally Released Under LGPL - original licence link has changed is not relivant.
12786  *
12787  * Fork - LGPL
12788  * <script type="text/javascript">
12789  */
12790
12791
12792 /**
12793  * @class Roo.data.SortTypes
12794  * @singleton
12795  * Defines the default sorting (casting?) comparison functions used when sorting data.
12796  */
12797 Roo.data.SortTypes = {
12798     /**
12799      * Default sort that does nothing
12800      * @param {Mixed} s The value being converted
12801      * @return {Mixed} The comparison value
12802      */
12803     none : function(s){
12804         return s;
12805     },
12806     
12807     /**
12808      * The regular expression used to strip tags
12809      * @type {RegExp}
12810      * @property
12811      */
12812     stripTagsRE : /<\/?[^>]+>/gi,
12813     
12814     /**
12815      * Strips all HTML tags to sort on text only
12816      * @param {Mixed} s The value being converted
12817      * @return {String} The comparison value
12818      */
12819     asText : function(s){
12820         return String(s).replace(this.stripTagsRE, "");
12821     },
12822     
12823     /**
12824      * Strips all HTML tags to sort on text only - Case insensitive
12825      * @param {Mixed} s The value being converted
12826      * @return {String} The comparison value
12827      */
12828     asUCText : function(s){
12829         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12830     },
12831     
12832     /**
12833      * Case insensitive string
12834      * @param {Mixed} s The value being converted
12835      * @return {String} The comparison value
12836      */
12837     asUCString : function(s) {
12838         return String(s).toUpperCase();
12839     },
12840     
12841     /**
12842      * Date sorting
12843      * @param {Mixed} s The value being converted
12844      * @return {Number} The comparison value
12845      */
12846     asDate : function(s) {
12847         if(!s){
12848             return 0;
12849         }
12850         if(s instanceof Date){
12851             return s.getTime();
12852         }
12853         return Date.parse(String(s));
12854     },
12855     
12856     /**
12857      * Float sorting
12858      * @param {Mixed} s The value being converted
12859      * @return {Float} The comparison value
12860      */
12861     asFloat : function(s) {
12862         var val = parseFloat(String(s).replace(/,/g, ""));
12863         if(isNaN(val)) {
12864             val = 0;
12865         }
12866         return val;
12867     },
12868     
12869     /**
12870      * Integer sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Number} The comparison value
12873      */
12874     asInt : function(s) {
12875         var val = parseInt(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     }
12881 };/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891
12892 /**
12893 * @class Roo.data.Record
12894  * Instances of this class encapsulate both record <em>definition</em> information, and record
12895  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12896  * to access Records cached in an {@link Roo.data.Store} object.<br>
12897  * <p>
12898  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12899  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12900  * objects.<br>
12901  * <p>
12902  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12903  * @constructor
12904  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12905  * {@link #create}. The parameters are the same.
12906  * @param {Array} data An associative Array of data values keyed by the field name.
12907  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12908  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12909  * not specified an integer id is generated.
12910  */
12911 Roo.data.Record = function(data, id){
12912     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12913     this.data = data;
12914 };
12915
12916 /**
12917  * Generate a constructor for a specific record layout.
12918  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12919  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12920  * Each field definition object may contain the following properties: <ul>
12921  * <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,
12922  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12923  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12924  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12925  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12926  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12927  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12928  * this may be omitted.</p></li>
12929  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12930  * <ul><li>auto (Default, implies no conversion)</li>
12931  * <li>string</li>
12932  * <li>int</li>
12933  * <li>float</li>
12934  * <li>boolean</li>
12935  * <li>date</li></ul></p></li>
12936  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12937  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12938  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12939  * by the Reader into an object that will be stored in the Record. It is passed the
12940  * following parameters:<ul>
12941  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12942  * </ul></p></li>
12943  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12944  * </ul>
12945  * <br>usage:<br><pre><code>
12946 var TopicRecord = Roo.data.Record.create(
12947     {name: 'title', mapping: 'topic_title'},
12948     {name: 'author', mapping: 'username'},
12949     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12950     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12951     {name: 'lastPoster', mapping: 'user2'},
12952     {name: 'excerpt', mapping: 'post_text'}
12953 );
12954
12955 var myNewRecord = new TopicRecord({
12956     title: 'Do my job please',
12957     author: 'noobie',
12958     totalPosts: 1,
12959     lastPost: new Date(),
12960     lastPoster: 'Animal',
12961     excerpt: 'No way dude!'
12962 });
12963 myStore.add(myNewRecord);
12964 </code></pre>
12965  * @method create
12966  * @static
12967  */
12968 Roo.data.Record.create = function(o){
12969     var f = function(){
12970         f.superclass.constructor.apply(this, arguments);
12971     };
12972     Roo.extend(f, Roo.data.Record);
12973     var p = f.prototype;
12974     p.fields = new Roo.util.MixedCollection(false, function(field){
12975         return field.name;
12976     });
12977     for(var i = 0, len = o.length; i < len; i++){
12978         p.fields.add(new Roo.data.Field(o[i]));
12979     }
12980     f.getField = function(name){
12981         return p.fields.get(name);  
12982     };
12983     return f;
12984 };
12985
12986 Roo.data.Record.AUTO_ID = 1000;
12987 Roo.data.Record.EDIT = 'edit';
12988 Roo.data.Record.REJECT = 'reject';
12989 Roo.data.Record.COMMIT = 'commit';
12990
12991 Roo.data.Record.prototype = {
12992     /**
12993      * Readonly flag - true if this record has been modified.
12994      * @type Boolean
12995      */
12996     dirty : false,
12997     editing : false,
12998     error: null,
12999     modified: null,
13000
13001     // private
13002     join : function(store){
13003         this.store = store;
13004     },
13005
13006     /**
13007      * Set the named field to the specified value.
13008      * @param {String} name The name of the field to set.
13009      * @param {Object} value The value to set the field to.
13010      */
13011     set : function(name, value){
13012         if(this.data[name] == value){
13013             return;
13014         }
13015         this.dirty = true;
13016         if(!this.modified){
13017             this.modified = {};
13018         }
13019         if(typeof this.modified[name] == 'undefined'){
13020             this.modified[name] = this.data[name];
13021         }
13022         this.data[name] = value;
13023         if(!this.editing && this.store){
13024             this.store.afterEdit(this);
13025         }       
13026     },
13027
13028     /**
13029      * Get the value of the named field.
13030      * @param {String} name The name of the field to get the value of.
13031      * @return {Object} The value of the field.
13032      */
13033     get : function(name){
13034         return this.data[name]; 
13035     },
13036
13037     // private
13038     beginEdit : function(){
13039         this.editing = true;
13040         this.modified = {}; 
13041     },
13042
13043     // private
13044     cancelEdit : function(){
13045         this.editing = false;
13046         delete this.modified;
13047     },
13048
13049     // private
13050     endEdit : function(){
13051         this.editing = false;
13052         if(this.dirty && this.store){
13053             this.store.afterEdit(this);
13054         }
13055     },
13056
13057     /**
13058      * Usually called by the {@link Roo.data.Store} which owns the Record.
13059      * Rejects all changes made to the Record since either creation, or the last commit operation.
13060      * Modified fields are reverted to their original values.
13061      * <p>
13062      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13063      * of reject operations.
13064      */
13065     reject : function(){
13066         var m = this.modified;
13067         for(var n in m){
13068             if(typeof m[n] != "function"){
13069                 this.data[n] = m[n];
13070             }
13071         }
13072         this.dirty = false;
13073         delete this.modified;
13074         this.editing = false;
13075         if(this.store){
13076             this.store.afterReject(this);
13077         }
13078     },
13079
13080     /**
13081      * Usually called by the {@link Roo.data.Store} which owns the Record.
13082      * Commits all changes made to the Record since either creation, or the last commit operation.
13083      * <p>
13084      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13085      * of commit operations.
13086      */
13087     commit : function(){
13088         this.dirty = false;
13089         delete this.modified;
13090         this.editing = false;
13091         if(this.store){
13092             this.store.afterCommit(this);
13093         }
13094     },
13095
13096     // private
13097     hasError : function(){
13098         return this.error != null;
13099     },
13100
13101     // private
13102     clearError : function(){
13103         this.error = null;
13104     },
13105
13106     /**
13107      * Creates a copy of this record.
13108      * @param {String} id (optional) A new record id if you don't want to use this record's id
13109      * @return {Record}
13110      */
13111     copy : function(newId) {
13112         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13113     }
13114 };/*
13115  * Based on:
13116  * Ext JS Library 1.1.1
13117  * Copyright(c) 2006-2007, Ext JS, LLC.
13118  *
13119  * Originally Released Under LGPL - original licence link has changed is not relivant.
13120  *
13121  * Fork - LGPL
13122  * <script type="text/javascript">
13123  */
13124
13125
13126
13127 /**
13128  * @class Roo.data.Store
13129  * @extends Roo.util.Observable
13130  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13131  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13132  * <p>
13133  * 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
13134  * has no knowledge of the format of the data returned by the Proxy.<br>
13135  * <p>
13136  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13137  * instances from the data object. These records are cached and made available through accessor functions.
13138  * @constructor
13139  * Creates a new Store.
13140  * @param {Object} config A config object containing the objects needed for the Store to access data,
13141  * and read the data into Records.
13142  */
13143 Roo.data.Store = function(config){
13144     this.data = new Roo.util.MixedCollection(false);
13145     this.data.getKey = function(o){
13146         return o.id;
13147     };
13148     this.baseParams = {};
13149     // private
13150     this.paramNames = {
13151         "start" : "start",
13152         "limit" : "limit",
13153         "sort" : "sort",
13154         "dir" : "dir",
13155         "multisort" : "_multisort"
13156     };
13157
13158     if(config && config.data){
13159         this.inlineData = config.data;
13160         delete config.data;
13161     }
13162
13163     Roo.apply(this, config);
13164     
13165     if(this.reader){ // reader passed
13166         this.reader = Roo.factory(this.reader, Roo.data);
13167         this.reader.xmodule = this.xmodule || false;
13168         if(!this.recordType){
13169             this.recordType = this.reader.recordType;
13170         }
13171         if(this.reader.onMetaChange){
13172             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13173         }
13174     }
13175
13176     if(this.recordType){
13177         this.fields = this.recordType.prototype.fields;
13178     }
13179     this.modified = [];
13180
13181     this.addEvents({
13182         /**
13183          * @event datachanged
13184          * Fires when the data cache has changed, and a widget which is using this Store
13185          * as a Record cache should refresh its view.
13186          * @param {Store} this
13187          */
13188         datachanged : true,
13189         /**
13190          * @event metachange
13191          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13192          * @param {Store} this
13193          * @param {Object} meta The JSON metadata
13194          */
13195         metachange : true,
13196         /**
13197          * @event add
13198          * Fires when Records have been added to the Store
13199          * @param {Store} this
13200          * @param {Roo.data.Record[]} records The array of Records added
13201          * @param {Number} index The index at which the record(s) were added
13202          */
13203         add : true,
13204         /**
13205          * @event remove
13206          * Fires when a Record has been removed from the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record} record The Record that was removed
13209          * @param {Number} index The index at which the record was removed
13210          */
13211         remove : true,
13212         /**
13213          * @event update
13214          * Fires when a Record has been updated
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was updated
13217          * @param {String} operation The update operation being performed.  Value may be one of:
13218          * <pre><code>
13219  Roo.data.Record.EDIT
13220  Roo.data.Record.REJECT
13221  Roo.data.Record.COMMIT
13222          * </code></pre>
13223          */
13224         update : true,
13225         /**
13226          * @event clear
13227          * Fires when the data cache has been cleared.
13228          * @param {Store} this
13229          */
13230         clear : true,
13231         /**
13232          * @event beforeload
13233          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13234          * the load action will be canceled.
13235          * @param {Store} this
13236          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13237          */
13238         beforeload : true,
13239         /**
13240          * @event beforeloadadd
13241          * Fires after a new set of Records has been loaded.
13242          * @param {Store} this
13243          * @param {Roo.data.Record[]} records The Records that were loaded
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeloadadd : true,
13247         /**
13248          * @event load
13249          * Fires after a new set of Records has been loaded, before they are added to the store.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          * @params {Object} return from reader
13254          */
13255         load : true,
13256         /**
13257          * @event loadexception
13258          * Fires if an exception occurs in the Proxy during loading.
13259          * Called with the signature of the Proxy's "loadexception" event.
13260          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13261          * 
13262          * @param {Proxy} 
13263          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13264          * @param {Object} load options 
13265          * @param {Object} jsonData from your request (normally this contains the Exception)
13266          */
13267         loadexception : true
13268     });
13269     
13270     if(this.proxy){
13271         this.proxy = Roo.factory(this.proxy, Roo.data);
13272         this.proxy.xmodule = this.xmodule || false;
13273         this.relayEvents(this.proxy,  ["loadexception"]);
13274     }
13275     this.sortToggle = {};
13276     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13277
13278     Roo.data.Store.superclass.constructor.call(this);
13279
13280     if(this.inlineData){
13281         this.loadData(this.inlineData);
13282         delete this.inlineData;
13283     }
13284 };
13285
13286 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13287      /**
13288     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13289     * without a remote query - used by combo/forms at present.
13290     */
13291     
13292     /**
13293     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13294     */
13295     /**
13296     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13297     */
13298     /**
13299     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13300     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13301     */
13302     /**
13303     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13304     * on any HTTP request
13305     */
13306     /**
13307     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13308     */
13309     /**
13310     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13311     */
13312     multiSort: false,
13313     /**
13314     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13315     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13316     */
13317     remoteSort : false,
13318
13319     /**
13320     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13321      * loaded or when a record is removed. (defaults to false).
13322     */
13323     pruneModifiedRecords : false,
13324
13325     // private
13326     lastOptions : null,
13327
13328     /**
13329      * Add Records to the Store and fires the add event.
13330      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13331      */
13332     add : function(records){
13333         records = [].concat(records);
13334         for(var i = 0, len = records.length; i < len; i++){
13335             records[i].join(this);
13336         }
13337         var index = this.data.length;
13338         this.data.addAll(records);
13339         this.fireEvent("add", this, records, index);
13340     },
13341
13342     /**
13343      * Remove a Record from the Store and fires the remove event.
13344      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13345      */
13346     remove : function(record){
13347         var index = this.data.indexOf(record);
13348         this.data.removeAt(index);
13349  
13350         if(this.pruneModifiedRecords){
13351             this.modified.remove(record);
13352         }
13353         this.fireEvent("remove", this, record, index);
13354     },
13355
13356     /**
13357      * Remove all Records from the Store and fires the clear event.
13358      */
13359     removeAll : function(){
13360         this.data.clear();
13361         if(this.pruneModifiedRecords){
13362             this.modified = [];
13363         }
13364         this.fireEvent("clear", this);
13365     },
13366
13367     /**
13368      * Inserts Records to the Store at the given index and fires the add event.
13369      * @param {Number} index The start index at which to insert the passed Records.
13370      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13371      */
13372     insert : function(index, records){
13373         records = [].concat(records);
13374         for(var i = 0, len = records.length; i < len; i++){
13375             this.data.insert(index, records[i]);
13376             records[i].join(this);
13377         }
13378         this.fireEvent("add", this, records, index);
13379     },
13380
13381     /**
13382      * Get the index within the cache of the passed Record.
13383      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13384      * @return {Number} The index of the passed Record. Returns -1 if not found.
13385      */
13386     indexOf : function(record){
13387         return this.data.indexOf(record);
13388     },
13389
13390     /**
13391      * Get the index within the cache of the Record with the passed id.
13392      * @param {String} id The id of the Record to find.
13393      * @return {Number} The index of the Record. Returns -1 if not found.
13394      */
13395     indexOfId : function(id){
13396         return this.data.indexOfKey(id);
13397     },
13398
13399     /**
13400      * Get the Record with the specified id.
13401      * @param {String} id The id of the Record to find.
13402      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13403      */
13404     getById : function(id){
13405         return this.data.key(id);
13406     },
13407
13408     /**
13409      * Get the Record at the specified index.
13410      * @param {Number} index The index of the Record to find.
13411      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13412      */
13413     getAt : function(index){
13414         return this.data.itemAt(index);
13415     },
13416
13417     /**
13418      * Returns a range of Records between specified indices.
13419      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13420      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13421      * @return {Roo.data.Record[]} An array of Records
13422      */
13423     getRange : function(start, end){
13424         return this.data.getRange(start, end);
13425     },
13426
13427     // private
13428     storeOptions : function(o){
13429         o = Roo.apply({}, o);
13430         delete o.callback;
13431         delete o.scope;
13432         this.lastOptions = o;
13433     },
13434
13435     /**
13436      * Loads the Record cache from the configured Proxy using the configured Reader.
13437      * <p>
13438      * If using remote paging, then the first load call must specify the <em>start</em>
13439      * and <em>limit</em> properties in the options.params property to establish the initial
13440      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13441      * <p>
13442      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13443      * and this call will return before the new data has been loaded. Perform any post-processing
13444      * in a callback function, or in a "load" event handler.</strong>
13445      * <p>
13446      * @param {Object} options An object containing properties which control loading options:<ul>
13447      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13448      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13449      * passed the following arguments:<ul>
13450      * <li>r : Roo.data.Record[]</li>
13451      * <li>options: Options object from the load call</li>
13452      * <li>success: Boolean success indicator</li></ul></li>
13453      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13454      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13455      * </ul>
13456      */
13457     load : function(options){
13458         options = options || {};
13459         if(this.fireEvent("beforeload", this, options) !== false){
13460             this.storeOptions(options);
13461             var p = Roo.apply(options.params || {}, this.baseParams);
13462             // if meta was not loaded from remote source.. try requesting it.
13463             if (!this.reader.metaFromRemote) {
13464                 p._requestMeta = 1;
13465             }
13466             if(this.sortInfo && this.remoteSort){
13467                 var pn = this.paramNames;
13468                 p[pn["sort"]] = this.sortInfo.field;
13469                 p[pn["dir"]] = this.sortInfo.direction;
13470             }
13471             if (this.multiSort) {
13472                 var pn = this.paramNames;
13473                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13474             }
13475             
13476             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13477         }
13478     },
13479
13480     /**
13481      * Reloads the Record cache from the configured Proxy using the configured Reader and
13482      * the options from the last load operation performed.
13483      * @param {Object} options (optional) An object containing properties which may override the options
13484      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13485      * the most recently used options are reused).
13486      */
13487     reload : function(options){
13488         this.load(Roo.applyIf(options||{}, this.lastOptions));
13489     },
13490
13491     // private
13492     // Called as a callback by the Reader during a load operation.
13493     loadRecords : function(o, options, success){
13494         if(!o || success === false){
13495             if(success !== false){
13496                 this.fireEvent("load", this, [], options, o);
13497             }
13498             if(options.callback){
13499                 options.callback.call(options.scope || this, [], options, false);
13500             }
13501             return;
13502         }
13503         // if data returned failure - throw an exception.
13504         if (o.success === false) {
13505             // show a message if no listener is registered.
13506             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13507                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13508             }
13509             // loadmask wil be hooked into this..
13510             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13511             return;
13512         }
13513         var r = o.records, t = o.totalRecords || r.length;
13514         
13515         this.fireEvent("beforeloadadd", this, r, options, o);
13516         
13517         if(!options || options.add !== true){
13518             if(this.pruneModifiedRecords){
13519                 this.modified = [];
13520             }
13521             for(var i = 0, len = r.length; i < len; i++){
13522                 r[i].join(this);
13523             }
13524             if(this.snapshot){
13525                 this.data = this.snapshot;
13526                 delete this.snapshot;
13527             }
13528             this.data.clear();
13529             this.data.addAll(r);
13530             this.totalLength = t;
13531             this.applySort();
13532             this.fireEvent("datachanged", this);
13533         }else{
13534             this.totalLength = Math.max(t, this.data.length+r.length);
13535             this.add(r);
13536         }
13537         
13538         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13539                 
13540             var e = new Roo.data.Record({});
13541
13542             e.set(this.parent.displayField, this.parent.emptyTitle);
13543             e.set(this.parent.valueField, '');
13544
13545             this.insert(0, e);
13546         }
13547             
13548         this.fireEvent("load", this, r, options, o);
13549         if(options.callback){
13550             options.callback.call(options.scope || this, r, options, true);
13551         }
13552     },
13553
13554
13555     /**
13556      * Loads data from a passed data block. A Reader which understands the format of the data
13557      * must have been configured in the constructor.
13558      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13559      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13560      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13561      */
13562     loadData : function(o, append){
13563         var r = this.reader.readRecords(o);
13564         this.loadRecords(r, {add: append}, true);
13565     },
13566     
13567      /**
13568      * using 'cn' the nested child reader read the child array into it's child stores.
13569      * @param {Object} rec The record with a 'children array
13570      */
13571     loadDataFromChildren : function(rec)
13572     {
13573         this.loadData(this.reader.toLoadData(rec));
13574     },
13575     
13576
13577     /**
13578      * Gets the number of cached records.
13579      * <p>
13580      * <em>If using paging, this may not be the total size of the dataset. If the data object
13581      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13582      * the data set size</em>
13583      */
13584     getCount : function(){
13585         return this.data.length || 0;
13586     },
13587
13588     /**
13589      * Gets the total number of records in the dataset as returned by the server.
13590      * <p>
13591      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13592      * the dataset size</em>
13593      */
13594     getTotalCount : function(){
13595         return this.totalLength || 0;
13596     },
13597
13598     /**
13599      * Returns the sort state of the Store as an object with two properties:
13600      * <pre><code>
13601  field {String} The name of the field by which the Records are sorted
13602  direction {String} The sort order, "ASC" or "DESC"
13603      * </code></pre>
13604      */
13605     getSortState : function(){
13606         return this.sortInfo;
13607     },
13608
13609     // private
13610     applySort : function(){
13611         if(this.sortInfo && !this.remoteSort){
13612             var s = this.sortInfo, f = s.field;
13613             var st = this.fields.get(f).sortType;
13614             var fn = function(r1, r2){
13615                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13616                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13617             };
13618             this.data.sort(s.direction, fn);
13619             if(this.snapshot && this.snapshot != this.data){
13620                 this.snapshot.sort(s.direction, fn);
13621             }
13622         }
13623     },
13624
13625     /**
13626      * Sets the default sort column and order to be used by the next load operation.
13627      * @param {String} fieldName The name of the field to sort by.
13628      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13629      */
13630     setDefaultSort : function(field, dir){
13631         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13632     },
13633
13634     /**
13635      * Sort the Records.
13636      * If remote sorting is used, the sort is performed on the server, and the cache is
13637      * reloaded. If local sorting is used, the cache is sorted internally.
13638      * @param {String} fieldName The name of the field to sort by.
13639      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13640      */
13641     sort : function(fieldName, dir){
13642         var f = this.fields.get(fieldName);
13643         if(!dir){
13644             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13645             
13646             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13647                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13648             }else{
13649                 dir = f.sortDir;
13650             }
13651         }
13652         this.sortToggle[f.name] = dir;
13653         this.sortInfo = {field: f.name, direction: dir};
13654         if(!this.remoteSort){
13655             this.applySort();
13656             this.fireEvent("datachanged", this);
13657         }else{
13658             this.load(this.lastOptions);
13659         }
13660     },
13661
13662     /**
13663      * Calls the specified function for each of the Records in the cache.
13664      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13665      * Returning <em>false</em> aborts and exits the iteration.
13666      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13667      */
13668     each : function(fn, scope){
13669         this.data.each(fn, scope);
13670     },
13671
13672     /**
13673      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13674      * (e.g., during paging).
13675      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13676      */
13677     getModifiedRecords : function(){
13678         return this.modified;
13679     },
13680
13681     // private
13682     createFilterFn : function(property, value, anyMatch){
13683         if(!value.exec){ // not a regex
13684             value = String(value);
13685             if(value.length == 0){
13686                 return false;
13687             }
13688             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13689         }
13690         return function(r){
13691             return value.test(r.data[property]);
13692         };
13693     },
13694
13695     /**
13696      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13697      * @param {String} property A field on your records
13698      * @param {Number} start The record index to start at (defaults to 0)
13699      * @param {Number} end The last record index to include (defaults to length - 1)
13700      * @return {Number} The sum
13701      */
13702     sum : function(property, start, end){
13703         var rs = this.data.items, v = 0;
13704         start = start || 0;
13705         end = (end || end === 0) ? end : rs.length-1;
13706
13707         for(var i = start; i <= end; i++){
13708             v += (rs[i].data[property] || 0);
13709         }
13710         return v;
13711     },
13712
13713     /**
13714      * Filter the records by a specified property.
13715      * @param {String} field A field on your records
13716      * @param {String/RegExp} value Either a string that the field
13717      * should start with or a RegExp to test against the field
13718      * @param {Boolean} anyMatch True to match any part not just the beginning
13719      */
13720     filter : function(property, value, anyMatch){
13721         var fn = this.createFilterFn(property, value, anyMatch);
13722         return fn ? this.filterBy(fn) : this.clearFilter();
13723     },
13724
13725     /**
13726      * Filter by a function. The specified function will be called with each
13727      * record in this data source. If the function returns true the record is included,
13728      * otherwise it is filtered.
13729      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13730      * @param {Object} scope (optional) The scope of the function (defaults to this)
13731      */
13732     filterBy : function(fn, scope){
13733         this.snapshot = this.snapshot || this.data;
13734         this.data = this.queryBy(fn, scope||this);
13735         this.fireEvent("datachanged", this);
13736     },
13737
13738     /**
13739      * Query the records by a specified property.
13740      * @param {String} field A field on your records
13741      * @param {String/RegExp} value Either a string that the field
13742      * should start with or a RegExp to test against the field
13743      * @param {Boolean} anyMatch True to match any part not just the beginning
13744      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13745      */
13746     query : function(property, value, anyMatch){
13747         var fn = this.createFilterFn(property, value, anyMatch);
13748         return fn ? this.queryBy(fn) : this.data.clone();
13749     },
13750
13751     /**
13752      * Query by a function. The specified function will be called with each
13753      * record in this data source. If the function returns true the record is included
13754      * in the results.
13755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13756      * @param {Object} scope (optional) The scope of the function (defaults to this)
13757       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      **/
13759     queryBy : function(fn, scope){
13760         var data = this.snapshot || this.data;
13761         return data.filterBy(fn, scope||this);
13762     },
13763
13764     /**
13765      * Collects unique values for a particular dataIndex from this store.
13766      * @param {String} dataIndex The property to collect
13767      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13768      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13769      * @return {Array} An array of the unique values
13770      **/
13771     collect : function(dataIndex, allowNull, bypassFilter){
13772         var d = (bypassFilter === true && this.snapshot) ?
13773                 this.snapshot.items : this.data.items;
13774         var v, sv, r = [], l = {};
13775         for(var i = 0, len = d.length; i < len; i++){
13776             v = d[i].data[dataIndex];
13777             sv = String(v);
13778             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13779                 l[sv] = true;
13780                 r[r.length] = v;
13781             }
13782         }
13783         return r;
13784     },
13785
13786     /**
13787      * Revert to a view of the Record cache with no filtering applied.
13788      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13789      */
13790     clearFilter : function(suppressEvent){
13791         if(this.snapshot && this.snapshot != this.data){
13792             this.data = this.snapshot;
13793             delete this.snapshot;
13794             if(suppressEvent !== true){
13795                 this.fireEvent("datachanged", this);
13796             }
13797         }
13798     },
13799
13800     // private
13801     afterEdit : function(record){
13802         if(this.modified.indexOf(record) == -1){
13803             this.modified.push(record);
13804         }
13805         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13806     },
13807     
13808     // private
13809     afterReject : function(record){
13810         this.modified.remove(record);
13811         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13812     },
13813
13814     // private
13815     afterCommit : function(record){
13816         this.modified.remove(record);
13817         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13818     },
13819
13820     /**
13821      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13822      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13823      */
13824     commitChanges : function(){
13825         var m = this.modified.slice(0);
13826         this.modified = [];
13827         for(var i = 0, len = m.length; i < len; i++){
13828             m[i].commit();
13829         }
13830     },
13831
13832     /**
13833      * Cancel outstanding changes on all changed records.
13834      */
13835     rejectChanges : function(){
13836         var m = this.modified.slice(0);
13837         this.modified = [];
13838         for(var i = 0, len = m.length; i < len; i++){
13839             m[i].reject();
13840         }
13841     },
13842
13843     onMetaChange : function(meta, rtype, o){
13844         this.recordType = rtype;
13845         this.fields = rtype.prototype.fields;
13846         delete this.snapshot;
13847         this.sortInfo = meta.sortInfo || this.sortInfo;
13848         this.modified = [];
13849         this.fireEvent('metachange', this, this.reader.meta);
13850     },
13851     
13852     moveIndex : function(data, type)
13853     {
13854         var index = this.indexOf(data);
13855         
13856         var newIndex = index + type;
13857         
13858         this.remove(data);
13859         
13860         this.insert(newIndex, data);
13861         
13862     }
13863 });/*
13864  * Based on:
13865  * Ext JS Library 1.1.1
13866  * Copyright(c) 2006-2007, Ext JS, LLC.
13867  *
13868  * Originally Released Under LGPL - original licence link has changed is not relivant.
13869  *
13870  * Fork - LGPL
13871  * <script type="text/javascript">
13872  */
13873
13874 /**
13875  * @class Roo.data.SimpleStore
13876  * @extends Roo.data.Store
13877  * Small helper class to make creating Stores from Array data easier.
13878  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13879  * @cfg {Array} fields An array of field definition objects, or field name strings.
13880  * @cfg {Object} an existing reader (eg. copied from another store)
13881  * @cfg {Array} data The multi-dimensional array of data
13882  * @constructor
13883  * @param {Object} config
13884  */
13885 Roo.data.SimpleStore = function(config)
13886 {
13887     Roo.data.SimpleStore.superclass.constructor.call(this, {
13888         isLocal : true,
13889         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13890                 id: config.id
13891             },
13892             Roo.data.Record.create(config.fields)
13893         ),
13894         proxy : new Roo.data.MemoryProxy(config.data)
13895     });
13896     this.load();
13897 };
13898 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13899  * Based on:
13900  * Ext JS Library 1.1.1
13901  * Copyright(c) 2006-2007, Ext JS, LLC.
13902  *
13903  * Originally Released Under LGPL - original licence link has changed is not relivant.
13904  *
13905  * Fork - LGPL
13906  * <script type="text/javascript">
13907  */
13908
13909 /**
13910 /**
13911  * @extends Roo.data.Store
13912  * @class Roo.data.JsonStore
13913  * Small helper class to make creating Stores for JSON data easier. <br/>
13914 <pre><code>
13915 var store = new Roo.data.JsonStore({
13916     url: 'get-images.php',
13917     root: 'images',
13918     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13919 });
13920 </code></pre>
13921  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13922  * JsonReader and HttpProxy (unless inline data is provided).</b>
13923  * @cfg {Array} fields An array of field definition objects, or field name strings.
13924  * @constructor
13925  * @param {Object} config
13926  */
13927 Roo.data.JsonStore = function(c){
13928     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13929         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13930         reader: new Roo.data.JsonReader(c, c.fields)
13931     }));
13932 };
13933 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13934  * Based on:
13935  * Ext JS Library 1.1.1
13936  * Copyright(c) 2006-2007, Ext JS, LLC.
13937  *
13938  * Originally Released Under LGPL - original licence link has changed is not relivant.
13939  *
13940  * Fork - LGPL
13941  * <script type="text/javascript">
13942  */
13943
13944  
13945 Roo.data.Field = function(config){
13946     if(typeof config == "string"){
13947         config = {name: config};
13948     }
13949     Roo.apply(this, config);
13950     
13951     if(!this.type){
13952         this.type = "auto";
13953     }
13954     
13955     var st = Roo.data.SortTypes;
13956     // named sortTypes are supported, here we look them up
13957     if(typeof this.sortType == "string"){
13958         this.sortType = st[this.sortType];
13959     }
13960     
13961     // set default sortType for strings and dates
13962     if(!this.sortType){
13963         switch(this.type){
13964             case "string":
13965                 this.sortType = st.asUCString;
13966                 break;
13967             case "date":
13968                 this.sortType = st.asDate;
13969                 break;
13970             default:
13971                 this.sortType = st.none;
13972         }
13973     }
13974
13975     // define once
13976     var stripRe = /[\$,%]/g;
13977
13978     // prebuilt conversion function for this field, instead of
13979     // switching every time we're reading a value
13980     if(!this.convert){
13981         var cv, dateFormat = this.dateFormat;
13982         switch(this.type){
13983             case "":
13984             case "auto":
13985             case undefined:
13986                 cv = function(v){ return v; };
13987                 break;
13988             case "string":
13989                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13990                 break;
13991             case "int":
13992                 cv = function(v){
13993                     return v !== undefined && v !== null && v !== '' ?
13994                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13995                     };
13996                 break;
13997             case "float":
13998                 cv = function(v){
13999                     return v !== undefined && v !== null && v !== '' ?
14000                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14001                     };
14002                 break;
14003             case "bool":
14004             case "boolean":
14005                 cv = function(v){ return v === true || v === "true" || v == 1; };
14006                 break;
14007             case "date":
14008                 cv = function(v){
14009                     if(!v){
14010                         return '';
14011                     }
14012                     if(v instanceof Date){
14013                         return v;
14014                     }
14015                     if(dateFormat){
14016                         if(dateFormat == "timestamp"){
14017                             return new Date(v*1000);
14018                         }
14019                         return Date.parseDate(v, dateFormat);
14020                     }
14021                     var parsed = Date.parse(v);
14022                     return parsed ? new Date(parsed) : null;
14023                 };
14024              break;
14025             
14026         }
14027         this.convert = cv;
14028     }
14029 };
14030
14031 Roo.data.Field.prototype = {
14032     dateFormat: null,
14033     defaultValue: "",
14034     mapping: null,
14035     sortType : null,
14036     sortDir : "ASC"
14037 };/*
14038  * Based on:
14039  * Ext JS Library 1.1.1
14040  * Copyright(c) 2006-2007, Ext JS, LLC.
14041  *
14042  * Originally Released Under LGPL - original licence link has changed is not relivant.
14043  *
14044  * Fork - LGPL
14045  * <script type="text/javascript">
14046  */
14047  
14048 // Base class for reading structured data from a data source.  This class is intended to be
14049 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14050
14051 /**
14052  * @class Roo.data.DataReader
14053  * Base class for reading structured data from a data source.  This class is intended to be
14054  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14055  */
14056
14057 Roo.data.DataReader = function(meta, recordType){
14058     
14059     this.meta = meta;
14060     
14061     this.recordType = recordType instanceof Array ? 
14062         Roo.data.Record.create(recordType) : recordType;
14063 };
14064
14065 Roo.data.DataReader.prototype = {
14066     
14067     
14068     readerType : 'Data',
14069      /**
14070      * Create an empty record
14071      * @param {Object} data (optional) - overlay some values
14072      * @return {Roo.data.Record} record created.
14073      */
14074     newRow :  function(d) {
14075         var da =  {};
14076         this.recordType.prototype.fields.each(function(c) {
14077             switch( c.type) {
14078                 case 'int' : da[c.name] = 0; break;
14079                 case 'date' : da[c.name] = new Date(); break;
14080                 case 'float' : da[c.name] = 0.0; break;
14081                 case 'boolean' : da[c.name] = false; break;
14082                 default : da[c.name] = ""; break;
14083             }
14084             
14085         });
14086         return new this.recordType(Roo.apply(da, d));
14087     }
14088     
14089     
14090 };/*
14091  * Based on:
14092  * Ext JS Library 1.1.1
14093  * Copyright(c) 2006-2007, Ext JS, LLC.
14094  *
14095  * Originally Released Under LGPL - original licence link has changed is not relivant.
14096  *
14097  * Fork - LGPL
14098  * <script type="text/javascript">
14099  */
14100
14101 /**
14102  * @class Roo.data.DataProxy
14103  * @extends Roo.data.Observable
14104  * This class is an abstract base class for implementations which provide retrieval of
14105  * unformatted data objects.<br>
14106  * <p>
14107  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14108  * (of the appropriate type which knows how to parse the data object) to provide a block of
14109  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14110  * <p>
14111  * Custom implementations must implement the load method as described in
14112  * {@link Roo.data.HttpProxy#load}.
14113  */
14114 Roo.data.DataProxy = function(){
14115     this.addEvents({
14116         /**
14117          * @event beforeload
14118          * Fires before a network request is made to retrieve a data object.
14119          * @param {Object} This DataProxy object.
14120          * @param {Object} params The params parameter to the load function.
14121          */
14122         beforeload : true,
14123         /**
14124          * @event load
14125          * Fires before the load method's callback is called.
14126          * @param {Object} This DataProxy object.
14127          * @param {Object} o The data object.
14128          * @param {Object} arg The callback argument object passed to the load function.
14129          */
14130         load : true,
14131         /**
14132          * @event loadexception
14133          * Fires if an Exception occurs during data retrieval.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          * @param {Object} e The Exception.
14138          */
14139         loadexception : true
14140     });
14141     Roo.data.DataProxy.superclass.constructor.call(this);
14142 };
14143
14144 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14145
14146     /**
14147      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14148      */
14149 /*
14150  * Based on:
14151  * Ext JS Library 1.1.1
14152  * Copyright(c) 2006-2007, Ext JS, LLC.
14153  *
14154  * Originally Released Under LGPL - original licence link has changed is not relivant.
14155  *
14156  * Fork - LGPL
14157  * <script type="text/javascript">
14158  */
14159 /**
14160  * @class Roo.data.MemoryProxy
14161  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14162  * to the Reader when its load method is called.
14163  * @constructor
14164  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14165  */
14166 Roo.data.MemoryProxy = function(data){
14167     if (data.data) {
14168         data = data.data;
14169     }
14170     Roo.data.MemoryProxy.superclass.constructor.call(this);
14171     this.data = data;
14172 };
14173
14174 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14175     
14176     /**
14177      * Load data from the requested source (in this case an in-memory
14178      * data object passed to the constructor), read the data object into
14179      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14180      * process that block using the passed callback.
14181      * @param {Object} params This parameter is not used by the MemoryProxy class.
14182      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14183      * object into a block of Roo.data.Records.
14184      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14185      * The function must be passed <ul>
14186      * <li>The Record block object</li>
14187      * <li>The "arg" argument from the load function</li>
14188      * <li>A boolean success indicator</li>
14189      * </ul>
14190      * @param {Object} scope The scope in which to call the callback
14191      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14192      */
14193     load : function(params, reader, callback, scope, arg){
14194         params = params || {};
14195         var result;
14196         try {
14197             result = reader.readRecords(params.data ? params.data :this.data);
14198         }catch(e){
14199             this.fireEvent("loadexception", this, arg, null, e);
14200             callback.call(scope, null, arg, false);
14201             return;
14202         }
14203         callback.call(scope, result, arg, true);
14204     },
14205     
14206     // private
14207     update : function(params, records){
14208         
14209     }
14210 });/*
14211  * Based on:
14212  * Ext JS Library 1.1.1
14213  * Copyright(c) 2006-2007, Ext JS, LLC.
14214  *
14215  * Originally Released Under LGPL - original licence link has changed is not relivant.
14216  *
14217  * Fork - LGPL
14218  * <script type="text/javascript">
14219  */
14220 /**
14221  * @class Roo.data.HttpProxy
14222  * @extends Roo.data.DataProxy
14223  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14224  * configured to reference a certain URL.<br><br>
14225  * <p>
14226  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14227  * from which the running page was served.<br><br>
14228  * <p>
14229  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14230  * <p>
14231  * Be aware that to enable the browser to parse an XML document, the server must set
14232  * the Content-Type header in the HTTP response to "text/xml".
14233  * @constructor
14234  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14235  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14236  * will be used to make the request.
14237  */
14238 Roo.data.HttpProxy = function(conn){
14239     Roo.data.HttpProxy.superclass.constructor.call(this);
14240     // is conn a conn config or a real conn?
14241     this.conn = conn;
14242     this.useAjax = !conn || !conn.events;
14243   
14244 };
14245
14246 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14247     // thse are take from connection...
14248     
14249     /**
14250      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14251      */
14252     /**
14253      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14254      * extra parameters to each request made by this object. (defaults to undefined)
14255      */
14256     /**
14257      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14258      *  to each request made by this object. (defaults to undefined)
14259      */
14260     /**
14261      * @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)
14262      */
14263     /**
14264      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14265      */
14266      /**
14267      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14268      * @type Boolean
14269      */
14270   
14271
14272     /**
14273      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14274      * @type Boolean
14275      */
14276     /**
14277      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14278      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14279      * a finer-grained basis than the DataProxy events.
14280      */
14281     getConnection : function(){
14282         return this.useAjax ? Roo.Ajax : this.conn;
14283     },
14284
14285     /**
14286      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14287      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14288      * process that block using the passed callback.
14289      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14290      * for the request to the remote server.
14291      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14292      * object into a block of Roo.data.Records.
14293      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14294      * The function must be passed <ul>
14295      * <li>The Record block object</li>
14296      * <li>The "arg" argument from the load function</li>
14297      * <li>A boolean success indicator</li>
14298      * </ul>
14299      * @param {Object} scope The scope in which to call the callback
14300      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14301      */
14302     load : function(params, reader, callback, scope, arg){
14303         if(this.fireEvent("beforeload", this, params) !== false){
14304             var  o = {
14305                 params : params || {},
14306                 request: {
14307                     callback : callback,
14308                     scope : scope,
14309                     arg : arg
14310                 },
14311                 reader: reader,
14312                 callback : this.loadResponse,
14313                 scope: this
14314             };
14315             if(this.useAjax){
14316                 Roo.applyIf(o, this.conn);
14317                 if(this.activeRequest){
14318                     Roo.Ajax.abort(this.activeRequest);
14319                 }
14320                 this.activeRequest = Roo.Ajax.request(o);
14321             }else{
14322                 this.conn.request(o);
14323             }
14324         }else{
14325             callback.call(scope||this, null, arg, false);
14326         }
14327     },
14328
14329     // private
14330     loadResponse : function(o, success, response){
14331         delete this.activeRequest;
14332         if(!success){
14333             this.fireEvent("loadexception", this, o, response);
14334             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14335             return;
14336         }
14337         var result;
14338         try {
14339             result = o.reader.read(response);
14340         }catch(e){
14341             this.fireEvent("loadexception", this, o, response, e);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         
14346         this.fireEvent("load", this, o, o.request.arg);
14347         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14348     },
14349
14350     // private
14351     update : function(dataSet){
14352
14353     },
14354
14355     // private
14356     updateResponse : function(dataSet){
14357
14358     }
14359 });/*
14360  * Based on:
14361  * Ext JS Library 1.1.1
14362  * Copyright(c) 2006-2007, Ext JS, LLC.
14363  *
14364  * Originally Released Under LGPL - original licence link has changed is not relivant.
14365  *
14366  * Fork - LGPL
14367  * <script type="text/javascript">
14368  */
14369
14370 /**
14371  * @class Roo.data.ScriptTagProxy
14372  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14373  * other than the originating domain of the running page.<br><br>
14374  * <p>
14375  * <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
14376  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14377  * <p>
14378  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14379  * source code that is used as the source inside a &lt;script> tag.<br><br>
14380  * <p>
14381  * In order for the browser to process the returned data, the server must wrap the data object
14382  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14383  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14384  * depending on whether the callback name was passed:
14385  * <p>
14386  * <pre><code>
14387 boolean scriptTag = false;
14388 String cb = request.getParameter("callback");
14389 if (cb != null) {
14390     scriptTag = true;
14391     response.setContentType("text/javascript");
14392 } else {
14393     response.setContentType("application/x-json");
14394 }
14395 Writer out = response.getWriter();
14396 if (scriptTag) {
14397     out.write(cb + "(");
14398 }
14399 out.print(dataBlock.toJsonString());
14400 if (scriptTag) {
14401     out.write(");");
14402 }
14403 </pre></code>
14404  *
14405  * @constructor
14406  * @param {Object} config A configuration object.
14407  */
14408 Roo.data.ScriptTagProxy = function(config){
14409     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14410     Roo.apply(this, config);
14411     this.head = document.getElementsByTagName("head")[0];
14412 };
14413
14414 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14415
14416 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14417     /**
14418      * @cfg {String} url The URL from which to request the data object.
14419      */
14420     /**
14421      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14422      */
14423     timeout : 30000,
14424     /**
14425      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14426      * the server the name of the callback function set up by the load call to process the returned data object.
14427      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14428      * javascript output which calls this named function passing the data object as its only parameter.
14429      */
14430     callbackParam : "callback",
14431     /**
14432      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14433      * name to the request.
14434      */
14435     nocache : true,
14436
14437     /**
14438      * Load data from the configured URL, read the data object into
14439      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14440      * process that block using the passed callback.
14441      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14442      * for the request to the remote server.
14443      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14444      * object into a block of Roo.data.Records.
14445      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14446      * The function must be passed <ul>
14447      * <li>The Record block object</li>
14448      * <li>The "arg" argument from the load function</li>
14449      * <li>A boolean success indicator</li>
14450      * </ul>
14451      * @param {Object} scope The scope in which to call the callback
14452      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14453      */
14454     load : function(params, reader, callback, scope, arg){
14455         if(this.fireEvent("beforeload", this, params) !== false){
14456
14457             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14458
14459             var url = this.url;
14460             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14461             if(this.nocache){
14462                 url += "&_dc=" + (new Date().getTime());
14463             }
14464             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14465             var trans = {
14466                 id : transId,
14467                 cb : "stcCallback"+transId,
14468                 scriptId : "stcScript"+transId,
14469                 params : params,
14470                 arg : arg,
14471                 url : url,
14472                 callback : callback,
14473                 scope : scope,
14474                 reader : reader
14475             };
14476             var conn = this;
14477
14478             window[trans.cb] = function(o){
14479                 conn.handleResponse(o, trans);
14480             };
14481
14482             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14483
14484             if(this.autoAbort !== false){
14485                 this.abort();
14486             }
14487
14488             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14489
14490             var script = document.createElement("script");
14491             script.setAttribute("src", url);
14492             script.setAttribute("type", "text/javascript");
14493             script.setAttribute("id", trans.scriptId);
14494             this.head.appendChild(script);
14495
14496             this.trans = trans;
14497         }else{
14498             callback.call(scope||this, null, arg, false);
14499         }
14500     },
14501
14502     // private
14503     isLoading : function(){
14504         return this.trans ? true : false;
14505     },
14506
14507     /**
14508      * Abort the current server request.
14509      */
14510     abort : function(){
14511         if(this.isLoading()){
14512             this.destroyTrans(this.trans);
14513         }
14514     },
14515
14516     // private
14517     destroyTrans : function(trans, isLoaded){
14518         this.head.removeChild(document.getElementById(trans.scriptId));
14519         clearTimeout(trans.timeoutId);
14520         if(isLoaded){
14521             window[trans.cb] = undefined;
14522             try{
14523                 delete window[trans.cb];
14524             }catch(e){}
14525         }else{
14526             // if hasn't been loaded, wait for load to remove it to prevent script error
14527             window[trans.cb] = function(){
14528                 window[trans.cb] = undefined;
14529                 try{
14530                     delete window[trans.cb];
14531                 }catch(e){}
14532             };
14533         }
14534     },
14535
14536     // private
14537     handleResponse : function(o, trans){
14538         this.trans = false;
14539         this.destroyTrans(trans, true);
14540         var result;
14541         try {
14542             result = trans.reader.readRecords(o);
14543         }catch(e){
14544             this.fireEvent("loadexception", this, o, trans.arg, e);
14545             trans.callback.call(trans.scope||window, null, trans.arg, false);
14546             return;
14547         }
14548         this.fireEvent("load", this, o, trans.arg);
14549         trans.callback.call(trans.scope||window, result, trans.arg, true);
14550     },
14551
14552     // private
14553     handleFailure : function(trans){
14554         this.trans = false;
14555         this.destroyTrans(trans, false);
14556         this.fireEvent("loadexception", this, null, trans.arg);
14557         trans.callback.call(trans.scope||window, null, trans.arg, false);
14558     }
14559 });/*
14560  * Based on:
14561  * Ext JS Library 1.1.1
14562  * Copyright(c) 2006-2007, Ext JS, LLC.
14563  *
14564  * Originally Released Under LGPL - original licence link has changed is not relivant.
14565  *
14566  * Fork - LGPL
14567  * <script type="text/javascript">
14568  */
14569
14570 /**
14571  * @class Roo.data.JsonReader
14572  * @extends Roo.data.DataReader
14573  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14574  * based on mappings in a provided Roo.data.Record constructor.
14575  * 
14576  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14577  * in the reply previously. 
14578  * 
14579  * <p>
14580  * Example code:
14581  * <pre><code>
14582 var RecordDef = Roo.data.Record.create([
14583     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14584     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14585 ]);
14586 var myReader = new Roo.data.JsonReader({
14587     totalProperty: "results",    // The property which contains the total dataset size (optional)
14588     root: "rows",                // The property which contains an Array of row objects
14589     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14590 }, RecordDef);
14591 </code></pre>
14592  * <p>
14593  * This would consume a JSON file like this:
14594  * <pre><code>
14595 { 'results': 2, 'rows': [
14596     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14597     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14598 }
14599 </code></pre>
14600  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14601  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14602  * paged from the remote server.
14603  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14604  * @cfg {String} root name of the property which contains the Array of row objects.
14605  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14606  * @cfg {Array} fields Array of field definition objects
14607  * @constructor
14608  * Create a new JsonReader
14609  * @param {Object} meta Metadata configuration options
14610  * @param {Object} recordType Either an Array of field definition objects,
14611  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14612  */
14613 Roo.data.JsonReader = function(meta, recordType){
14614     
14615     meta = meta || {};
14616     // set some defaults:
14617     Roo.applyIf(meta, {
14618         totalProperty: 'total',
14619         successProperty : 'success',
14620         root : 'data',
14621         id : 'id'
14622     });
14623     
14624     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14625 };
14626 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14627     
14628     readerType : 'Json',
14629     
14630     /**
14631      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14632      * Used by Store query builder to append _requestMeta to params.
14633      * 
14634      */
14635     metaFromRemote : false,
14636     /**
14637      * This method is only used by a DataProxy which has retrieved data from a remote server.
14638      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14639      * @return {Object} data A data block which is used by an Roo.data.Store object as
14640      * a cache of Roo.data.Records.
14641      */
14642     read : function(response){
14643         var json = response.responseText;
14644        
14645         var o = /* eval:var:o */ eval("("+json+")");
14646         if(!o) {
14647             throw {message: "JsonReader.read: Json object not found"};
14648         }
14649         
14650         if(o.metaData){
14651             
14652             delete this.ef;
14653             this.metaFromRemote = true;
14654             this.meta = o.metaData;
14655             this.recordType = Roo.data.Record.create(o.metaData.fields);
14656             this.onMetaChange(this.meta, this.recordType, o);
14657         }
14658         return this.readRecords(o);
14659     },
14660
14661     // private function a store will implement
14662     onMetaChange : function(meta, recordType, o){
14663
14664     },
14665
14666     /**
14667          * @ignore
14668          */
14669     simpleAccess: function(obj, subsc) {
14670         return obj[subsc];
14671     },
14672
14673         /**
14674          * @ignore
14675          */
14676     getJsonAccessor: function(){
14677         var re = /[\[\.]/;
14678         return function(expr) {
14679             try {
14680                 return(re.test(expr))
14681                     ? new Function("obj", "return obj." + expr)
14682                     : function(obj){
14683                         return obj[expr];
14684                     };
14685             } catch(e){}
14686             return Roo.emptyFn;
14687         };
14688     }(),
14689
14690     /**
14691      * Create a data block containing Roo.data.Records from an XML document.
14692      * @param {Object} o An object which contains an Array of row objects in the property specified
14693      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14694      * which contains the total size of the dataset.
14695      * @return {Object} data A data block which is used by an Roo.data.Store object as
14696      * a cache of Roo.data.Records.
14697      */
14698     readRecords : function(o){
14699         /**
14700          * After any data loads, the raw JSON data is available for further custom processing.
14701          * @type Object
14702          */
14703         this.o = o;
14704         var s = this.meta, Record = this.recordType,
14705             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14706
14707 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14708         if (!this.ef) {
14709             if(s.totalProperty) {
14710                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14711                 }
14712                 if(s.successProperty) {
14713                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14714                 }
14715                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14716                 if (s.id) {
14717                         var g = this.getJsonAccessor(s.id);
14718                         this.getId = function(rec) {
14719                                 var r = g(rec);  
14720                                 return (r === undefined || r === "") ? null : r;
14721                         };
14722                 } else {
14723                         this.getId = function(){return null;};
14724                 }
14725             this.ef = [];
14726             for(var jj = 0; jj < fl; jj++){
14727                 f = fi[jj];
14728                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14729                 this.ef[jj] = this.getJsonAccessor(map);
14730             }
14731         }
14732
14733         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14734         if(s.totalProperty){
14735             var vt = parseInt(this.getTotal(o), 10);
14736             if(!isNaN(vt)){
14737                 totalRecords = vt;
14738             }
14739         }
14740         if(s.successProperty){
14741             var vs = this.getSuccess(o);
14742             if(vs === false || vs === 'false'){
14743                 success = false;
14744             }
14745         }
14746         var records = [];
14747         for(var i = 0; i < c; i++){
14748                 var n = root[i];
14749             var values = {};
14750             var id = this.getId(n);
14751             for(var j = 0; j < fl; j++){
14752                 f = fi[j];
14753             var v = this.ef[j](n);
14754             if (!f.convert) {
14755                 Roo.log('missing convert for ' + f.name);
14756                 Roo.log(f);
14757                 continue;
14758             }
14759             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14760             }
14761             var record = new Record(values, id);
14762             record.json = n;
14763             records[i] = record;
14764         }
14765         return {
14766             raw : o,
14767             success : success,
14768             records : records,
14769             totalRecords : totalRecords
14770         };
14771     },
14772     // used when loading children.. @see loadDataFromChildren
14773     toLoadData: function(rec)
14774     {
14775         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14776         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14777         return { data : data, total : data.length };
14778         
14779     }
14780 });/*
14781  * Based on:
14782  * Ext JS Library 1.1.1
14783  * Copyright(c) 2006-2007, Ext JS, LLC.
14784  *
14785  * Originally Released Under LGPL - original licence link has changed is not relivant.
14786  *
14787  * Fork - LGPL
14788  * <script type="text/javascript">
14789  */
14790
14791 /**
14792  * @class Roo.data.ArrayReader
14793  * @extends Roo.data.DataReader
14794  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14795  * Each element of that Array represents a row of data fields. The
14796  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14797  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14798  * <p>
14799  * Example code:.
14800  * <pre><code>
14801 var RecordDef = Roo.data.Record.create([
14802     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14803     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14804 ]);
14805 var myReader = new Roo.data.ArrayReader({
14806     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14807 }, RecordDef);
14808 </code></pre>
14809  * <p>
14810  * This would consume an Array like this:
14811  * <pre><code>
14812 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14813   </code></pre>
14814  
14815  * @constructor
14816  * Create a new JsonReader
14817  * @param {Object} meta Metadata configuration options.
14818  * @param {Object|Array} recordType Either an Array of field definition objects
14819  * 
14820  * @cfg {Array} fields Array of field definition objects
14821  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14822  * as specified to {@link Roo.data.Record#create},
14823  * or an {@link Roo.data.Record} object
14824  *
14825  * 
14826  * created using {@link Roo.data.Record#create}.
14827  */
14828 Roo.data.ArrayReader = function(meta, recordType)
14829 {    
14830     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14831 };
14832
14833 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14834     
14835       /**
14836      * Create a data block containing Roo.data.Records from an XML document.
14837      * @param {Object} o An Array of row objects which represents the dataset.
14838      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14839      * a cache of Roo.data.Records.
14840      */
14841     readRecords : function(o)
14842     {
14843         var sid = this.meta ? this.meta.id : null;
14844         var recordType = this.recordType, fields = recordType.prototype.fields;
14845         var records = [];
14846         var root = o;
14847         for(var i = 0; i < root.length; i++){
14848                 var n = root[i];
14849             var values = {};
14850             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14851             for(var j = 0, jlen = fields.length; j < jlen; j++){
14852                 var f = fields.items[j];
14853                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14854                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14855                 v = f.convert(v);
14856                 values[f.name] = v;
14857             }
14858             var record = new recordType(values, id);
14859             record.json = n;
14860             records[records.length] = record;
14861         }
14862         return {
14863             records : records,
14864             totalRecords : records.length
14865         };
14866     },
14867     // used when loading children.. @see loadDataFromChildren
14868     toLoadData: function(rec)
14869     {
14870         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14871         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14872         
14873     }
14874     
14875     
14876 });/*
14877  * - LGPL
14878  * * 
14879  */
14880
14881 /**
14882  * @class Roo.bootstrap.ComboBox
14883  * @extends Roo.bootstrap.TriggerField
14884  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14885  * @cfg {Boolean} append (true|false) default false
14886  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14887  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14888  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14889  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14890  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14891  * @cfg {Boolean} animate default true
14892  * @cfg {Boolean} emptyResultText only for touch device
14893  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14894  * @cfg {String} emptyTitle default ''
14895  * @cfg {Number} width fixed with? experimental
14896  * @constructor
14897  * Create a new ComboBox.
14898  * @param {Object} config Configuration options
14899  */
14900 Roo.bootstrap.ComboBox = function(config){
14901     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14902     this.addEvents({
14903         /**
14904          * @event expand
14905          * Fires when the dropdown list is expanded
14906         * @param {Roo.bootstrap.ComboBox} combo This combo box
14907         */
14908         'expand' : true,
14909         /**
14910          * @event collapse
14911          * Fires when the dropdown list is collapsed
14912         * @param {Roo.bootstrap.ComboBox} combo This combo box
14913         */
14914         'collapse' : true,
14915         /**
14916          * @event beforeselect
14917          * Fires before a list item is selected. Return false to cancel the selection.
14918         * @param {Roo.bootstrap.ComboBox} combo This combo box
14919         * @param {Roo.data.Record} record The data record returned from the underlying store
14920         * @param {Number} index The index of the selected item in the dropdown list
14921         */
14922         'beforeselect' : true,
14923         /**
14924          * @event select
14925          * Fires when a list item is selected
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'select' : true,
14931         /**
14932          * @event beforequery
14933          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14934          * The event object passed has these properties:
14935         * @param {Roo.bootstrap.ComboBox} combo This combo box
14936         * @param {String} query The query
14937         * @param {Boolean} forceAll true to force "all" query
14938         * @param {Boolean} cancel true to cancel the query
14939         * @param {Object} e The query event object
14940         */
14941         'beforequery': true,
14942          /**
14943          * @event add
14944          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14945         * @param {Roo.bootstrap.ComboBox} combo This combo box
14946         */
14947         'add' : true,
14948         /**
14949          * @event edit
14950          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14951         * @param {Roo.bootstrap.ComboBox} combo This combo box
14952         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14953         */
14954         'edit' : true,
14955         /**
14956          * @event remove
14957          * Fires when the remove value from the combobox array
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'remove' : true,
14961         /**
14962          * @event afterremove
14963          * Fires when the remove value from the combobox array
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         */
14966         'afterremove' : true,
14967         /**
14968          * @event specialfilter
14969          * Fires when specialfilter
14970             * @param {Roo.bootstrap.ComboBox} combo This combo box
14971             */
14972         'specialfilter' : true,
14973         /**
14974          * @event tick
14975          * Fires when tick the element
14976             * @param {Roo.bootstrap.ComboBox} combo This combo box
14977             */
14978         'tick' : true,
14979         /**
14980          * @event touchviewdisplay
14981          * Fires when touch view require special display (default is using displayField)
14982             * @param {Roo.bootstrap.ComboBox} combo This combo box
14983             * @param {Object} cfg set html .
14984             */
14985         'touchviewdisplay' : true
14986         
14987     });
14988     
14989     this.item = [];
14990     this.tickItems = [];
14991     
14992     this.selectedIndex = -1;
14993     if(this.mode == 'local'){
14994         if(config.queryDelay === undefined){
14995             this.queryDelay = 10;
14996         }
14997         if(config.minChars === undefined){
14998             this.minChars = 0;
14999         }
15000     }
15001 };
15002
15003 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15004      
15005     /**
15006      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15007      * rendering into an Roo.Editor, defaults to false)
15008      */
15009     /**
15010      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15011      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15012      */
15013     /**
15014      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15015      */
15016     /**
15017      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15018      * the dropdown list (defaults to undefined, with no header element)
15019      */
15020
15021      /**
15022      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15023      */
15024      
15025      /**
15026      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15027      */
15028     listWidth: undefined,
15029     /**
15030      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15031      * mode = 'remote' or 'text' if mode = 'local')
15032      */
15033     displayField: undefined,
15034     
15035     /**
15036      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15037      * mode = 'remote' or 'value' if mode = 'local'). 
15038      * Note: use of a valueField requires the user make a selection
15039      * in order for a value to be mapped.
15040      */
15041     valueField: undefined,
15042     /**
15043      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15044      */
15045     modalTitle : '',
15046     
15047     /**
15048      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15049      * field's data value (defaults to the underlying DOM element's name)
15050      */
15051     hiddenName: undefined,
15052     /**
15053      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15054      */
15055     listClass: '',
15056     /**
15057      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15058      */
15059     selectedClass: 'active',
15060     
15061     /**
15062      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15063      */
15064     shadow:'sides',
15065     /**
15066      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15067      * anchor positions (defaults to 'tl-bl')
15068      */
15069     listAlign: 'tl-bl?',
15070     /**
15071      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15072      */
15073     maxHeight: 300,
15074     /**
15075      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15076      * query specified by the allQuery config option (defaults to 'query')
15077      */
15078     triggerAction: 'query',
15079     /**
15080      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15081      * (defaults to 4, does not apply if editable = false)
15082      */
15083     minChars : 4,
15084     /**
15085      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15086      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15087      */
15088     typeAhead: false,
15089     /**
15090      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15091      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15092      */
15093     queryDelay: 500,
15094     /**
15095      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15096      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15097      */
15098     pageSize: 0,
15099     /**
15100      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15101      * when editable = true (defaults to false)
15102      */
15103     selectOnFocus:false,
15104     /**
15105      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15106      */
15107     queryParam: 'query',
15108     /**
15109      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15110      * when mode = 'remote' (defaults to 'Loading...')
15111      */
15112     loadingText: 'Loading...',
15113     /**
15114      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15115      */
15116     resizable: false,
15117     /**
15118      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15119      */
15120     handleHeight : 8,
15121     /**
15122      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15123      * traditional select (defaults to true)
15124      */
15125     editable: true,
15126     /**
15127      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15128      */
15129     allQuery: '',
15130     /**
15131      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15132      */
15133     mode: 'remote',
15134     /**
15135      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15136      * listWidth has a higher value)
15137      */
15138     minListWidth : 70,
15139     /**
15140      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15141      * allow the user to set arbitrary text into the field (defaults to false)
15142      */
15143     forceSelection:false,
15144     /**
15145      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15146      * if typeAhead = true (defaults to 250)
15147      */
15148     typeAheadDelay : 250,
15149     /**
15150      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15151      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15152      */
15153     valueNotFoundText : undefined,
15154     /**
15155      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15156      */
15157     blockFocus : false,
15158     
15159     /**
15160      * @cfg {Boolean} disableClear Disable showing of clear button.
15161      */
15162     disableClear : false,
15163     /**
15164      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15165      */
15166     alwaysQuery : false,
15167     
15168     /**
15169      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15170      */
15171     multiple : false,
15172     
15173     /**
15174      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15175      */
15176     invalidClass : "has-warning",
15177     
15178     /**
15179      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15180      */
15181     validClass : "has-success",
15182     
15183     /**
15184      * @cfg {Boolean} specialFilter (true|false) special filter default false
15185      */
15186     specialFilter : false,
15187     
15188     /**
15189      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15190      */
15191     mobileTouchView : true,
15192     
15193     /**
15194      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15195      */
15196     useNativeIOS : false,
15197     
15198     /**
15199      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15200      */
15201     mobile_restrict_height : false,
15202     
15203     ios_options : false,
15204     
15205     //private
15206     addicon : false,
15207     editicon: false,
15208     
15209     page: 0,
15210     hasQuery: false,
15211     append: false,
15212     loadNext: false,
15213     autoFocus : true,
15214     tickable : false,
15215     btnPosition : 'right',
15216     triggerList : true,
15217     showToggleBtn : true,
15218     animate : true,
15219     emptyResultText: 'Empty',
15220     triggerText : 'Select',
15221     emptyTitle : '',
15222     width : false,
15223     
15224     // element that contains real text value.. (when hidden is used..)
15225     
15226     getAutoCreate : function()
15227     {   
15228         var cfg = false;
15229         //render
15230         /*
15231          * Render classic select for iso
15232          */
15233         
15234         if(Roo.isIOS && this.useNativeIOS){
15235             cfg = this.getAutoCreateNativeIOS();
15236             return cfg;
15237         }
15238         
15239         /*
15240          * Touch Devices
15241          */
15242         
15243         if(Roo.isTouch && this.mobileTouchView){
15244             cfg = this.getAutoCreateTouchView();
15245             return cfg;;
15246         }
15247         
15248         /*
15249          *  Normal ComboBox
15250          */
15251         if(!this.tickable){
15252             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15253             return cfg;
15254         }
15255         
15256         /*
15257          *  ComboBox with tickable selections
15258          */
15259              
15260         var align = this.labelAlign || this.parentLabelAlign();
15261         
15262         cfg = {
15263             cls : 'form-group roo-combobox-tickable' //input-group
15264         };
15265         
15266         var btn_text_select = '';
15267         var btn_text_done = '';
15268         var btn_text_cancel = '';
15269         
15270         if (this.btn_text_show) {
15271             btn_text_select = 'Select';
15272             btn_text_done = 'Done';
15273             btn_text_cancel = 'Cancel'; 
15274         }
15275         
15276         var buttons = {
15277             tag : 'div',
15278             cls : 'tickable-buttons',
15279             cn : [
15280                 {
15281                     tag : 'button',
15282                     type : 'button',
15283                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15284                     //html : this.triggerText
15285                     html: btn_text_select
15286                 },
15287                 {
15288                     tag : 'button',
15289                     type : 'button',
15290                     name : 'ok',
15291                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15292                     //html : 'Done'
15293                     html: btn_text_done
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'cancel',
15299                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15300                     //html : 'Cancel'
15301                     html: btn_text_cancel
15302                 }
15303             ]
15304         };
15305         
15306         if(this.editable){
15307             buttons.cn.unshift({
15308                 tag: 'input',
15309                 cls: 'roo-select2-search-field-input'
15310             });
15311         }
15312         
15313         var _this = this;
15314         
15315         Roo.each(buttons.cn, function(c){
15316             if (_this.size) {
15317                 c.cls += ' btn-' + _this.size;
15318             }
15319
15320             if (_this.disabled) {
15321                 c.disabled = true;
15322             }
15323         });
15324         
15325         var box = {
15326             tag: 'div',
15327             style : 'display: contents',
15328             cn: [
15329                 {
15330                     tag: 'input',
15331                     type : 'hidden',
15332                     cls: 'form-hidden-field'
15333                 },
15334                 {
15335                     tag: 'ul',
15336                     cls: 'roo-select2-choices',
15337                     cn:[
15338                         {
15339                             tag: 'li',
15340                             cls: 'roo-select2-search-field',
15341                             cn: [
15342                                 buttons
15343                             ]
15344                         }
15345                     ]
15346                 }
15347             ]
15348         };
15349         
15350         var combobox = {
15351             cls: 'roo-select2-container input-group roo-select2-container-multi',
15352             cn: [
15353                 
15354                 box
15355 //                {
15356 //                    tag: 'ul',
15357 //                    cls: 'typeahead typeahead-long dropdown-menu',
15358 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15359 //                }
15360             ]
15361         };
15362         
15363         if(this.hasFeedback && !this.allowBlank){
15364             
15365             var feedback = {
15366                 tag: 'span',
15367                 cls: 'glyphicon form-control-feedback'
15368             };
15369
15370             combobox.cn.push(feedback);
15371         }
15372         
15373         
15374         
15375         var indicator = {
15376             tag : 'i',
15377             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15378             tooltip : 'This field is required'
15379         };
15380         if (Roo.bootstrap.version == 4) {
15381             indicator = {
15382                 tag : 'i',
15383                 style : 'display:none'
15384             };
15385         }
15386         if (align ==='left' && this.fieldLabel.length) {
15387             
15388             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15389             
15390             cfg.cn = [
15391                 indicator,
15392                 {
15393                     tag: 'label',
15394                     'for' :  id,
15395                     cls : 'control-label col-form-label',
15396                     html : this.fieldLabel
15397
15398                 },
15399                 {
15400                     cls : "", 
15401                     cn: [
15402                         combobox
15403                     ]
15404                 }
15405
15406             ];
15407             
15408             var labelCfg = cfg.cn[1];
15409             var contentCfg = cfg.cn[2];
15410             
15411
15412             if(this.indicatorpos == 'right'){
15413                 
15414                 cfg.cn = [
15415                     {
15416                         tag: 'label',
15417                         'for' :  id,
15418                         cls : 'control-label col-form-label',
15419                         cn : [
15420                             {
15421                                 tag : 'span',
15422                                 html : this.fieldLabel
15423                             },
15424                             indicator
15425                         ]
15426                     },
15427                     {
15428                         cls : "",
15429                         cn: [
15430                             combobox
15431                         ]
15432                     }
15433
15434                 ];
15435                 
15436                 
15437                 
15438                 labelCfg = cfg.cn[0];
15439                 contentCfg = cfg.cn[1];
15440             
15441             }
15442             
15443             if(this.labelWidth > 12){
15444                 labelCfg.style = "width: " + this.labelWidth + 'px';
15445             }
15446             if(this.width * 1 > 0){
15447                 contentCfg.style = "width: " + this.width + 'px';
15448             }
15449             if(this.labelWidth < 13 && this.labelmd == 0){
15450                 this.labelmd = this.labelWidth;
15451             }
15452             
15453             if(this.labellg > 0){
15454                 labelCfg.cls += ' col-lg-' + this.labellg;
15455                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15456             }
15457             
15458             if(this.labelmd > 0){
15459                 labelCfg.cls += ' col-md-' + this.labelmd;
15460                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15461             }
15462             
15463             if(this.labelsm > 0){
15464                 labelCfg.cls += ' col-sm-' + this.labelsm;
15465                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15466             }
15467             
15468             if(this.labelxs > 0){
15469                 labelCfg.cls += ' col-xs-' + this.labelxs;
15470                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15471             }
15472                 
15473                 
15474         } else if ( this.fieldLabel.length) {
15475 //                Roo.log(" label");
15476                  cfg.cn = [
15477                    indicator,
15478                     {
15479                         tag: 'label',
15480                         //cls : 'input-group-addon',
15481                         html : this.fieldLabel
15482                     },
15483                     combobox
15484                 ];
15485                 
15486                 if(this.indicatorpos == 'right'){
15487                     cfg.cn = [
15488                         {
15489                             tag: 'label',
15490                             //cls : 'input-group-addon',
15491                             html : this.fieldLabel
15492                         },
15493                         indicator,
15494                         combobox
15495                     ];
15496                     
15497                 }
15498
15499         } else {
15500             
15501 //                Roo.log(" no label && no align");
15502                 cfg = combobox
15503                      
15504                 
15505         }
15506          
15507         var settings=this;
15508         ['xs','sm','md','lg'].map(function(size){
15509             if (settings[size]) {
15510                 cfg.cls += ' col-' + size + '-' + settings[size];
15511             }
15512         });
15513         
15514         return cfg;
15515         
15516     },
15517     
15518     _initEventsCalled : false,
15519     
15520     // private
15521     initEvents: function()
15522     {   
15523         if (this._initEventsCalled) { // as we call render... prevent looping...
15524             return;
15525         }
15526         this._initEventsCalled = true;
15527         
15528         if (!this.store) {
15529             throw "can not find store for combo";
15530         }
15531         
15532         this.indicator = this.indicatorEl();
15533         
15534         this.store = Roo.factory(this.store, Roo.data);
15535         this.store.parent = this;
15536         
15537         // if we are building from html. then this element is so complex, that we can not really
15538         // use the rendered HTML.
15539         // so we have to trash and replace the previous code.
15540         if (Roo.XComponent.build_from_html) {
15541             // remove this element....
15542             var e = this.el.dom, k=0;
15543             while (e ) { e = e.previousSibling;  ++k;}
15544
15545             this.el.remove();
15546             
15547             this.el=false;
15548             this.rendered = false;
15549             
15550             this.render(this.parent().getChildContainer(true), k);
15551         }
15552         
15553         if(Roo.isIOS && this.useNativeIOS){
15554             this.initIOSView();
15555             return;
15556         }
15557         
15558         /*
15559          * Touch Devices
15560          */
15561         
15562         if(Roo.isTouch && this.mobileTouchView){
15563             this.initTouchView();
15564             return;
15565         }
15566         
15567         if(this.tickable){
15568             this.initTickableEvents();
15569             return;
15570         }
15571         
15572         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15573         
15574         if(this.hiddenName){
15575             
15576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15577             
15578             this.hiddenField.dom.value =
15579                 this.hiddenValue !== undefined ? this.hiddenValue :
15580                 this.value !== undefined ? this.value : '';
15581
15582             // prevent input submission
15583             this.el.dom.removeAttribute('name');
15584             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15585              
15586              
15587         }
15588         //if(Roo.isGecko){
15589         //    this.el.dom.setAttribute('autocomplete', 'off');
15590         //}
15591         
15592         var cls = 'x-combo-list';
15593         
15594         //this.list = new Roo.Layer({
15595         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15596         //});
15597         
15598         var _this = this;
15599         
15600         (function(){
15601             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15602             _this.list.setWidth(lw);
15603         }).defer(100);
15604         
15605         this.list.on('mouseover', this.onViewOver, this);
15606         this.list.on('mousemove', this.onViewMove, this);
15607         this.list.on('scroll', this.onViewScroll, this);
15608         
15609         /*
15610         this.list.swallowEvent('mousewheel');
15611         this.assetHeight = 0;
15612
15613         if(this.title){
15614             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15615             this.assetHeight += this.header.getHeight();
15616         }
15617
15618         this.innerList = this.list.createChild({cls:cls+'-inner'});
15619         this.innerList.on('mouseover', this.onViewOver, this);
15620         this.innerList.on('mousemove', this.onViewMove, this);
15621         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15622         
15623         if(this.allowBlank && !this.pageSize && !this.disableClear){
15624             this.footer = this.list.createChild({cls:cls+'-ft'});
15625             this.pageTb = new Roo.Toolbar(this.footer);
15626            
15627         }
15628         if(this.pageSize){
15629             this.footer = this.list.createChild({cls:cls+'-ft'});
15630             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15631                     {pageSize: this.pageSize});
15632             
15633         }
15634         
15635         if (this.pageTb && this.allowBlank && !this.disableClear) {
15636             var _this = this;
15637             this.pageTb.add(new Roo.Toolbar.Fill(), {
15638                 cls: 'x-btn-icon x-btn-clear',
15639                 text: '&#160;',
15640                 handler: function()
15641                 {
15642                     _this.collapse();
15643                     _this.clearValue();
15644                     _this.onSelect(false, -1);
15645                 }
15646             });
15647         }
15648         if (this.footer) {
15649             this.assetHeight += this.footer.getHeight();
15650         }
15651         */
15652             
15653         if(!this.tpl){
15654             this.tpl = Roo.bootstrap.version == 4 ?
15655                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15656                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15657         }
15658
15659         this.view = new Roo.View(this.list, this.tpl, {
15660             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15661         });
15662         //this.view.wrapEl.setDisplayed(false);
15663         this.view.on('click', this.onViewClick, this);
15664         
15665         
15666         this.store.on('beforeload', this.onBeforeLoad, this);
15667         this.store.on('load', this.onLoad, this);
15668         this.store.on('loadexception', this.onLoadException, this);
15669         /*
15670         if(this.resizable){
15671             this.resizer = new Roo.Resizable(this.list,  {
15672                pinned:true, handles:'se'
15673             });
15674             this.resizer.on('resize', function(r, w, h){
15675                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15676                 this.listWidth = w;
15677                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15678                 this.restrictHeight();
15679             }, this);
15680             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15681         }
15682         */
15683         if(!this.editable){
15684             this.editable = true;
15685             this.setEditable(false);
15686         }
15687         
15688         /*
15689         
15690         if (typeof(this.events.add.listeners) != 'undefined') {
15691             
15692             this.addicon = this.wrap.createChild(
15693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15694        
15695             this.addicon.on('click', function(e) {
15696                 this.fireEvent('add', this);
15697             }, this);
15698         }
15699         if (typeof(this.events.edit.listeners) != 'undefined') {
15700             
15701             this.editicon = this.wrap.createChild(
15702                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15703             if (this.addicon) {
15704                 this.editicon.setStyle('margin-left', '40px');
15705             }
15706             this.editicon.on('click', function(e) {
15707                 
15708                 // we fire even  if inothing is selected..
15709                 this.fireEvent('edit', this, this.lastData );
15710                 
15711             }, this);
15712         }
15713         */
15714         
15715         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15716             "up" : function(e){
15717                 this.inKeyMode = true;
15718                 this.selectPrev();
15719             },
15720
15721             "down" : function(e){
15722                 if(!this.isExpanded()){
15723                     this.onTriggerClick();
15724                 }else{
15725                     this.inKeyMode = true;
15726                     this.selectNext();
15727                 }
15728             },
15729
15730             "enter" : function(e){
15731 //                this.onViewClick();
15732                 //return true;
15733                 this.collapse();
15734                 
15735                 if(this.fireEvent("specialkey", this, e)){
15736                     this.onViewClick(false);
15737                 }
15738                 
15739                 return true;
15740             },
15741
15742             "esc" : function(e){
15743                 this.collapse();
15744             },
15745
15746             "tab" : function(e){
15747                 this.collapse();
15748                 
15749                 if(this.fireEvent("specialkey", this, e)){
15750                     this.onViewClick(false);
15751                 }
15752                 
15753                 return true;
15754             },
15755
15756             scope : this,
15757
15758             doRelay : function(foo, bar, hname){
15759                 if(hname == 'down' || this.scope.isExpanded()){
15760                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15761                 }
15762                 return true;
15763             },
15764
15765             forceKeyDown: true
15766         });
15767         
15768         
15769         this.queryDelay = Math.max(this.queryDelay || 10,
15770                 this.mode == 'local' ? 10 : 250);
15771         
15772         
15773         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15774         
15775         if(this.typeAhead){
15776             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15777         }
15778         if(this.editable !== false){
15779             this.inputEl().on("keyup", this.onKeyUp, this);
15780         }
15781         if(this.forceSelection){
15782             this.inputEl().on('blur', this.doForce, this);
15783         }
15784         
15785         if(this.multiple){
15786             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15787             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15788         }
15789     },
15790     
15791     initTickableEvents: function()
15792     {   
15793         this.createList();
15794         
15795         if(this.hiddenName){
15796             
15797             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15798             
15799             this.hiddenField.dom.value =
15800                 this.hiddenValue !== undefined ? this.hiddenValue :
15801                 this.value !== undefined ? this.value : '';
15802
15803             // prevent input submission
15804             this.el.dom.removeAttribute('name');
15805             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15806              
15807              
15808         }
15809         
15810 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15811         
15812         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15813         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15814         if(this.triggerList){
15815             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15816         }
15817          
15818         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15819         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15820         
15821         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15822         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15823         
15824         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15825         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15826         
15827         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15828         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15829         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15830         
15831         this.okBtn.hide();
15832         this.cancelBtn.hide();
15833         
15834         var _this = this;
15835         
15836         (function(){
15837             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15838             _this.list.setWidth(lw);
15839         }).defer(100);
15840         
15841         this.list.on('mouseover', this.onViewOver, this);
15842         this.list.on('mousemove', this.onViewMove, this);
15843         
15844         this.list.on('scroll', this.onViewScroll, this);
15845         
15846         if(!this.tpl){
15847             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15848                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15849         }
15850
15851         this.view = new Roo.View(this.list, this.tpl, {
15852             singleSelect:true,
15853             tickable:true,
15854             parent:this,
15855             store: this.store,
15856             selectedClass: this.selectedClass
15857         });
15858         
15859         //this.view.wrapEl.setDisplayed(false);
15860         this.view.on('click', this.onViewClick, this);
15861         
15862         
15863         
15864         this.store.on('beforeload', this.onBeforeLoad, this);
15865         this.store.on('load', this.onLoad, this);
15866         this.store.on('loadexception', this.onLoadException, this);
15867         
15868         if(this.editable){
15869             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15870                 "up" : function(e){
15871                     this.inKeyMode = true;
15872                     this.selectPrev();
15873                 },
15874
15875                 "down" : function(e){
15876                     this.inKeyMode = true;
15877                     this.selectNext();
15878                 },
15879
15880                 "enter" : function(e){
15881                     if(this.fireEvent("specialkey", this, e)){
15882                         this.onViewClick(false);
15883                     }
15884                     
15885                     return true;
15886                 },
15887
15888                 "esc" : function(e){
15889                     this.onTickableFooterButtonClick(e, false, false);
15890                 },
15891
15892                 "tab" : function(e){
15893                     this.fireEvent("specialkey", this, e);
15894                     
15895                     this.onTickableFooterButtonClick(e, false, false);
15896                     
15897                     return true;
15898                 },
15899
15900                 scope : this,
15901
15902                 doRelay : function(e, fn, key){
15903                     if(this.scope.isExpanded()){
15904                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15905                     }
15906                     return true;
15907                 },
15908
15909                 forceKeyDown: true
15910             });
15911         }
15912         
15913         this.queryDelay = Math.max(this.queryDelay || 10,
15914                 this.mode == 'local' ? 10 : 250);
15915         
15916         
15917         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15918         
15919         if(this.typeAhead){
15920             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15921         }
15922         
15923         if(this.editable !== false){
15924             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15925         }
15926         
15927         this.indicator = this.indicatorEl();
15928         
15929         if(this.indicator){
15930             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15931             this.indicator.hide();
15932         }
15933         
15934     },
15935
15936     onDestroy : function(){
15937         if(this.view){
15938             this.view.setStore(null);
15939             this.view.el.removeAllListeners();
15940             this.view.el.remove();
15941             this.view.purgeListeners();
15942         }
15943         if(this.list){
15944             this.list.dom.innerHTML  = '';
15945         }
15946         
15947         if(this.store){
15948             this.store.un('beforeload', this.onBeforeLoad, this);
15949             this.store.un('load', this.onLoad, this);
15950             this.store.un('loadexception', this.onLoadException, this);
15951         }
15952         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15953     },
15954
15955     // private
15956     fireKey : function(e){
15957         if(e.isNavKeyPress() && !this.list.isVisible()){
15958             this.fireEvent("specialkey", this, e);
15959         }
15960     },
15961
15962     // private
15963     onResize: function(w, h)
15964     {
15965         
15966         
15967 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15968 //        
15969 //        if(typeof w != 'number'){
15970 //            // we do not handle it!?!?
15971 //            return;
15972 //        }
15973 //        var tw = this.trigger.getWidth();
15974 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15975 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15976 //        var x = w - tw;
15977 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15978 //            
15979 //        //this.trigger.setStyle('left', x+'px');
15980 //        
15981 //        if(this.list && this.listWidth === undefined){
15982 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15983 //            this.list.setWidth(lw);
15984 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15985 //        }
15986         
15987     
15988         
15989     },
15990
15991     /**
15992      * Allow or prevent the user from directly editing the field text.  If false is passed,
15993      * the user will only be able to select from the items defined in the dropdown list.  This method
15994      * is the runtime equivalent of setting the 'editable' config option at config time.
15995      * @param {Boolean} value True to allow the user to directly edit the field text
15996      */
15997     setEditable : function(value){
15998         if(value == this.editable){
15999             return;
16000         }
16001         this.editable = value;
16002         if(!value){
16003             this.inputEl().dom.setAttribute('readOnly', true);
16004             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16005             this.inputEl().addClass('x-combo-noedit');
16006         }else{
16007             this.inputEl().dom.setAttribute('readOnly', false);
16008             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16009             this.inputEl().removeClass('x-combo-noedit');
16010         }
16011     },
16012
16013     // private
16014     
16015     onBeforeLoad : function(combo,opts){
16016         if(!this.hasFocus){
16017             return;
16018         }
16019          if (!opts.add) {
16020             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16021          }
16022         this.restrictHeight();
16023         this.selectedIndex = -1;
16024     },
16025
16026     // private
16027     onLoad : function(){
16028         
16029         this.hasQuery = false;
16030         
16031         if(!this.hasFocus){
16032             return;
16033         }
16034         
16035         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16036             this.loading.hide();
16037         }
16038         
16039         if(this.store.getCount() > 0){
16040             
16041             this.expand();
16042             this.restrictHeight();
16043             if(this.lastQuery == this.allQuery){
16044                 if(this.editable && !this.tickable){
16045                     this.inputEl().dom.select();
16046                 }
16047                 
16048                 if(
16049                     !this.selectByValue(this.value, true) &&
16050                     this.autoFocus && 
16051                     (
16052                         !this.store.lastOptions ||
16053                         typeof(this.store.lastOptions.add) == 'undefined' || 
16054                         this.store.lastOptions.add != true
16055                     )
16056                 ){
16057                     this.select(0, true);
16058                 }
16059             }else{
16060                 if(this.autoFocus){
16061                     this.selectNext();
16062                 }
16063                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16064                     this.taTask.delay(this.typeAheadDelay);
16065                 }
16066             }
16067         }else{
16068             this.onEmptyResults();
16069         }
16070         
16071         //this.el.focus();
16072     },
16073     // private
16074     onLoadException : function()
16075     {
16076         this.hasQuery = false;
16077         
16078         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16079             this.loading.hide();
16080         }
16081         
16082         if(this.tickable && this.editable){
16083             return;
16084         }
16085         
16086         this.collapse();
16087         // only causes errors at present
16088         //Roo.log(this.store.reader.jsonData);
16089         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16090             // fixme
16091             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16092         //}
16093         
16094         
16095     },
16096     // private
16097     onTypeAhead : function(){
16098         if(this.store.getCount() > 0){
16099             var r = this.store.getAt(0);
16100             var newValue = r.data[this.displayField];
16101             var len = newValue.length;
16102             var selStart = this.getRawValue().length;
16103             
16104             if(selStart != len){
16105                 this.setRawValue(newValue);
16106                 this.selectText(selStart, newValue.length);
16107             }
16108         }
16109     },
16110
16111     // private
16112     onSelect : function(record, index){
16113         
16114         if(this.fireEvent('beforeselect', this, record, index) !== false){
16115         
16116             this.setFromData(index > -1 ? record.data : false);
16117             
16118             this.collapse();
16119             this.fireEvent('select', this, record, index);
16120         }
16121     },
16122
16123     /**
16124      * Returns the currently selected field value or empty string if no value is set.
16125      * @return {String} value The selected value
16126      */
16127     getValue : function()
16128     {
16129         if(Roo.isIOS && this.useNativeIOS){
16130             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16131         }
16132         
16133         if(this.multiple){
16134             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16135         }
16136         
16137         if(this.valueField){
16138             return typeof this.value != 'undefined' ? this.value : '';
16139         }else{
16140             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16141         }
16142     },
16143     
16144     getRawValue : function()
16145     {
16146         if(Roo.isIOS && this.useNativeIOS){
16147             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16148         }
16149         
16150         var v = this.inputEl().getValue();
16151         
16152         return v;
16153     },
16154
16155     /**
16156      * Clears any text/value currently set in the field
16157      */
16158     clearValue : function(){
16159         
16160         if(this.hiddenField){
16161             this.hiddenField.dom.value = '';
16162         }
16163         this.value = '';
16164         this.setRawValue('');
16165         this.lastSelectionText = '';
16166         this.lastData = false;
16167         
16168         var close = this.closeTriggerEl();
16169         
16170         if(close){
16171             close.hide();
16172         }
16173         
16174         this.validate();
16175         
16176     },
16177
16178     /**
16179      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16180      * will be displayed in the field.  If the value does not match the data value of an existing item,
16181      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16182      * Otherwise the field will be blank (although the value will still be set).
16183      * @param {String} value The value to match
16184      */
16185     setValue : function(v)
16186     {
16187         if(Roo.isIOS && this.useNativeIOS){
16188             this.setIOSValue(v);
16189             return;
16190         }
16191         
16192         if(this.multiple){
16193             this.syncValue();
16194             return;
16195         }
16196         
16197         var text = v;
16198         if(this.valueField){
16199             var r = this.findRecord(this.valueField, v);
16200             if(r){
16201                 text = r.data[this.displayField];
16202             }else if(this.valueNotFoundText !== undefined){
16203                 text = this.valueNotFoundText;
16204             }
16205         }
16206         this.lastSelectionText = text;
16207         if(this.hiddenField){
16208             this.hiddenField.dom.value = v;
16209         }
16210         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16211         this.value = v;
16212         
16213         var close = this.closeTriggerEl();
16214         
16215         if(close){
16216             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16217         }
16218         
16219         this.validate();
16220     },
16221     /**
16222      * @property {Object} the last set data for the element
16223      */
16224     
16225     lastData : false,
16226     /**
16227      * Sets the value of the field based on a object which is related to the record format for the store.
16228      * @param {Object} value the value to set as. or false on reset?
16229      */
16230     setFromData : function(o){
16231         
16232         if(this.multiple){
16233             this.addItem(o);
16234             return;
16235         }
16236             
16237         var dv = ''; // display value
16238         var vv = ''; // value value..
16239         this.lastData = o;
16240         if (this.displayField) {
16241             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16242         } else {
16243             // this is an error condition!!!
16244             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16245         }
16246         
16247         if(this.valueField){
16248             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16249         }
16250         
16251         var close = this.closeTriggerEl();
16252         
16253         if(close){
16254             if(dv.length || vv * 1 > 0){
16255                 close.show() ;
16256                 this.blockFocus=true;
16257             } else {
16258                 close.hide();
16259             }             
16260         }
16261         
16262         if(this.hiddenField){
16263             this.hiddenField.dom.value = vv;
16264             
16265             this.lastSelectionText = dv;
16266             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16267             this.value = vv;
16268             return;
16269         }
16270         // no hidden field.. - we store the value in 'value', but still display
16271         // display field!!!!
16272         this.lastSelectionText = dv;
16273         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16274         this.value = vv;
16275         
16276         
16277         
16278     },
16279     // private
16280     reset : function(){
16281         // overridden so that last data is reset..
16282         
16283         if(this.multiple){
16284             this.clearItem();
16285             return;
16286         }
16287         
16288         this.setValue(this.originalValue);
16289         //this.clearInvalid();
16290         this.lastData = false;
16291         if (this.view) {
16292             this.view.clearSelections();
16293         }
16294         
16295         this.validate();
16296     },
16297     // private
16298     findRecord : function(prop, value){
16299         var record;
16300         if(this.store.getCount() > 0){
16301             this.store.each(function(r){
16302                 if(r.data[prop] == value){
16303                     record = r;
16304                     return false;
16305                 }
16306                 return true;
16307             });
16308         }
16309         return record;
16310     },
16311     
16312     getName: function()
16313     {
16314         // returns hidden if it's set..
16315         if (!this.rendered) {return ''};
16316         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16317         
16318     },
16319     // private
16320     onViewMove : function(e, t){
16321         this.inKeyMode = false;
16322     },
16323
16324     // private
16325     onViewOver : function(e, t){
16326         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16327             return;
16328         }
16329         var item = this.view.findItemFromChild(t);
16330         
16331         if(item){
16332             var index = this.view.indexOf(item);
16333             this.select(index, false);
16334         }
16335     },
16336
16337     // private
16338     onViewClick : function(view, doFocus, el, e)
16339     {
16340         var index = this.view.getSelectedIndexes()[0];
16341         
16342         var r = this.store.getAt(index);
16343         
16344         if(this.tickable){
16345             
16346             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16347                 return;
16348             }
16349             
16350             var rm = false;
16351             var _this = this;
16352             
16353             Roo.each(this.tickItems, function(v,k){
16354                 
16355                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16356                     Roo.log(v);
16357                     _this.tickItems.splice(k, 1);
16358                     
16359                     if(typeof(e) == 'undefined' && view == false){
16360                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16361                     }
16362                     
16363                     rm = true;
16364                     return;
16365                 }
16366             });
16367             
16368             if(rm){
16369                 return;
16370             }
16371             
16372             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16373                 this.tickItems.push(r.data);
16374             }
16375             
16376             if(typeof(e) == 'undefined' && view == false){
16377                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16378             }
16379                     
16380             return;
16381         }
16382         
16383         if(r){
16384             this.onSelect(r, index);
16385         }
16386         if(doFocus !== false && !this.blockFocus){
16387             this.inputEl().focus();
16388         }
16389     },
16390
16391     // private
16392     restrictHeight : function(){
16393         //this.innerList.dom.style.height = '';
16394         //var inner = this.innerList.dom;
16395         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16396         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16397         //this.list.beginUpdate();
16398         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16399         this.list.alignTo(this.inputEl(), this.listAlign);
16400         this.list.alignTo(this.inputEl(), this.listAlign);
16401         //this.list.endUpdate();
16402     },
16403
16404     // private
16405     onEmptyResults : function(){
16406         
16407         if(this.tickable && this.editable){
16408             this.hasFocus = false;
16409             this.restrictHeight();
16410             return;
16411         }
16412         
16413         this.collapse();
16414     },
16415
16416     /**
16417      * Returns true if the dropdown list is expanded, else false.
16418      */
16419     isExpanded : function(){
16420         return this.list.isVisible();
16421     },
16422
16423     /**
16424      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16425      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16426      * @param {String} value The data value of the item to select
16427      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16428      * selected item if it is not currently in view (defaults to true)
16429      * @return {Boolean} True if the value matched an item in the list, else false
16430      */
16431     selectByValue : function(v, scrollIntoView){
16432         if(v !== undefined && v !== null){
16433             var r = this.findRecord(this.valueField || this.displayField, v);
16434             if(r){
16435                 this.select(this.store.indexOf(r), scrollIntoView);
16436                 return true;
16437             }
16438         }
16439         return false;
16440     },
16441
16442     /**
16443      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16444      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16445      * @param {Number} index The zero-based index of the list item to select
16446      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16447      * selected item if it is not currently in view (defaults to true)
16448      */
16449     select : function(index, scrollIntoView){
16450         this.selectedIndex = index;
16451         this.view.select(index);
16452         if(scrollIntoView !== false){
16453             var el = this.view.getNode(index);
16454             /*
16455              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16456              */
16457             if(el){
16458                 this.list.scrollChildIntoView(el, false);
16459             }
16460         }
16461     },
16462
16463     // private
16464     selectNext : function(){
16465         var ct = this.store.getCount();
16466         if(ct > 0){
16467             if(this.selectedIndex == -1){
16468                 this.select(0);
16469             }else if(this.selectedIndex < ct-1){
16470                 this.select(this.selectedIndex+1);
16471             }
16472         }
16473     },
16474
16475     // private
16476     selectPrev : function(){
16477         var ct = this.store.getCount();
16478         if(ct > 0){
16479             if(this.selectedIndex == -1){
16480                 this.select(0);
16481             }else if(this.selectedIndex != 0){
16482                 this.select(this.selectedIndex-1);
16483             }
16484         }
16485     },
16486
16487     // private
16488     onKeyUp : function(e){
16489         if(this.editable !== false && !e.isSpecialKey()){
16490             this.lastKey = e.getKey();
16491             this.dqTask.delay(this.queryDelay);
16492         }
16493     },
16494
16495     // private
16496     validateBlur : function(){
16497         return !this.list || !this.list.isVisible();   
16498     },
16499
16500     // private
16501     initQuery : function(){
16502         
16503         var v = this.getRawValue();
16504         
16505         if(this.tickable && this.editable){
16506             v = this.tickableInputEl().getValue();
16507         }
16508         
16509         this.doQuery(v);
16510     },
16511
16512     // private
16513     doForce : function(){
16514         if(this.inputEl().dom.value.length > 0){
16515             this.inputEl().dom.value =
16516                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16517              
16518         }
16519     },
16520
16521     /**
16522      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16523      * query allowing the query action to be canceled if needed.
16524      * @param {String} query The SQL query to execute
16525      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16526      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16527      * saved in the current store (defaults to false)
16528      */
16529     doQuery : function(q, forceAll){
16530         
16531         if(q === undefined || q === null){
16532             q = '';
16533         }
16534         var qe = {
16535             query: q,
16536             forceAll: forceAll,
16537             combo: this,
16538             cancel:false
16539         };
16540         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16541             return false;
16542         }
16543         q = qe.query;
16544         
16545         forceAll = qe.forceAll;
16546         if(forceAll === true || (q.length >= this.minChars)){
16547             
16548             this.hasQuery = true;
16549             
16550             if(this.lastQuery != q || this.alwaysQuery){
16551                 this.lastQuery = q;
16552                 if(this.mode == 'local'){
16553                     this.selectedIndex = -1;
16554                     if(forceAll){
16555                         this.store.clearFilter();
16556                     }else{
16557                         
16558                         if(this.specialFilter){
16559                             this.fireEvent('specialfilter', this);
16560                             this.onLoad();
16561                             return;
16562                         }
16563                         
16564                         this.store.filter(this.displayField, q);
16565                     }
16566                     
16567                     this.store.fireEvent("datachanged", this.store);
16568                     
16569                     this.onLoad();
16570                     
16571                     
16572                 }else{
16573                     
16574                     this.store.baseParams[this.queryParam] = q;
16575                     
16576                     var options = {params : this.getParams(q)};
16577                     
16578                     if(this.loadNext){
16579                         options.add = true;
16580                         options.params.start = this.page * this.pageSize;
16581                     }
16582                     
16583                     this.store.load(options);
16584                     
16585                     /*
16586                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16587                      *  we should expand the list on onLoad
16588                      *  so command out it
16589                      */
16590 //                    this.expand();
16591                 }
16592             }else{
16593                 this.selectedIndex = -1;
16594                 this.onLoad();   
16595             }
16596         }
16597         
16598         this.loadNext = false;
16599     },
16600     
16601     // private
16602     getParams : function(q){
16603         var p = {};
16604         //p[this.queryParam] = q;
16605         
16606         if(this.pageSize){
16607             p.start = 0;
16608             p.limit = this.pageSize;
16609         }
16610         return p;
16611     },
16612
16613     /**
16614      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16615      */
16616     collapse : function(){
16617         if(!this.isExpanded()){
16618             return;
16619         }
16620         
16621         this.list.hide();
16622         
16623         this.hasFocus = false;
16624         
16625         if(this.tickable){
16626             this.okBtn.hide();
16627             this.cancelBtn.hide();
16628             this.trigger.show();
16629             
16630             if(this.editable){
16631                 this.tickableInputEl().dom.value = '';
16632                 this.tickableInputEl().blur();
16633             }
16634             
16635         }
16636         
16637         Roo.get(document).un('mousedown', this.collapseIf, this);
16638         Roo.get(document).un('mousewheel', this.collapseIf, this);
16639         if (!this.editable) {
16640             Roo.get(document).un('keydown', this.listKeyPress, this);
16641         }
16642         this.fireEvent('collapse', this);
16643         
16644         this.validate();
16645     },
16646
16647     // private
16648     collapseIf : function(e){
16649         var in_combo  = e.within(this.el);
16650         var in_list =  e.within(this.list);
16651         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16652         
16653         if (in_combo || in_list || is_list) {
16654             //e.stopPropagation();
16655             return;
16656         }
16657         
16658         if(this.tickable){
16659             this.onTickableFooterButtonClick(e, false, false);
16660         }
16661
16662         this.collapse();
16663         
16664     },
16665
16666     /**
16667      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16668      */
16669     expand : function(){
16670        
16671         if(this.isExpanded() || !this.hasFocus){
16672             return;
16673         }
16674         
16675         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16676         this.list.setWidth(lw);
16677         
16678         Roo.log('expand');
16679         
16680         this.list.show();
16681         
16682         this.restrictHeight();
16683         
16684         if(this.tickable){
16685             
16686             this.tickItems = Roo.apply([], this.item);
16687             
16688             this.okBtn.show();
16689             this.cancelBtn.show();
16690             this.trigger.hide();
16691             
16692             if(this.editable){
16693                 this.tickableInputEl().focus();
16694             }
16695             
16696         }
16697         
16698         Roo.get(document).on('mousedown', this.collapseIf, this);
16699         Roo.get(document).on('mousewheel', this.collapseIf, this);
16700         if (!this.editable) {
16701             Roo.get(document).on('keydown', this.listKeyPress, this);
16702         }
16703         
16704         this.fireEvent('expand', this);
16705     },
16706
16707     // private
16708     // Implements the default empty TriggerField.onTriggerClick function
16709     onTriggerClick : function(e)
16710     {
16711         Roo.log('trigger click');
16712         
16713         if(this.disabled || !this.triggerList){
16714             return;
16715         }
16716         
16717         this.page = 0;
16718         this.loadNext = false;
16719         
16720         if(this.isExpanded()){
16721             this.collapse();
16722             if (!this.blockFocus) {
16723                 this.inputEl().focus();
16724             }
16725             
16726         }else {
16727             this.hasFocus = true;
16728             if(this.triggerAction == 'all') {
16729                 this.doQuery(this.allQuery, true);
16730             } else {
16731                 this.doQuery(this.getRawValue());
16732             }
16733             if (!this.blockFocus) {
16734                 this.inputEl().focus();
16735             }
16736         }
16737     },
16738     
16739     onTickableTriggerClick : function(e)
16740     {
16741         if(this.disabled){
16742             return;
16743         }
16744         
16745         this.page = 0;
16746         this.loadNext = false;
16747         this.hasFocus = true;
16748         
16749         if(this.triggerAction == 'all') {
16750             this.doQuery(this.allQuery, true);
16751         } else {
16752             this.doQuery(this.getRawValue());
16753         }
16754     },
16755     
16756     onSearchFieldClick : function(e)
16757     {
16758         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16759             this.onTickableFooterButtonClick(e, false, false);
16760             return;
16761         }
16762         
16763         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16764             return;
16765         }
16766         
16767         this.page = 0;
16768         this.loadNext = false;
16769         this.hasFocus = true;
16770         
16771         if(this.triggerAction == 'all') {
16772             this.doQuery(this.allQuery, true);
16773         } else {
16774             this.doQuery(this.getRawValue());
16775         }
16776     },
16777     
16778     listKeyPress : function(e)
16779     {
16780         //Roo.log('listkeypress');
16781         // scroll to first matching element based on key pres..
16782         if (e.isSpecialKey()) {
16783             return false;
16784         }
16785         var k = String.fromCharCode(e.getKey()).toUpperCase();
16786         //Roo.log(k);
16787         var match  = false;
16788         var csel = this.view.getSelectedNodes();
16789         var cselitem = false;
16790         if (csel.length) {
16791             var ix = this.view.indexOf(csel[0]);
16792             cselitem  = this.store.getAt(ix);
16793             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16794                 cselitem = false;
16795             }
16796             
16797         }
16798         
16799         this.store.each(function(v) { 
16800             if (cselitem) {
16801                 // start at existing selection.
16802                 if (cselitem.id == v.id) {
16803                     cselitem = false;
16804                 }
16805                 return true;
16806             }
16807                 
16808             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16809                 match = this.store.indexOf(v);
16810                 return false;
16811             }
16812             return true;
16813         }, this);
16814         
16815         if (match === false) {
16816             return true; // no more action?
16817         }
16818         // scroll to?
16819         this.view.select(match);
16820         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16821         sn.scrollIntoView(sn.dom.parentNode, false);
16822     },
16823     
16824     onViewScroll : function(e, t){
16825         
16826         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){
16827             return;
16828         }
16829         
16830         this.hasQuery = true;
16831         
16832         this.loading = this.list.select('.loading', true).first();
16833         
16834         if(this.loading === null){
16835             this.list.createChild({
16836                 tag: 'div',
16837                 cls: 'loading roo-select2-more-results roo-select2-active',
16838                 html: 'Loading more results...'
16839             });
16840             
16841             this.loading = this.list.select('.loading', true).first();
16842             
16843             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16844             
16845             this.loading.hide();
16846         }
16847         
16848         this.loading.show();
16849         
16850         var _combo = this;
16851         
16852         this.page++;
16853         this.loadNext = true;
16854         
16855         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16856         
16857         return;
16858     },
16859     
16860     addItem : function(o)
16861     {   
16862         var dv = ''; // display value
16863         
16864         if (this.displayField) {
16865             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16866         } else {
16867             // this is an error condition!!!
16868             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16869         }
16870         
16871         if(!dv.length){
16872             return;
16873         }
16874         
16875         var choice = this.choices.createChild({
16876             tag: 'li',
16877             cls: 'roo-select2-search-choice',
16878             cn: [
16879                 {
16880                     tag: 'div',
16881                     html: dv
16882                 },
16883                 {
16884                     tag: 'a',
16885                     href: '#',
16886                     cls: 'roo-select2-search-choice-close fa fa-times',
16887                     tabindex: '-1'
16888                 }
16889             ]
16890             
16891         }, this.searchField);
16892         
16893         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16894         
16895         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16896         
16897         this.item.push(o);
16898         
16899         this.lastData = o;
16900         
16901         this.syncValue();
16902         
16903         this.inputEl().dom.value = '';
16904         
16905         this.validate();
16906     },
16907     
16908     onRemoveItem : function(e, _self, o)
16909     {
16910         e.preventDefault();
16911         
16912         this.lastItem = Roo.apply([], this.item);
16913         
16914         var index = this.item.indexOf(o.data) * 1;
16915         
16916         if( index < 0){
16917             Roo.log('not this item?!');
16918             return;
16919         }
16920         
16921         this.item.splice(index, 1);
16922         o.item.remove();
16923         
16924         this.syncValue();
16925         
16926         this.fireEvent('remove', this, e);
16927         
16928         this.validate();
16929         
16930     },
16931     
16932     syncValue : function()
16933     {
16934         if(!this.item.length){
16935             this.clearValue();
16936             return;
16937         }
16938             
16939         var value = [];
16940         var _this = this;
16941         Roo.each(this.item, function(i){
16942             if(_this.valueField){
16943                 value.push(i[_this.valueField]);
16944                 return;
16945             }
16946
16947             value.push(i);
16948         });
16949
16950         this.value = value.join(',');
16951
16952         if(this.hiddenField){
16953             this.hiddenField.dom.value = this.value;
16954         }
16955         
16956         this.store.fireEvent("datachanged", this.store);
16957         
16958         this.validate();
16959     },
16960     
16961     clearItem : function()
16962     {
16963         if(!this.multiple){
16964             return;
16965         }
16966         
16967         this.item = [];
16968         
16969         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16970            c.remove();
16971         });
16972         
16973         this.syncValue();
16974         
16975         this.validate();
16976         
16977         if(this.tickable && !Roo.isTouch){
16978             this.view.refresh();
16979         }
16980     },
16981     
16982     inputEl: function ()
16983     {
16984         if(Roo.isIOS && this.useNativeIOS){
16985             return this.el.select('select.roo-ios-select', true).first();
16986         }
16987         
16988         if(Roo.isTouch && this.mobileTouchView){
16989             return this.el.select('input.form-control',true).first();
16990         }
16991         
16992         if(this.tickable){
16993             return this.searchField;
16994         }
16995         
16996         return this.el.select('input.form-control',true).first();
16997     },
16998     
16999     onTickableFooterButtonClick : function(e, btn, el)
17000     {
17001         e.preventDefault();
17002         
17003         this.lastItem = Roo.apply([], this.item);
17004         
17005         if(btn && btn.name == 'cancel'){
17006             this.tickItems = Roo.apply([], this.item);
17007             this.collapse();
17008             return;
17009         }
17010         
17011         this.clearItem();
17012         
17013         var _this = this;
17014         
17015         Roo.each(this.tickItems, function(o){
17016             _this.addItem(o);
17017         });
17018         
17019         this.collapse();
17020         
17021     },
17022     
17023     validate : function()
17024     {
17025         if(this.getVisibilityEl().hasClass('hidden')){
17026             return true;
17027         }
17028         
17029         var v = this.getRawValue();
17030         
17031         if(this.multiple){
17032             v = this.getValue();
17033         }
17034         
17035         if(this.disabled || this.allowBlank || v.length){
17036             this.markValid();
17037             return true;
17038         }
17039         
17040         this.markInvalid();
17041         return false;
17042     },
17043     
17044     tickableInputEl : function()
17045     {
17046         if(!this.tickable || !this.editable){
17047             return this.inputEl();
17048         }
17049         
17050         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17051     },
17052     
17053     
17054     getAutoCreateTouchView : function()
17055     {
17056         var id = Roo.id();
17057         
17058         var cfg = {
17059             cls: 'form-group' //input-group
17060         };
17061         
17062         var input =  {
17063             tag: 'input',
17064             id : id,
17065             type : this.inputType,
17066             cls : 'form-control x-combo-noedit',
17067             autocomplete: 'new-password',
17068             placeholder : this.placeholder || '',
17069             readonly : true
17070         };
17071         
17072         if (this.name) {
17073             input.name = this.name;
17074         }
17075         
17076         if (this.size) {
17077             input.cls += ' input-' + this.size;
17078         }
17079         
17080         if (this.disabled) {
17081             input.disabled = true;
17082         }
17083         
17084         var inputblock = {
17085             cls : 'roo-combobox-wrap',
17086             cn : [
17087                 input
17088             ]
17089         };
17090         
17091         if(this.before){
17092             inputblock.cls += ' input-group';
17093             
17094             inputblock.cn.unshift({
17095                 tag :'span',
17096                 cls : 'input-group-addon input-group-prepend input-group-text',
17097                 html : this.before
17098             });
17099         }
17100         
17101         if(this.removable && !this.multiple){
17102             inputblock.cls += ' roo-removable';
17103             
17104             inputblock.cn.push({
17105                 tag: 'button',
17106                 html : 'x',
17107                 cls : 'roo-combo-removable-btn close'
17108             });
17109         }
17110
17111         if(this.hasFeedback && !this.allowBlank){
17112             
17113             inputblock.cls += ' has-feedback';
17114             
17115             inputblock.cn.push({
17116                 tag: 'span',
17117                 cls: 'glyphicon form-control-feedback'
17118             });
17119             
17120         }
17121         
17122         if (this.after) {
17123             
17124             inputblock.cls += (this.before) ? '' : ' input-group';
17125             
17126             inputblock.cn.push({
17127                 tag :'span',
17128                 cls : 'input-group-addon input-group-append input-group-text',
17129                 html : this.after
17130             });
17131         }
17132
17133         
17134         var ibwrap = inputblock;
17135         
17136         if(this.multiple){
17137             ibwrap = {
17138                 tag: 'ul',
17139                 cls: 'roo-select2-choices',
17140                 cn:[
17141                     {
17142                         tag: 'li',
17143                         cls: 'roo-select2-search-field',
17144                         cn: [
17145
17146                             inputblock
17147                         ]
17148                     }
17149                 ]
17150             };
17151         
17152             
17153         }
17154         
17155         var combobox = {
17156             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17157             cn: [
17158                 {
17159                     tag: 'input',
17160                     type : 'hidden',
17161                     cls: 'form-hidden-field'
17162                 },
17163                 ibwrap
17164             ]
17165         };
17166         
17167         if(!this.multiple && this.showToggleBtn){
17168             
17169             var caret = {
17170                 cls: 'caret'
17171             };
17172             
17173             if (this.caret != false) {
17174                 caret = {
17175                      tag: 'i',
17176                      cls: 'fa fa-' + this.caret
17177                 };
17178                 
17179             }
17180             
17181             combobox.cn.push({
17182                 tag :'span',
17183                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17184                 cn : [
17185                     Roo.bootstrap.version == 3 ? caret : '',
17186                     {
17187                         tag: 'span',
17188                         cls: 'combobox-clear',
17189                         cn  : [
17190                             {
17191                                 tag : 'i',
17192                                 cls: 'icon-remove'
17193                             }
17194                         ]
17195                     }
17196                 ]
17197
17198             })
17199         }
17200         
17201         if(this.multiple){
17202             combobox.cls += ' roo-select2-container-multi';
17203         }
17204         
17205         var align = this.labelAlign || this.parentLabelAlign();
17206         
17207         if (align ==='left' && this.fieldLabel.length) {
17208
17209             cfg.cn = [
17210                 {
17211                    tag : 'i',
17212                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17213                    tooltip : 'This field is required'
17214                 },
17215                 {
17216                     tag: 'label',
17217                     cls : 'control-label col-form-label',
17218                     html : this.fieldLabel
17219
17220                 },
17221                 {
17222                     cls : 'roo-combobox-wrap ', 
17223                     cn: [
17224                         combobox
17225                     ]
17226                 }
17227             ];
17228             
17229             var labelCfg = cfg.cn[1];
17230             var contentCfg = cfg.cn[2];
17231             
17232
17233             if(this.indicatorpos == 'right'){
17234                 cfg.cn = [
17235                     {
17236                         tag: 'label',
17237                         'for' :  id,
17238                         cls : 'control-label col-form-label',
17239                         cn : [
17240                             {
17241                                 tag : 'span',
17242                                 html : this.fieldLabel
17243                             },
17244                             {
17245                                 tag : 'i',
17246                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17247                                 tooltip : 'This field is required'
17248                             }
17249                         ]
17250                     },
17251                     {
17252                         cls : "roo-combobox-wrap ",
17253                         cn: [
17254                             combobox
17255                         ]
17256                     }
17257
17258                 ];
17259                 
17260                 labelCfg = cfg.cn[0];
17261                 contentCfg = cfg.cn[1];
17262             }
17263             
17264            
17265             
17266             if(this.labelWidth > 12){
17267                 labelCfg.style = "width: " + this.labelWidth + 'px';
17268             }
17269            
17270             if(this.labelWidth < 13 && this.labelmd == 0){
17271                 this.labelmd = this.labelWidth;
17272             }
17273             
17274             if(this.labellg > 0){
17275                 labelCfg.cls += ' col-lg-' + this.labellg;
17276                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17277             }
17278             
17279             if(this.labelmd > 0){
17280                 labelCfg.cls += ' col-md-' + this.labelmd;
17281                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17282             }
17283             
17284             if(this.labelsm > 0){
17285                 labelCfg.cls += ' col-sm-' + this.labelsm;
17286                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17287             }
17288             
17289             if(this.labelxs > 0){
17290                 labelCfg.cls += ' col-xs-' + this.labelxs;
17291                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17292             }
17293                 
17294                 
17295         } else if ( this.fieldLabel.length) {
17296             cfg.cn = [
17297                 {
17298                    tag : 'i',
17299                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17300                    tooltip : 'This field is required'
17301                 },
17302                 {
17303                     tag: 'label',
17304                     cls : 'control-label',
17305                     html : this.fieldLabel
17306
17307                 },
17308                 {
17309                     cls : '', 
17310                     cn: [
17311                         combobox
17312                     ]
17313                 }
17314             ];
17315             
17316             if(this.indicatorpos == 'right'){
17317                 cfg.cn = [
17318                     {
17319                         tag: 'label',
17320                         cls : 'control-label',
17321                         html : this.fieldLabel,
17322                         cn : [
17323                             {
17324                                tag : 'i',
17325                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17326                                tooltip : 'This field is required'
17327                             }
17328                         ]
17329                     },
17330                     {
17331                         cls : '', 
17332                         cn: [
17333                             combobox
17334                         ]
17335                     }
17336                 ];
17337             }
17338         } else {
17339             cfg.cn = combobox;    
17340         }
17341         
17342         
17343         var settings = this;
17344         
17345         ['xs','sm','md','lg'].map(function(size){
17346             if (settings[size]) {
17347                 cfg.cls += ' col-' + size + '-' + settings[size];
17348             }
17349         });
17350         
17351         return cfg;
17352     },
17353     
17354     initTouchView : function()
17355     {
17356         this.renderTouchView();
17357         
17358         this.touchViewEl.on('scroll', function(){
17359             this.el.dom.scrollTop = 0;
17360         }, this);
17361         
17362         this.originalValue = this.getValue();
17363         
17364         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17365         
17366         this.inputEl().on("click", this.showTouchView, this);
17367         if (this.triggerEl) {
17368             this.triggerEl.on("click", this.showTouchView, this);
17369         }
17370         
17371         
17372         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17373         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17374         
17375         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17376         
17377         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17378         this.store.on('load', this.onTouchViewLoad, this);
17379         this.store.on('loadexception', this.onTouchViewLoadException, this);
17380         
17381         if(this.hiddenName){
17382             
17383             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17384             
17385             this.hiddenField.dom.value =
17386                 this.hiddenValue !== undefined ? this.hiddenValue :
17387                 this.value !== undefined ? this.value : '';
17388         
17389             this.el.dom.removeAttribute('name');
17390             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17391         }
17392         
17393         if(this.multiple){
17394             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17395             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17396         }
17397         
17398         if(this.removable && !this.multiple){
17399             var close = this.closeTriggerEl();
17400             if(close){
17401                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17402                 close.on('click', this.removeBtnClick, this, close);
17403             }
17404         }
17405         /*
17406          * fix the bug in Safari iOS8
17407          */
17408         this.inputEl().on("focus", function(e){
17409             document.activeElement.blur();
17410         }, this);
17411         
17412         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17413         
17414         return;
17415         
17416         
17417     },
17418     
17419     renderTouchView : function()
17420     {
17421         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17422         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17423         
17424         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17425         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17426         
17427         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17428         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17429         this.touchViewBodyEl.setStyle('overflow', 'auto');
17430         
17431         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17432         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17433         
17434         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17435         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437     },
17438     
17439     showTouchView : function()
17440     {
17441         if(this.disabled){
17442             return;
17443         }
17444         
17445         this.touchViewHeaderEl.hide();
17446
17447         if(this.modalTitle.length){
17448             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17449             this.touchViewHeaderEl.show();
17450         }
17451
17452         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17453         this.touchViewEl.show();
17454
17455         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17456         
17457         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17458         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17459
17460         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17461
17462         if(this.modalTitle.length){
17463             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17464         }
17465         
17466         this.touchViewBodyEl.setHeight(bodyHeight);
17467
17468         if(this.animate){
17469             var _this = this;
17470             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17471         }else{
17472             this.touchViewEl.addClass(['in','show']);
17473         }
17474         
17475         if(this._touchViewMask){
17476             Roo.get(document.body).addClass("x-body-masked");
17477             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17478             this._touchViewMask.setStyle('z-index', 10000);
17479             this._touchViewMask.addClass('show');
17480         }
17481         
17482         this.doTouchViewQuery();
17483         
17484     },
17485     
17486     hideTouchView : function()
17487     {
17488         this.touchViewEl.removeClass(['in','show']);
17489
17490         if(this.animate){
17491             var _this = this;
17492             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17493         }else{
17494             this.touchViewEl.setStyle('display', 'none');
17495         }
17496         
17497         if(this._touchViewMask){
17498             this._touchViewMask.removeClass('show');
17499             Roo.get(document.body).removeClass("x-body-masked");
17500         }
17501     },
17502     
17503     setTouchViewValue : function()
17504     {
17505         if(this.multiple){
17506             this.clearItem();
17507         
17508             var _this = this;
17509
17510             Roo.each(this.tickItems, function(o){
17511                 this.addItem(o);
17512             }, this);
17513         }
17514         
17515         this.hideTouchView();
17516     },
17517     
17518     doTouchViewQuery : function()
17519     {
17520         var qe = {
17521             query: '',
17522             forceAll: true,
17523             combo: this,
17524             cancel:false
17525         };
17526         
17527         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17528             return false;
17529         }
17530         
17531         if(!this.alwaysQuery || this.mode == 'local'){
17532             this.onTouchViewLoad();
17533             return;
17534         }
17535         
17536         this.store.load();
17537     },
17538     
17539     onTouchViewBeforeLoad : function(combo,opts)
17540     {
17541         return;
17542     },
17543
17544     // private
17545     onTouchViewLoad : function()
17546     {
17547         if(this.store.getCount() < 1){
17548             this.onTouchViewEmptyResults();
17549             return;
17550         }
17551         
17552         this.clearTouchView();
17553         
17554         var rawValue = this.getRawValue();
17555         
17556         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17557         
17558         this.tickItems = [];
17559         
17560         this.store.data.each(function(d, rowIndex){
17561             var row = this.touchViewListGroup.createChild(template);
17562             
17563             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17564                 row.addClass(d.data.cls);
17565             }
17566             
17567             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17568                 var cfg = {
17569                     data : d.data,
17570                     html : d.data[this.displayField]
17571                 };
17572                 
17573                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17574                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17575                 }
17576             }
17577             row.removeClass('selected');
17578             if(!this.multiple && this.valueField &&
17579                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17580             {
17581                 // radio buttons..
17582                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17583                 row.addClass('selected');
17584             }
17585             
17586             if(this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17588             {
17589                 
17590                 // checkboxes...
17591                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17592                 this.tickItems.push(d.data);
17593             }
17594             
17595             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17596             
17597         }, this);
17598         
17599         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17600         
17601         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17602
17603         if(this.modalTitle.length){
17604             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17605         }
17606
17607         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17608         
17609         if(this.mobile_restrict_height && listHeight < bodyHeight){
17610             this.touchViewBodyEl.setHeight(listHeight);
17611         }
17612         
17613         var _this = this;
17614         
17615         if(firstChecked && listHeight > bodyHeight){
17616             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17617         }
17618         
17619     },
17620     
17621     onTouchViewLoadException : function()
17622     {
17623         this.hideTouchView();
17624     },
17625     
17626     onTouchViewEmptyResults : function()
17627     {
17628         this.clearTouchView();
17629         
17630         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17631         
17632         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17633         
17634     },
17635     
17636     clearTouchView : function()
17637     {
17638         this.touchViewListGroup.dom.innerHTML = '';
17639     },
17640     
17641     onTouchViewClick : function(e, el, o)
17642     {
17643         e.preventDefault();
17644         
17645         var row = o.row;
17646         var rowIndex = o.rowIndex;
17647         
17648         var r = this.store.getAt(rowIndex);
17649         
17650         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17651             
17652             if(!this.multiple){
17653                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17654                     c.dom.removeAttribute('checked');
17655                 }, this);
17656
17657                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17658
17659                 this.setFromData(r.data);
17660
17661                 var close = this.closeTriggerEl();
17662
17663                 if(close){
17664                     close.show();
17665                 }
17666
17667                 this.hideTouchView();
17668
17669                 this.fireEvent('select', this, r, rowIndex);
17670
17671                 return;
17672             }
17673
17674             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17675                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17676                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17677                 return;
17678             }
17679
17680             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17681             this.addItem(r.data);
17682             this.tickItems.push(r.data);
17683         }
17684     },
17685     
17686     getAutoCreateNativeIOS : function()
17687     {
17688         var cfg = {
17689             cls: 'form-group' //input-group,
17690         };
17691         
17692         var combobox =  {
17693             tag: 'select',
17694             cls : 'roo-ios-select'
17695         };
17696         
17697         if (this.name) {
17698             combobox.name = this.name;
17699         }
17700         
17701         if (this.disabled) {
17702             combobox.disabled = true;
17703         }
17704         
17705         var settings = this;
17706         
17707         ['xs','sm','md','lg'].map(function(size){
17708             if (settings[size]) {
17709                 cfg.cls += ' col-' + size + '-' + settings[size];
17710             }
17711         });
17712         
17713         cfg.cn = combobox;
17714         
17715         return cfg;
17716         
17717     },
17718     
17719     initIOSView : function()
17720     {
17721         this.store.on('load', this.onIOSViewLoad, this);
17722         
17723         return;
17724     },
17725     
17726     onIOSViewLoad : function()
17727     {
17728         if(this.store.getCount() < 1){
17729             return;
17730         }
17731         
17732         this.clearIOSView();
17733         
17734         if(this.allowBlank) {
17735             
17736             var default_text = '-- SELECT --';
17737             
17738             if(this.placeholder.length){
17739                 default_text = this.placeholder;
17740             }
17741             
17742             if(this.emptyTitle.length){
17743                 default_text += ' - ' + this.emptyTitle + ' -';
17744             }
17745             
17746             var opt = this.inputEl().createChild({
17747                 tag: 'option',
17748                 value : 0,
17749                 html : default_text
17750             });
17751             
17752             var o = {};
17753             o[this.valueField] = 0;
17754             o[this.displayField] = default_text;
17755             
17756             this.ios_options.push({
17757                 data : o,
17758                 el : opt
17759             });
17760             
17761         }
17762         
17763         this.store.data.each(function(d, rowIndex){
17764             
17765             var html = '';
17766             
17767             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17768                 html = d.data[this.displayField];
17769             }
17770             
17771             var value = '';
17772             
17773             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17774                 value = d.data[this.valueField];
17775             }
17776             
17777             var option = {
17778                 tag: 'option',
17779                 value : value,
17780                 html : html
17781             };
17782             
17783             if(this.value == d.data[this.valueField]){
17784                 option['selected'] = true;
17785             }
17786             
17787             var opt = this.inputEl().createChild(option);
17788             
17789             this.ios_options.push({
17790                 data : d.data,
17791                 el : opt
17792             });
17793             
17794         }, this);
17795         
17796         this.inputEl().on('change', function(){
17797            this.fireEvent('select', this);
17798         }, this);
17799         
17800     },
17801     
17802     clearIOSView: function()
17803     {
17804         this.inputEl().dom.innerHTML = '';
17805         
17806         this.ios_options = [];
17807     },
17808     
17809     setIOSValue: function(v)
17810     {
17811         this.value = v;
17812         
17813         if(!this.ios_options){
17814             return;
17815         }
17816         
17817         Roo.each(this.ios_options, function(opts){
17818            
17819            opts.el.dom.removeAttribute('selected');
17820            
17821            if(opts.data[this.valueField] != v){
17822                return;
17823            }
17824            
17825            opts.el.dom.setAttribute('selected', true);
17826            
17827         }, this);
17828     }
17829
17830     /** 
17831     * @cfg {Boolean} grow 
17832     * @hide 
17833     */
17834     /** 
17835     * @cfg {Number} growMin 
17836     * @hide 
17837     */
17838     /** 
17839     * @cfg {Number} growMax 
17840     * @hide 
17841     */
17842     /**
17843      * @hide
17844      * @method autoSize
17845      */
17846 });
17847
17848 Roo.apply(Roo.bootstrap.ComboBox,  {
17849     
17850     header : {
17851         tag: 'div',
17852         cls: 'modal-header',
17853         cn: [
17854             {
17855                 tag: 'h4',
17856                 cls: 'modal-title'
17857             }
17858         ]
17859     },
17860     
17861     body : {
17862         tag: 'div',
17863         cls: 'modal-body',
17864         cn: [
17865             {
17866                 tag: 'ul',
17867                 cls: 'list-group'
17868             }
17869         ]
17870     },
17871     
17872     listItemRadio : {
17873         tag: 'li',
17874         cls: 'list-group-item',
17875         cn: [
17876             {
17877                 tag: 'span',
17878                 cls: 'roo-combobox-list-group-item-value'
17879             },
17880             {
17881                 tag: 'div',
17882                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17883                 cn: [
17884                     {
17885                         tag: 'input',
17886                         type: 'radio'
17887                     },
17888                     {
17889                         tag: 'label'
17890                     }
17891                 ]
17892             }
17893         ]
17894     },
17895     
17896     listItemCheckbox : {
17897         tag: 'li',
17898         cls: 'list-group-item',
17899         cn: [
17900             {
17901                 tag: 'span',
17902                 cls: 'roo-combobox-list-group-item-value'
17903             },
17904             {
17905                 tag: 'div',
17906                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17907                 cn: [
17908                     {
17909                         tag: 'input',
17910                         type: 'checkbox'
17911                     },
17912                     {
17913                         tag: 'label'
17914                     }
17915                 ]
17916             }
17917         ]
17918     },
17919     
17920     emptyResult : {
17921         tag: 'div',
17922         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17923     },
17924     
17925     footer : {
17926         tag: 'div',
17927         cls: 'modal-footer',
17928         cn: [
17929             {
17930                 tag: 'div',
17931                 cls: 'row',
17932                 cn: [
17933                     {
17934                         tag: 'div',
17935                         cls: 'col-xs-6 text-left',
17936                         cn: {
17937                             tag: 'button',
17938                             cls: 'btn btn-danger roo-touch-view-cancel',
17939                             html: 'Cancel'
17940                         }
17941                     },
17942                     {
17943                         tag: 'div',
17944                         cls: 'col-xs-6 text-right',
17945                         cn: {
17946                             tag: 'button',
17947                             cls: 'btn btn-success roo-touch-view-ok',
17948                             html: 'OK'
17949                         }
17950                     }
17951                 ]
17952             }
17953         ]
17954         
17955     }
17956 });
17957
17958 Roo.apply(Roo.bootstrap.ComboBox,  {
17959     
17960     touchViewTemplate : {
17961         tag: 'div',
17962         cls: 'modal fade roo-combobox-touch-view',
17963         cn: [
17964             {
17965                 tag: 'div',
17966                 cls: 'modal-dialog',
17967                 style : 'position:fixed', // we have to fix position....
17968                 cn: [
17969                     {
17970                         tag: 'div',
17971                         cls: 'modal-content',
17972                         cn: [
17973                             Roo.bootstrap.ComboBox.header,
17974                             Roo.bootstrap.ComboBox.body,
17975                             Roo.bootstrap.ComboBox.footer
17976                         ]
17977                     }
17978                 ]
17979             }
17980         ]
17981     }
17982 });/*
17983  * Based on:
17984  * Ext JS Library 1.1.1
17985  * Copyright(c) 2006-2007, Ext JS, LLC.
17986  *
17987  * Originally Released Under LGPL - original licence link has changed is not relivant.
17988  *
17989  * Fork - LGPL
17990  * <script type="text/javascript">
17991  */
17992
17993 /**
17994  * @class Roo.View
17995  * @extends Roo.util.Observable
17996  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17997  * This class also supports single and multi selection modes. <br>
17998  * Create a data model bound view:
17999  <pre><code>
18000  var store = new Roo.data.Store(...);
18001
18002  var view = new Roo.View({
18003     el : "my-element",
18004     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18005  
18006     singleSelect: true,
18007     selectedClass: "ydataview-selected",
18008     store: store
18009  });
18010
18011  // listen for node click?
18012  view.on("click", function(vw, index, node, e){
18013  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18014  });
18015
18016  // load XML data
18017  dataModel.load("foobar.xml");
18018  </code></pre>
18019  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18020  * <br><br>
18021  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18022  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18023  * 
18024  * Note: old style constructor is still suported (container, template, config)
18025  * 
18026  * @constructor
18027  * Create a new View
18028  * @param {Object} config The config object
18029  * 
18030  */
18031 Roo.View = function(config, depreciated_tpl, depreciated_config){
18032     
18033     this.parent = false;
18034     
18035     if (typeof(depreciated_tpl) == 'undefined') {
18036         // new way.. - universal constructor.
18037         Roo.apply(this, config);
18038         this.el  = Roo.get(this.el);
18039     } else {
18040         // old format..
18041         this.el  = Roo.get(config);
18042         this.tpl = depreciated_tpl;
18043         Roo.apply(this, depreciated_config);
18044     }
18045     this.wrapEl  = this.el.wrap().wrap();
18046     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18047     
18048     
18049     if(typeof(this.tpl) == "string"){
18050         this.tpl = new Roo.Template(this.tpl);
18051     } else {
18052         // support xtype ctors..
18053         this.tpl = new Roo.factory(this.tpl, Roo);
18054     }
18055     
18056     
18057     this.tpl.compile();
18058     
18059     /** @private */
18060     this.addEvents({
18061         /**
18062          * @event beforeclick
18063          * Fires before a click is processed. Returns false to cancel the default action.
18064          * @param {Roo.View} this
18065          * @param {Number} index The index of the target node
18066          * @param {HTMLElement} node The target node
18067          * @param {Roo.EventObject} e The raw event object
18068          */
18069             "beforeclick" : true,
18070         /**
18071          * @event click
18072          * Fires when a template node is clicked.
18073          * @param {Roo.View} this
18074          * @param {Number} index The index of the target node
18075          * @param {HTMLElement} node The target node
18076          * @param {Roo.EventObject} e The raw event object
18077          */
18078             "click" : true,
18079         /**
18080          * @event dblclick
18081          * Fires when a template node is double clicked.
18082          * @param {Roo.View} this
18083          * @param {Number} index The index of the target node
18084          * @param {HTMLElement} node The target node
18085          * @param {Roo.EventObject} e The raw event object
18086          */
18087             "dblclick" : true,
18088         /**
18089          * @event contextmenu
18090          * Fires when a template node is right clicked.
18091          * @param {Roo.View} this
18092          * @param {Number} index The index of the target node
18093          * @param {HTMLElement} node The target node
18094          * @param {Roo.EventObject} e The raw event object
18095          */
18096             "contextmenu" : true,
18097         /**
18098          * @event selectionchange
18099          * Fires when the selected nodes change.
18100          * @param {Roo.View} this
18101          * @param {Array} selections Array of the selected nodes
18102          */
18103             "selectionchange" : true,
18104     
18105         /**
18106          * @event beforeselect
18107          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18108          * @param {Roo.View} this
18109          * @param {HTMLElement} node The node to be selected
18110          * @param {Array} selections Array of currently selected nodes
18111          */
18112             "beforeselect" : true,
18113         /**
18114          * @event preparedata
18115          * Fires on every row to render, to allow you to change the data.
18116          * @param {Roo.View} this
18117          * @param {Object} data to be rendered (change this)
18118          */
18119           "preparedata" : true
18120           
18121           
18122         });
18123
18124
18125
18126     this.el.on({
18127         "click": this.onClick,
18128         "dblclick": this.onDblClick,
18129         "contextmenu": this.onContextMenu,
18130         scope:this
18131     });
18132
18133     this.selections = [];
18134     this.nodes = [];
18135     this.cmp = new Roo.CompositeElementLite([]);
18136     if(this.store){
18137         this.store = Roo.factory(this.store, Roo.data);
18138         this.setStore(this.store, true);
18139     }
18140     
18141     if ( this.footer && this.footer.xtype) {
18142            
18143          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18144         
18145         this.footer.dataSource = this.store;
18146         this.footer.container = fctr;
18147         this.footer = Roo.factory(this.footer, Roo);
18148         fctr.insertFirst(this.el);
18149         
18150         // this is a bit insane - as the paging toolbar seems to detach the el..
18151 //        dom.parentNode.parentNode.parentNode
18152          // they get detached?
18153     }
18154     
18155     
18156     Roo.View.superclass.constructor.call(this);
18157     
18158     
18159 };
18160
18161 Roo.extend(Roo.View, Roo.util.Observable, {
18162     
18163      /**
18164      * @cfg {Roo.data.Store} store Data store to load data from.
18165      */
18166     store : false,
18167     
18168     /**
18169      * @cfg {String|Roo.Element} el The container element.
18170      */
18171     el : '',
18172     
18173     /**
18174      * @cfg {String|Roo.Template} tpl The template used by this View 
18175      */
18176     tpl : false,
18177     /**
18178      * @cfg {String} dataName the named area of the template to use as the data area
18179      *                          Works with domtemplates roo-name="name"
18180      */
18181     dataName: false,
18182     /**
18183      * @cfg {String} selectedClass The css class to add to selected nodes
18184      */
18185     selectedClass : "x-view-selected",
18186      /**
18187      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18188      */
18189     emptyText : "",
18190     
18191     /**
18192      * @cfg {String} text to display on mask (default Loading)
18193      */
18194     mask : false,
18195     /**
18196      * @cfg {Boolean} multiSelect Allow multiple selection
18197      */
18198     multiSelect : false,
18199     /**
18200      * @cfg {Boolean} singleSelect Allow single selection
18201      */
18202     singleSelect:  false,
18203     
18204     /**
18205      * @cfg {Boolean} toggleSelect - selecting 
18206      */
18207     toggleSelect : false,
18208     
18209     /**
18210      * @cfg {Boolean} tickable - selecting 
18211      */
18212     tickable : false,
18213     
18214     /**
18215      * Returns the element this view is bound to.
18216      * @return {Roo.Element}
18217      */
18218     getEl : function(){
18219         return this.wrapEl;
18220     },
18221     
18222     
18223
18224     /**
18225      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18226      */
18227     refresh : function(){
18228         //Roo.log('refresh');
18229         var t = this.tpl;
18230         
18231         // if we are using something like 'domtemplate', then
18232         // the what gets used is:
18233         // t.applySubtemplate(NAME, data, wrapping data..)
18234         // the outer template then get' applied with
18235         //     the store 'extra data'
18236         // and the body get's added to the
18237         //      roo-name="data" node?
18238         //      <span class='roo-tpl-{name}'></span> ?????
18239         
18240         
18241         
18242         this.clearSelections();
18243         this.el.update("");
18244         var html = [];
18245         var records = this.store.getRange();
18246         if(records.length < 1) {
18247             
18248             // is this valid??  = should it render a template??
18249             
18250             this.el.update(this.emptyText);
18251             return;
18252         }
18253         var el = this.el;
18254         if (this.dataName) {
18255             this.el.update(t.apply(this.store.meta)); //????
18256             el = this.el.child('.roo-tpl-' + this.dataName);
18257         }
18258         
18259         for(var i = 0, len = records.length; i < len; i++){
18260             var data = this.prepareData(records[i].data, i, records[i]);
18261             this.fireEvent("preparedata", this, data, i, records[i]);
18262             
18263             var d = Roo.apply({}, data);
18264             
18265             if(this.tickable){
18266                 Roo.apply(d, {'roo-id' : Roo.id()});
18267                 
18268                 var _this = this;
18269             
18270                 Roo.each(this.parent.item, function(item){
18271                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18272                         return;
18273                     }
18274                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18275                 });
18276             }
18277             
18278             html[html.length] = Roo.util.Format.trim(
18279                 this.dataName ?
18280                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18281                     t.apply(d)
18282             );
18283         }
18284         
18285         
18286         
18287         el.update(html.join(""));
18288         this.nodes = el.dom.childNodes;
18289         this.updateIndexes(0);
18290     },
18291     
18292
18293     /**
18294      * Function to override to reformat the data that is sent to
18295      * the template for each node.
18296      * DEPRICATED - use the preparedata event handler.
18297      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18298      * a JSON object for an UpdateManager bound view).
18299      */
18300     prepareData : function(data, index, record)
18301     {
18302         this.fireEvent("preparedata", this, data, index, record);
18303         return data;
18304     },
18305
18306     onUpdate : function(ds, record){
18307         // Roo.log('on update');   
18308         this.clearSelections();
18309         var index = this.store.indexOf(record);
18310         var n = this.nodes[index];
18311         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18312         n.parentNode.removeChild(n);
18313         this.updateIndexes(index, index);
18314     },
18315
18316     
18317     
18318 // --------- FIXME     
18319     onAdd : function(ds, records, index)
18320     {
18321         //Roo.log(['on Add', ds, records, index] );        
18322         this.clearSelections();
18323         if(this.nodes.length == 0){
18324             this.refresh();
18325             return;
18326         }
18327         var n = this.nodes[index];
18328         for(var i = 0, len = records.length; i < len; i++){
18329             var d = this.prepareData(records[i].data, i, records[i]);
18330             if(n){
18331                 this.tpl.insertBefore(n, d);
18332             }else{
18333                 
18334                 this.tpl.append(this.el, d);
18335             }
18336         }
18337         this.updateIndexes(index);
18338     },
18339
18340     onRemove : function(ds, record, index){
18341        // Roo.log('onRemove');
18342         this.clearSelections();
18343         var el = this.dataName  ?
18344             this.el.child('.roo-tpl-' + this.dataName) :
18345             this.el; 
18346         
18347         el.dom.removeChild(this.nodes[index]);
18348         this.updateIndexes(index);
18349     },
18350
18351     /**
18352      * Refresh an individual node.
18353      * @param {Number} index
18354      */
18355     refreshNode : function(index){
18356         this.onUpdate(this.store, this.store.getAt(index));
18357     },
18358
18359     updateIndexes : function(startIndex, endIndex){
18360         var ns = this.nodes;
18361         startIndex = startIndex || 0;
18362         endIndex = endIndex || ns.length - 1;
18363         for(var i = startIndex; i <= endIndex; i++){
18364             ns[i].nodeIndex = i;
18365         }
18366     },
18367
18368     /**
18369      * Changes the data store this view uses and refresh the view.
18370      * @param {Store} store
18371      */
18372     setStore : function(store, initial){
18373         if(!initial && this.store){
18374             this.store.un("datachanged", this.refresh);
18375             this.store.un("add", this.onAdd);
18376             this.store.un("remove", this.onRemove);
18377             this.store.un("update", this.onUpdate);
18378             this.store.un("clear", this.refresh);
18379             this.store.un("beforeload", this.onBeforeLoad);
18380             this.store.un("load", this.onLoad);
18381             this.store.un("loadexception", this.onLoad);
18382         }
18383         if(store){
18384           
18385             store.on("datachanged", this.refresh, this);
18386             store.on("add", this.onAdd, this);
18387             store.on("remove", this.onRemove, this);
18388             store.on("update", this.onUpdate, this);
18389             store.on("clear", this.refresh, this);
18390             store.on("beforeload", this.onBeforeLoad, this);
18391             store.on("load", this.onLoad, this);
18392             store.on("loadexception", this.onLoad, this);
18393         }
18394         
18395         if(store){
18396             this.refresh();
18397         }
18398     },
18399     /**
18400      * onbeforeLoad - masks the loading area.
18401      *
18402      */
18403     onBeforeLoad : function(store,opts)
18404     {
18405          //Roo.log('onBeforeLoad');   
18406         if (!opts.add) {
18407             this.el.update("");
18408         }
18409         this.el.mask(this.mask ? this.mask : "Loading" ); 
18410     },
18411     onLoad : function ()
18412     {
18413         this.el.unmask();
18414     },
18415     
18416
18417     /**
18418      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18419      * @param {HTMLElement} node
18420      * @return {HTMLElement} The template node
18421      */
18422     findItemFromChild : function(node){
18423         var el = this.dataName  ?
18424             this.el.child('.roo-tpl-' + this.dataName,true) :
18425             this.el.dom; 
18426         
18427         if(!node || node.parentNode == el){
18428                     return node;
18429             }
18430             var p = node.parentNode;
18431             while(p && p != el){
18432             if(p.parentNode == el){
18433                 return p;
18434             }
18435             p = p.parentNode;
18436         }
18437             return null;
18438     },
18439
18440     /** @ignore */
18441     onClick : function(e){
18442         var item = this.findItemFromChild(e.getTarget());
18443         if(item){
18444             var index = this.indexOf(item);
18445             if(this.onItemClick(item, index, e) !== false){
18446                 this.fireEvent("click", this, index, item, e);
18447             }
18448         }else{
18449             this.clearSelections();
18450         }
18451     },
18452
18453     /** @ignore */
18454     onContextMenu : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18458         }
18459     },
18460
18461     /** @ignore */
18462     onDblClick : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     onItemClick : function(item, index, e)
18470     {
18471         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18472             return false;
18473         }
18474         if (this.toggleSelect) {
18475             var m = this.isSelected(item) ? 'unselect' : 'select';
18476             //Roo.log(m);
18477             var _t = this;
18478             _t[m](item, true, false);
18479             return true;
18480         }
18481         if(this.multiSelect || this.singleSelect){
18482             if(this.multiSelect && e.shiftKey && this.lastSelection){
18483                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18484             }else{
18485                 this.select(item, this.multiSelect && e.ctrlKey);
18486                 this.lastSelection = item;
18487             }
18488             
18489             if(!this.tickable){
18490                 e.preventDefault();
18491             }
18492             
18493         }
18494         return true;
18495     },
18496
18497     /**
18498      * Get the number of selected nodes.
18499      * @return {Number}
18500      */
18501     getSelectionCount : function(){
18502         return this.selections.length;
18503     },
18504
18505     /**
18506      * Get the currently selected nodes.
18507      * @return {Array} An array of HTMLElements
18508      */
18509     getSelectedNodes : function(){
18510         return this.selections;
18511     },
18512
18513     /**
18514      * Get the indexes of the selected nodes.
18515      * @return {Array}
18516      */
18517     getSelectedIndexes : function(){
18518         var indexes = [], s = this.selections;
18519         for(var i = 0, len = s.length; i < len; i++){
18520             indexes.push(s[i].nodeIndex);
18521         }
18522         return indexes;
18523     },
18524
18525     /**
18526      * Clear all selections
18527      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18528      */
18529     clearSelections : function(suppressEvent){
18530         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18531             this.cmp.elements = this.selections;
18532             this.cmp.removeClass(this.selectedClass);
18533             this.selections = [];
18534             if(!suppressEvent){
18535                 this.fireEvent("selectionchange", this, this.selections);
18536             }
18537         }
18538     },
18539
18540     /**
18541      * Returns true if the passed node is selected
18542      * @param {HTMLElement/Number} node The node or node index
18543      * @return {Boolean}
18544      */
18545     isSelected : function(node){
18546         var s = this.selections;
18547         if(s.length < 1){
18548             return false;
18549         }
18550         node = this.getNode(node);
18551         return s.indexOf(node) !== -1;
18552     },
18553
18554     /**
18555      * Selects nodes.
18556      * @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
18557      * @param {Boolean} keepExisting (optional) true to keep existing selections
18558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18559      */
18560     select : function(nodeInfo, keepExisting, suppressEvent){
18561         if(nodeInfo instanceof Array){
18562             if(!keepExisting){
18563                 this.clearSelections(true);
18564             }
18565             for(var i = 0, len = nodeInfo.length; i < len; i++){
18566                 this.select(nodeInfo[i], true, true);
18567             }
18568             return;
18569         } 
18570         var node = this.getNode(nodeInfo);
18571         if(!node || this.isSelected(node)){
18572             return; // already selected.
18573         }
18574         if(!keepExisting){
18575             this.clearSelections(true);
18576         }
18577         
18578         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18579             Roo.fly(node).addClass(this.selectedClass);
18580             this.selections.push(node);
18581             if(!suppressEvent){
18582                 this.fireEvent("selectionchange", this, this.selections);
18583             }
18584         }
18585         
18586         
18587     },
18588       /**
18589      * Unselects nodes.
18590      * @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
18591      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18592      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18593      */
18594     unselect : function(nodeInfo, keepExisting, suppressEvent)
18595     {
18596         if(nodeInfo instanceof Array){
18597             Roo.each(this.selections, function(s) {
18598                 this.unselect(s, nodeInfo);
18599             }, this);
18600             return;
18601         }
18602         var node = this.getNode(nodeInfo);
18603         if(!node || !this.isSelected(node)){
18604             //Roo.log("not selected");
18605             return; // not selected.
18606         }
18607         // fireevent???
18608         var ns = [];
18609         Roo.each(this.selections, function(s) {
18610             if (s == node ) {
18611                 Roo.fly(node).removeClass(this.selectedClass);
18612
18613                 return;
18614             }
18615             ns.push(s);
18616         },this);
18617         
18618         this.selections= ns;
18619         this.fireEvent("selectionchange", this, this.selections);
18620     },
18621
18622     /**
18623      * Gets a template node.
18624      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18625      * @return {HTMLElement} The node or null if it wasn't found
18626      */
18627     getNode : function(nodeInfo){
18628         if(typeof nodeInfo == "string"){
18629             return document.getElementById(nodeInfo);
18630         }else if(typeof nodeInfo == "number"){
18631             return this.nodes[nodeInfo];
18632         }
18633         return nodeInfo;
18634     },
18635
18636     /**
18637      * Gets a range template nodes.
18638      * @param {Number} startIndex
18639      * @param {Number} endIndex
18640      * @return {Array} An array of nodes
18641      */
18642     getNodes : function(start, end){
18643         var ns = this.nodes;
18644         start = start || 0;
18645         end = typeof end == "undefined" ? ns.length - 1 : end;
18646         var nodes = [];
18647         if(start <= end){
18648             for(var i = start; i <= end; i++){
18649                 nodes.push(ns[i]);
18650             }
18651         } else{
18652             for(var i = start; i >= end; i--){
18653                 nodes.push(ns[i]);
18654             }
18655         }
18656         return nodes;
18657     },
18658
18659     /**
18660      * Finds the index of the passed node
18661      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18662      * @return {Number} The index of the node or -1
18663      */
18664     indexOf : function(node){
18665         node = this.getNode(node);
18666         if(typeof node.nodeIndex == "number"){
18667             return node.nodeIndex;
18668         }
18669         var ns = this.nodes;
18670         for(var i = 0, len = ns.length; i < len; i++){
18671             if(ns[i] == node){
18672                 return i;
18673             }
18674         }
18675         return -1;
18676     }
18677 });
18678 /*
18679  * - LGPL
18680  *
18681  * based on jquery fullcalendar
18682  * 
18683  */
18684
18685 Roo.bootstrap = Roo.bootstrap || {};
18686 /**
18687  * @class Roo.bootstrap.Calendar
18688  * @extends Roo.bootstrap.Component
18689  * Bootstrap Calendar class
18690  * @cfg {Boolean} loadMask (true|false) default false
18691  * @cfg {Object} header generate the user specific header of the calendar, default false
18692
18693  * @constructor
18694  * Create a new Container
18695  * @param {Object} config The config object
18696  */
18697
18698
18699
18700 Roo.bootstrap.Calendar = function(config){
18701     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18702      this.addEvents({
18703         /**
18704              * @event select
18705              * Fires when a date is selected
18706              * @param {DatePicker} this
18707              * @param {Date} date The selected date
18708              */
18709         'select': true,
18710         /**
18711              * @event monthchange
18712              * Fires when the displayed month changes 
18713              * @param {DatePicker} this
18714              * @param {Date} date The selected month
18715              */
18716         'monthchange': true,
18717         /**
18718              * @event evententer
18719              * Fires when mouse over an event
18720              * @param {Calendar} this
18721              * @param {event} Event
18722              */
18723         'evententer': true,
18724         /**
18725              * @event eventleave
18726              * Fires when the mouse leaves an
18727              * @param {Calendar} this
18728              * @param {event}
18729              */
18730         'eventleave': true,
18731         /**
18732              * @event eventclick
18733              * Fires when the mouse click an
18734              * @param {Calendar} this
18735              * @param {event}
18736              */
18737         'eventclick': true
18738         
18739     });
18740
18741 };
18742
18743 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18744     
18745      /**
18746      * @cfg {Number} startDay
18747      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18748      */
18749     startDay : 0,
18750     
18751     loadMask : false,
18752     
18753     header : false,
18754       
18755     getAutoCreate : function(){
18756         
18757         
18758         var fc_button = function(name, corner, style, content ) {
18759             return Roo.apply({},{
18760                 tag : 'span',
18761                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18762                          (corner.length ?
18763                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18764                             ''
18765                         ),
18766                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18767                 unselectable: 'on'
18768             });
18769         };
18770         
18771         var header = {};
18772         
18773         if(!this.header){
18774             header = {
18775                 tag : 'table',
18776                 cls : 'fc-header',
18777                 style : 'width:100%',
18778                 cn : [
18779                     {
18780                         tag: 'tr',
18781                         cn : [
18782                             {
18783                                 tag : 'td',
18784                                 cls : 'fc-header-left',
18785                                 cn : [
18786                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18787                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18788                                     { tag: 'span', cls: 'fc-header-space' },
18789                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18790
18791
18792                                 ]
18793                             },
18794
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-center',
18798                                 cn : [
18799                                     {
18800                                         tag: 'span',
18801                                         cls: 'fc-header-title',
18802                                         cn : {
18803                                             tag: 'H2',
18804                                             html : 'month / year'
18805                                         }
18806                                     }
18807
18808                                 ]
18809                             },
18810                             {
18811                                 tag : 'td',
18812                                 cls : 'fc-header-right',
18813                                 cn : [
18814                               /*      fc_button('month', 'left', '', 'month' ),
18815                                     fc_button('week', '', '', 'week' ),
18816                                     fc_button('day', 'right', '', 'day' )
18817                                 */    
18818
18819                                 ]
18820                             }
18821
18822                         ]
18823                     }
18824                 ]
18825             };
18826         }
18827         
18828         header = this.header;
18829         
18830        
18831         var cal_heads = function() {
18832             var ret = [];
18833             // fixme - handle this.
18834             
18835             for (var i =0; i < Date.dayNames.length; i++) {
18836                 var d = Date.dayNames[i];
18837                 ret.push({
18838                     tag: 'th',
18839                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18840                     html : d.substring(0,3)
18841                 });
18842                 
18843             }
18844             ret[0].cls += ' fc-first';
18845             ret[6].cls += ' fc-last';
18846             return ret;
18847         };
18848         var cal_cell = function(n) {
18849             return  {
18850                 tag: 'td',
18851                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18852                 cn : [
18853                     {
18854                         cn : [
18855                             {
18856                                 cls: 'fc-day-number',
18857                                 html: 'D'
18858                             },
18859                             {
18860                                 cls: 'fc-day-content',
18861                              
18862                                 cn : [
18863                                      {
18864                                         style: 'position: relative;' // height: 17px;
18865                                     }
18866                                 ]
18867                             }
18868                             
18869                             
18870                         ]
18871                     }
18872                 ]
18873                 
18874             }
18875         };
18876         var cal_rows = function() {
18877             
18878             var ret = [];
18879             for (var r = 0; r < 6; r++) {
18880                 var row= {
18881                     tag : 'tr',
18882                     cls : 'fc-week',
18883                     cn : []
18884                 };
18885                 
18886                 for (var i =0; i < Date.dayNames.length; i++) {
18887                     var d = Date.dayNames[i];
18888                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18889
18890                 }
18891                 row.cn[0].cls+=' fc-first';
18892                 row.cn[0].cn[0].style = 'min-height:90px';
18893                 row.cn[6].cls+=' fc-last';
18894                 ret.push(row);
18895                 
18896             }
18897             ret[0].cls += ' fc-first';
18898             ret[4].cls += ' fc-prev-last';
18899             ret[5].cls += ' fc-last';
18900             return ret;
18901             
18902         };
18903         
18904         var cal_table = {
18905             tag: 'table',
18906             cls: 'fc-border-separate',
18907             style : 'width:100%',
18908             cellspacing  : 0,
18909             cn : [
18910                 { 
18911                     tag: 'thead',
18912                     cn : [
18913                         { 
18914                             tag: 'tr',
18915                             cls : 'fc-first fc-last',
18916                             cn : cal_heads()
18917                         }
18918                     ]
18919                 },
18920                 { 
18921                     tag: 'tbody',
18922                     cn : cal_rows()
18923                 }
18924                   
18925             ]
18926         };
18927          
18928          var cfg = {
18929             cls : 'fc fc-ltr',
18930             cn : [
18931                 header,
18932                 {
18933                     cls : 'fc-content',
18934                     style : "position: relative;",
18935                     cn : [
18936                         {
18937                             cls : 'fc-view fc-view-month fc-grid',
18938                             style : 'position: relative',
18939                             unselectable : 'on',
18940                             cn : [
18941                                 {
18942                                     cls : 'fc-event-container',
18943                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18944                                 },
18945                                 cal_table
18946                             ]
18947                         }
18948                     ]
18949     
18950                 }
18951            ] 
18952             
18953         };
18954         
18955          
18956         
18957         return cfg;
18958     },
18959     
18960     
18961     initEvents : function()
18962     {
18963         if(!this.store){
18964             throw "can not find store for calendar";
18965         }
18966         
18967         var mark = {
18968             tag: "div",
18969             cls:"x-dlg-mask",
18970             style: "text-align:center",
18971             cn: [
18972                 {
18973                     tag: "div",
18974                     style: "background-color:white;width:50%;margin:250 auto",
18975                     cn: [
18976                         {
18977                             tag: "img",
18978                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18979                         },
18980                         {
18981                             tag: "span",
18982                             html: "Loading"
18983                         }
18984                         
18985                     ]
18986                 }
18987             ]
18988         };
18989         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18990         
18991         var size = this.el.select('.fc-content', true).first().getSize();
18992         this.maskEl.setSize(size.width, size.height);
18993         this.maskEl.enableDisplayMode("block");
18994         if(!this.loadMask){
18995             this.maskEl.hide();
18996         }
18997         
18998         this.store = Roo.factory(this.store, Roo.data);
18999         this.store.on('load', this.onLoad, this);
19000         this.store.on('beforeload', this.onBeforeLoad, this);
19001         
19002         this.resize();
19003         
19004         this.cells = this.el.select('.fc-day',true);
19005         //Roo.log(this.cells);
19006         this.textNodes = this.el.query('.fc-day-number');
19007         this.cells.addClassOnOver('fc-state-hover');
19008         
19009         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19010         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19011         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19012         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19013         
19014         this.on('monthchange', this.onMonthChange, this);
19015         
19016         this.update(new Date().clearTime());
19017     },
19018     
19019     resize : function() {
19020         var sz  = this.el.getSize();
19021         
19022         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19023         this.el.select('.fc-day-content div',true).setHeight(34);
19024     },
19025     
19026     
19027     // private
19028     showPrevMonth : function(e){
19029         this.update(this.activeDate.add("mo", -1));
19030     },
19031     showToday : function(e){
19032         this.update(new Date().clearTime());
19033     },
19034     // private
19035     showNextMonth : function(e){
19036         this.update(this.activeDate.add("mo", 1));
19037     },
19038
19039     // private
19040     showPrevYear : function(){
19041         this.update(this.activeDate.add("y", -1));
19042     },
19043
19044     // private
19045     showNextYear : function(){
19046         this.update(this.activeDate.add("y", 1));
19047     },
19048
19049     
19050    // private
19051     update : function(date)
19052     {
19053         var vd = this.activeDate;
19054         this.activeDate = date;
19055 //        if(vd && this.el){
19056 //            var t = date.getTime();
19057 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19058 //                Roo.log('using add remove');
19059 //                
19060 //                this.fireEvent('monthchange', this, date);
19061 //                
19062 //                this.cells.removeClass("fc-state-highlight");
19063 //                this.cells.each(function(c){
19064 //                   if(c.dateValue == t){
19065 //                       c.addClass("fc-state-highlight");
19066 //                       setTimeout(function(){
19067 //                            try{c.dom.firstChild.focus();}catch(e){}
19068 //                       }, 50);
19069 //                       return false;
19070 //                   }
19071 //                   return true;
19072 //                });
19073 //                return;
19074 //            }
19075 //        }
19076         
19077         var days = date.getDaysInMonth();
19078         
19079         var firstOfMonth = date.getFirstDateOfMonth();
19080         var startingPos = firstOfMonth.getDay()-this.startDay;
19081         
19082         if(startingPos < this.startDay){
19083             startingPos += 7;
19084         }
19085         
19086         var pm = date.add(Date.MONTH, -1);
19087         var prevStart = pm.getDaysInMonth()-startingPos;
19088 //        
19089         this.cells = this.el.select('.fc-day',true);
19090         this.textNodes = this.el.query('.fc-day-number');
19091         this.cells.addClassOnOver('fc-state-hover');
19092         
19093         var cells = this.cells.elements;
19094         var textEls = this.textNodes;
19095         
19096         Roo.each(cells, function(cell){
19097             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19098         });
19099         
19100         days += startingPos;
19101
19102         // convert everything to numbers so it's fast
19103         var day = 86400000;
19104         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19105         //Roo.log(d);
19106         //Roo.log(pm);
19107         //Roo.log(prevStart);
19108         
19109         var today = new Date().clearTime().getTime();
19110         var sel = date.clearTime().getTime();
19111         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19112         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19113         var ddMatch = this.disabledDatesRE;
19114         var ddText = this.disabledDatesText;
19115         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19116         var ddaysText = this.disabledDaysText;
19117         var format = this.format;
19118         
19119         var setCellClass = function(cal, cell){
19120             cell.row = 0;
19121             cell.events = [];
19122             cell.more = [];
19123             //Roo.log('set Cell Class');
19124             cell.title = "";
19125             var t = d.getTime();
19126             
19127             //Roo.log(d);
19128             
19129             cell.dateValue = t;
19130             if(t == today){
19131                 cell.className += " fc-today";
19132                 cell.className += " fc-state-highlight";
19133                 cell.title = cal.todayText;
19134             }
19135             if(t == sel){
19136                 // disable highlight in other month..
19137                 //cell.className += " fc-state-highlight";
19138                 
19139             }
19140             // disabling
19141             if(t < min) {
19142                 cell.className = " fc-state-disabled";
19143                 cell.title = cal.minText;
19144                 return;
19145             }
19146             if(t > max) {
19147                 cell.className = " fc-state-disabled";
19148                 cell.title = cal.maxText;
19149                 return;
19150             }
19151             if(ddays){
19152                 if(ddays.indexOf(d.getDay()) != -1){
19153                     cell.title = ddaysText;
19154                     cell.className = " fc-state-disabled";
19155                 }
19156             }
19157             if(ddMatch && format){
19158                 var fvalue = d.dateFormat(format);
19159                 if(ddMatch.test(fvalue)){
19160                     cell.title = ddText.replace("%0", fvalue);
19161                     cell.className = " fc-state-disabled";
19162                 }
19163             }
19164             
19165             if (!cell.initialClassName) {
19166                 cell.initialClassName = cell.dom.className;
19167             }
19168             
19169             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19170         };
19171
19172         var i = 0;
19173         
19174         for(; i < startingPos; i++) {
19175             textEls[i].innerHTML = (++prevStart);
19176             d.setDate(d.getDate()+1);
19177             
19178             cells[i].className = "fc-past fc-other-month";
19179             setCellClass(this, cells[i]);
19180         }
19181         
19182         var intDay = 0;
19183         
19184         for(; i < days; i++){
19185             intDay = i - startingPos + 1;
19186             textEls[i].innerHTML = (intDay);
19187             d.setDate(d.getDate()+1);
19188             
19189             cells[i].className = ''; // "x-date-active";
19190             setCellClass(this, cells[i]);
19191         }
19192         var extraDays = 0;
19193         
19194         for(; i < 42; i++) {
19195             textEls[i].innerHTML = (++extraDays);
19196             d.setDate(d.getDate()+1);
19197             
19198             cells[i].className = "fc-future fc-other-month";
19199             setCellClass(this, cells[i]);
19200         }
19201         
19202         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19203         
19204         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19205         
19206         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19207         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19208         
19209         if(totalRows != 6){
19210             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19211             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19212         }
19213         
19214         this.fireEvent('monthchange', this, date);
19215         
19216         
19217         /*
19218         if(!this.internalRender){
19219             var main = this.el.dom.firstChild;
19220             var w = main.offsetWidth;
19221             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19222             Roo.fly(main).setWidth(w);
19223             this.internalRender = true;
19224             // opera does not respect the auto grow header center column
19225             // then, after it gets a width opera refuses to recalculate
19226             // without a second pass
19227             if(Roo.isOpera && !this.secondPass){
19228                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19229                 this.secondPass = true;
19230                 this.update.defer(10, this, [date]);
19231             }
19232         }
19233         */
19234         
19235     },
19236     
19237     findCell : function(dt) {
19238         dt = dt.clearTime().getTime();
19239         var ret = false;
19240         this.cells.each(function(c){
19241             //Roo.log("check " +c.dateValue + '?=' + dt);
19242             if(c.dateValue == dt){
19243                 ret = c;
19244                 return false;
19245             }
19246             return true;
19247         });
19248         
19249         return ret;
19250     },
19251     
19252     findCells : function(ev) {
19253         var s = ev.start.clone().clearTime().getTime();
19254        // Roo.log(s);
19255         var e= ev.end.clone().clearTime().getTime();
19256        // Roo.log(e);
19257         var ret = [];
19258         this.cells.each(function(c){
19259              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19260             
19261             if(c.dateValue > e){
19262                 return ;
19263             }
19264             if(c.dateValue < s){
19265                 return ;
19266             }
19267             ret.push(c);
19268         });
19269         
19270         return ret;    
19271     },
19272     
19273 //    findBestRow: function(cells)
19274 //    {
19275 //        var ret = 0;
19276 //        
19277 //        for (var i =0 ; i < cells.length;i++) {
19278 //            ret  = Math.max(cells[i].rows || 0,ret);
19279 //        }
19280 //        return ret;
19281 //        
19282 //    },
19283     
19284     
19285     addItem : function(ev)
19286     {
19287         // look for vertical location slot in
19288         var cells = this.findCells(ev);
19289         
19290 //        ev.row = this.findBestRow(cells);
19291         
19292         // work out the location.
19293         
19294         var crow = false;
19295         var rows = [];
19296         for(var i =0; i < cells.length; i++) {
19297             
19298             cells[i].row = cells[0].row;
19299             
19300             if(i == 0){
19301                 cells[i].row = cells[i].row + 1;
19302             }
19303             
19304             if (!crow) {
19305                 crow = {
19306                     start : cells[i],
19307                     end :  cells[i]
19308                 };
19309                 continue;
19310             }
19311             if (crow.start.getY() == cells[i].getY()) {
19312                 // on same row.
19313                 crow.end = cells[i];
19314                 continue;
19315             }
19316             // different row.
19317             rows.push(crow);
19318             crow = {
19319                 start: cells[i],
19320                 end : cells[i]
19321             };
19322             
19323         }
19324         
19325         rows.push(crow);
19326         ev.els = [];
19327         ev.rows = rows;
19328         ev.cells = cells;
19329         
19330         cells[0].events.push(ev);
19331         
19332         this.calevents.push(ev);
19333     },
19334     
19335     clearEvents: function() {
19336         
19337         if(!this.calevents){
19338             return;
19339         }
19340         
19341         Roo.each(this.cells.elements, function(c){
19342             c.row = 0;
19343             c.events = [];
19344             c.more = [];
19345         });
19346         
19347         Roo.each(this.calevents, function(e) {
19348             Roo.each(e.els, function(el) {
19349                 el.un('mouseenter' ,this.onEventEnter, this);
19350                 el.un('mouseleave' ,this.onEventLeave, this);
19351                 el.remove();
19352             },this);
19353         },this);
19354         
19355         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19356             e.remove();
19357         });
19358         
19359     },
19360     
19361     renderEvents: function()
19362     {   
19363         var _this = this;
19364         
19365         this.cells.each(function(c) {
19366             
19367             if(c.row < 5){
19368                 return;
19369             }
19370             
19371             var ev = c.events;
19372             
19373             var r = 4;
19374             if(c.row != c.events.length){
19375                 r = 4 - (4 - (c.row - c.events.length));
19376             }
19377             
19378             c.events = ev.slice(0, r);
19379             c.more = ev.slice(r);
19380             
19381             if(c.more.length && c.more.length == 1){
19382                 c.events.push(c.more.pop());
19383             }
19384             
19385             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19386             
19387         });
19388             
19389         this.cells.each(function(c) {
19390             
19391             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19392             
19393             
19394             for (var e = 0; e < c.events.length; e++){
19395                 var ev = c.events[e];
19396                 var rows = ev.rows;
19397                 
19398                 for(var i = 0; i < rows.length; i++) {
19399                 
19400                     // how many rows should it span..
19401
19402                     var  cfg = {
19403                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19404                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19405
19406                         unselectable : "on",
19407                         cn : [
19408                             {
19409                                 cls: 'fc-event-inner',
19410                                 cn : [
19411     //                                {
19412     //                                  tag:'span',
19413     //                                  cls: 'fc-event-time',
19414     //                                  html : cells.length > 1 ? '' : ev.time
19415     //                                },
19416                                     {
19417                                       tag:'span',
19418                                       cls: 'fc-event-title',
19419                                       html : String.format('{0}', ev.title)
19420                                     }
19421
19422
19423                                 ]
19424                             },
19425                             {
19426                                 cls: 'ui-resizable-handle ui-resizable-e',
19427                                 html : '&nbsp;&nbsp;&nbsp'
19428                             }
19429
19430                         ]
19431                     };
19432
19433                     if (i == 0) {
19434                         cfg.cls += ' fc-event-start';
19435                     }
19436                     if ((i+1) == rows.length) {
19437                         cfg.cls += ' fc-event-end';
19438                     }
19439
19440                     var ctr = _this.el.select('.fc-event-container',true).first();
19441                     var cg = ctr.createChild(cfg);
19442
19443                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19444                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19445
19446                     var r = (c.more.length) ? 1 : 0;
19447                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19448                     cg.setWidth(ebox.right - sbox.x -2);
19449
19450                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19451                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19452                     cg.on('click', _this.onEventClick, _this, ev);
19453
19454                     ev.els.push(cg);
19455                     
19456                 }
19457                 
19458             }
19459             
19460             
19461             if(c.more.length){
19462                 var  cfg = {
19463                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19464                     style : 'position: absolute',
19465                     unselectable : "on",
19466                     cn : [
19467                         {
19468                             cls: 'fc-event-inner',
19469                             cn : [
19470                                 {
19471                                   tag:'span',
19472                                   cls: 'fc-event-title',
19473                                   html : 'More'
19474                                 }
19475
19476
19477                             ]
19478                         },
19479                         {
19480                             cls: 'ui-resizable-handle ui-resizable-e',
19481                             html : '&nbsp;&nbsp;&nbsp'
19482                         }
19483
19484                     ]
19485                 };
19486
19487                 var ctr = _this.el.select('.fc-event-container',true).first();
19488                 var cg = ctr.createChild(cfg);
19489
19490                 var sbox = c.select('.fc-day-content',true).first().getBox();
19491                 var ebox = c.select('.fc-day-content',true).first().getBox();
19492                 //Roo.log(cg);
19493                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19494                 cg.setWidth(ebox.right - sbox.x -2);
19495
19496                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19497                 
19498             }
19499             
19500         });
19501         
19502         
19503         
19504     },
19505     
19506     onEventEnter: function (e, el,event,d) {
19507         this.fireEvent('evententer', this, el, event);
19508     },
19509     
19510     onEventLeave: function (e, el,event,d) {
19511         this.fireEvent('eventleave', this, el, event);
19512     },
19513     
19514     onEventClick: function (e, el,event,d) {
19515         this.fireEvent('eventclick', this, el, event);
19516     },
19517     
19518     onMonthChange: function () {
19519         this.store.load();
19520     },
19521     
19522     onMoreEventClick: function(e, el, more)
19523     {
19524         var _this = this;
19525         
19526         this.calpopover.placement = 'right';
19527         this.calpopover.setTitle('More');
19528         
19529         this.calpopover.setContent('');
19530         
19531         var ctr = this.calpopover.el.select('.popover-content', true).first();
19532         
19533         Roo.each(more, function(m){
19534             var cfg = {
19535                 cls : 'fc-event-hori fc-event-draggable',
19536                 html : m.title
19537             };
19538             var cg = ctr.createChild(cfg);
19539             
19540             cg.on('click', _this.onEventClick, _this, m);
19541         });
19542         
19543         this.calpopover.show(el);
19544         
19545         
19546     },
19547     
19548     onLoad: function () 
19549     {   
19550         this.calevents = [];
19551         var cal = this;
19552         
19553         if(this.store.getCount() > 0){
19554             this.store.data.each(function(d){
19555                cal.addItem({
19556                     id : d.data.id,
19557                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19558                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19559                     time : d.data.start_time,
19560                     title : d.data.title,
19561                     description : d.data.description,
19562                     venue : d.data.venue
19563                 });
19564             });
19565         }
19566         
19567         this.renderEvents();
19568         
19569         if(this.calevents.length && this.loadMask){
19570             this.maskEl.hide();
19571         }
19572     },
19573     
19574     onBeforeLoad: function()
19575     {
19576         this.clearEvents();
19577         if(this.loadMask){
19578             this.maskEl.show();
19579         }
19580     }
19581 });
19582
19583  
19584  /*
19585  * - LGPL
19586  *
19587  * element
19588  * 
19589  */
19590
19591 /**
19592  * @class Roo.bootstrap.Popover
19593  * @extends Roo.bootstrap.Component
19594  * Bootstrap Popover class
19595  * @cfg {String} html contents of the popover   (or false to use children..)
19596  * @cfg {String} title of popover (or false to hide)
19597  * @cfg {String} placement how it is placed
19598  * @cfg {String} trigger click || hover (or false to trigger manually)
19599  * @cfg {String} over what (parent or false to trigger manually.)
19600  * @cfg {Number} delay - delay before showing
19601  
19602  * @constructor
19603  * Create a new Popover
19604  * @param {Object} config The config object
19605  */
19606
19607 Roo.bootstrap.Popover = function(config){
19608     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19609     
19610     this.addEvents({
19611         // raw events
19612          /**
19613          * @event show
19614          * After the popover show
19615          * 
19616          * @param {Roo.bootstrap.Popover} this
19617          */
19618         "show" : true,
19619         /**
19620          * @event hide
19621          * After the popover hide
19622          * 
19623          * @param {Roo.bootstrap.Popover} this
19624          */
19625         "hide" : true
19626     });
19627 };
19628
19629 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19630     
19631     title: 'Fill in a title',
19632     html: false,
19633     
19634     placement : 'right',
19635     trigger : 'hover', // hover
19636     
19637     delay : 0,
19638     
19639     over: 'parent',
19640     
19641     can_build_overlaid : false,
19642     
19643     getChildContainer : function()
19644     {
19645         return this.el.select('.popover-content',true).first();
19646     },
19647     
19648     getAutoCreate : function(){
19649          
19650         var cfg = {
19651            cls : 'popover roo-dynamic',
19652            style: 'display:block',
19653            cn : [
19654                 {
19655                     cls : 'arrow'
19656                 },
19657                 {
19658                     cls : 'popover-inner',
19659                     cn : [
19660                         {
19661                             tag: 'h3',
19662                             cls: 'popover-title popover-header',
19663                             html : this.title
19664                         },
19665                         {
19666                             cls : 'popover-content popover-body',
19667                             html : this.html
19668                         }
19669                     ]
19670                     
19671                 }
19672            ]
19673         };
19674         
19675         return cfg;
19676     },
19677     setTitle: function(str)
19678     {
19679         this.title = str;
19680         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19681     },
19682     setContent: function(str)
19683     {
19684         this.html = str;
19685         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19686     },
19687     // as it get's added to the bottom of the page.
19688     onRender : function(ct, position)
19689     {
19690         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19691         if(!this.el){
19692             var cfg = Roo.apply({},  this.getAutoCreate());
19693             cfg.id = Roo.id();
19694             
19695             if (this.cls) {
19696                 cfg.cls += ' ' + this.cls;
19697             }
19698             if (this.style) {
19699                 cfg.style = this.style;
19700             }
19701             //Roo.log("adding to ");
19702             this.el = Roo.get(document.body).createChild(cfg, position);
19703 //            Roo.log(this.el);
19704         }
19705         this.initEvents();
19706     },
19707     
19708     initEvents : function()
19709     {
19710         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19711         this.el.enableDisplayMode('block');
19712         this.el.hide();
19713         if (this.over === false) {
19714             return; 
19715         }
19716         if (this.triggers === false) {
19717             return;
19718         }
19719         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19720         var triggers = this.trigger ? this.trigger.split(' ') : [];
19721         Roo.each(triggers, function(trigger) {
19722         
19723             if (trigger == 'click') {
19724                 on_el.on('click', this.toggle, this);
19725             } else if (trigger != 'manual') {
19726                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19727                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19728       
19729                 on_el.on(eventIn  ,this.enter, this);
19730                 on_el.on(eventOut, this.leave, this);
19731             }
19732         }, this);
19733         
19734     },
19735     
19736     
19737     // private
19738     timeout : null,
19739     hoverState : null,
19740     
19741     toggle : function () {
19742         this.hoverState == 'in' ? this.leave() : this.enter();
19743     },
19744     
19745     enter : function () {
19746         
19747         clearTimeout(this.timeout);
19748     
19749         this.hoverState = 'in';
19750     
19751         if (!this.delay || !this.delay.show) {
19752             this.show();
19753             return;
19754         }
19755         var _t = this;
19756         this.timeout = setTimeout(function () {
19757             if (_t.hoverState == 'in') {
19758                 _t.show();
19759             }
19760         }, this.delay.show)
19761     },
19762     
19763     leave : function() {
19764         clearTimeout(this.timeout);
19765     
19766         this.hoverState = 'out';
19767     
19768         if (!this.delay || !this.delay.hide) {
19769             this.hide();
19770             return;
19771         }
19772         var _t = this;
19773         this.timeout = setTimeout(function () {
19774             if (_t.hoverState == 'out') {
19775                 _t.hide();
19776             }
19777         }, this.delay.hide)
19778     },
19779     
19780     show : function (on_el)
19781     {
19782         if (!on_el) {
19783             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19784         }
19785         
19786         // set content.
19787         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19788         if (this.html !== false) {
19789             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19790         }
19791         this.el.removeClass([
19792             'fade','top','bottom', 'left', 'right','in',
19793             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19794         ]);
19795         if (!this.title.length) {
19796             this.el.select('.popover-title',true).hide();
19797         }
19798         
19799         var placement = typeof this.placement == 'function' ?
19800             this.placement.call(this, this.el, on_el) :
19801             this.placement;
19802             
19803         var autoToken = /\s?auto?\s?/i;
19804         var autoPlace = autoToken.test(placement);
19805         if (autoPlace) {
19806             placement = placement.replace(autoToken, '') || 'top';
19807         }
19808         
19809         //this.el.detach()
19810         //this.el.setXY([0,0]);
19811         this.el.show();
19812         this.el.dom.style.display='block';
19813         this.el.addClass(placement);
19814         
19815         //this.el.appendTo(on_el);
19816         
19817         var p = this.getPosition();
19818         var box = this.el.getBox();
19819         
19820         if (autoPlace) {
19821             // fixme..
19822         }
19823         var align = Roo.bootstrap.Popover.alignment[placement];
19824         
19825 //        Roo.log(align);
19826         this.el.alignTo(on_el, align[0],align[1]);
19827         //var arrow = this.el.select('.arrow',true).first();
19828         //arrow.set(align[2], 
19829         
19830         this.el.addClass('in');
19831         
19832         
19833         if (this.el.hasClass('fade')) {
19834             // fade it?
19835         }
19836         
19837         this.hoverState = 'in';
19838         
19839         this.fireEvent('show', this);
19840         
19841     },
19842     hide : function()
19843     {
19844         this.el.setXY([0,0]);
19845         this.el.removeClass('in');
19846         this.el.hide();
19847         this.hoverState = null;
19848         
19849         this.fireEvent('hide', this);
19850     }
19851     
19852 });
19853
19854 Roo.bootstrap.Popover.alignment = {
19855     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19856     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19857     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19858     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19859 };
19860
19861  /*
19862  * - LGPL
19863  *
19864  * Progress
19865  * 
19866  */
19867
19868 /**
19869  * @class Roo.bootstrap.Progress
19870  * @extends Roo.bootstrap.Component
19871  * Bootstrap Progress class
19872  * @cfg {Boolean} striped striped of the progress bar
19873  * @cfg {Boolean} active animated of the progress bar
19874  * 
19875  * 
19876  * @constructor
19877  * Create a new Progress
19878  * @param {Object} config The config object
19879  */
19880
19881 Roo.bootstrap.Progress = function(config){
19882     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19883 };
19884
19885 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19886     
19887     striped : false,
19888     active: false,
19889     
19890     getAutoCreate : function(){
19891         var cfg = {
19892             tag: 'div',
19893             cls: 'progress'
19894         };
19895         
19896         
19897         if(this.striped){
19898             cfg.cls += ' progress-striped';
19899         }
19900       
19901         if(this.active){
19902             cfg.cls += ' active';
19903         }
19904         
19905         
19906         return cfg;
19907     }
19908    
19909 });
19910
19911  
19912
19913  /*
19914  * - LGPL
19915  *
19916  * ProgressBar
19917  * 
19918  */
19919
19920 /**
19921  * @class Roo.bootstrap.ProgressBar
19922  * @extends Roo.bootstrap.Component
19923  * Bootstrap ProgressBar class
19924  * @cfg {Number} aria_valuenow aria-value now
19925  * @cfg {Number} aria_valuemin aria-value min
19926  * @cfg {Number} aria_valuemax aria-value max
19927  * @cfg {String} label label for the progress bar
19928  * @cfg {String} panel (success | info | warning | danger )
19929  * @cfg {String} role role of the progress bar
19930  * @cfg {String} sr_only text
19931  * 
19932  * 
19933  * @constructor
19934  * Create a new ProgressBar
19935  * @param {Object} config The config object
19936  */
19937
19938 Roo.bootstrap.ProgressBar = function(config){
19939     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19940 };
19941
19942 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19943     
19944     aria_valuenow : 0,
19945     aria_valuemin : 0,
19946     aria_valuemax : 100,
19947     label : false,
19948     panel : false,
19949     role : false,
19950     sr_only: false,
19951     
19952     getAutoCreate : function()
19953     {
19954         
19955         var cfg = {
19956             tag: 'div',
19957             cls: 'progress-bar',
19958             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19959         };
19960         
19961         if(this.sr_only){
19962             cfg.cn = {
19963                 tag: 'span',
19964                 cls: 'sr-only',
19965                 html: this.sr_only
19966             }
19967         }
19968         
19969         if(this.role){
19970             cfg.role = this.role;
19971         }
19972         
19973         if(this.aria_valuenow){
19974             cfg['aria-valuenow'] = this.aria_valuenow;
19975         }
19976         
19977         if(this.aria_valuemin){
19978             cfg['aria-valuemin'] = this.aria_valuemin;
19979         }
19980         
19981         if(this.aria_valuemax){
19982             cfg['aria-valuemax'] = this.aria_valuemax;
19983         }
19984         
19985         if(this.label && !this.sr_only){
19986             cfg.html = this.label;
19987         }
19988         
19989         if(this.panel){
19990             cfg.cls += ' progress-bar-' + this.panel;
19991         }
19992         
19993         return cfg;
19994     },
19995     
19996     update : function(aria_valuenow)
19997     {
19998         this.aria_valuenow = aria_valuenow;
19999         
20000         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20001     }
20002    
20003 });
20004
20005  
20006
20007  /*
20008  * - LGPL
20009  *
20010  * column
20011  * 
20012  */
20013
20014 /**
20015  * @class Roo.bootstrap.TabGroup
20016  * @extends Roo.bootstrap.Column
20017  * Bootstrap Column class
20018  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20019  * @cfg {Boolean} carousel true to make the group behave like a carousel
20020  * @cfg {Boolean} bullets show bullets for the panels
20021  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20022  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20023  * @cfg {Boolean} showarrow (true|false) show arrow default true
20024  * 
20025  * @constructor
20026  * Create a new TabGroup
20027  * @param {Object} config The config object
20028  */
20029
20030 Roo.bootstrap.TabGroup = function(config){
20031     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20032     if (!this.navId) {
20033         this.navId = Roo.id();
20034     }
20035     this.tabs = [];
20036     Roo.bootstrap.TabGroup.register(this);
20037     
20038 };
20039
20040 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20041     
20042     carousel : false,
20043     transition : false,
20044     bullets : 0,
20045     timer : 0,
20046     autoslide : false,
20047     slideFn : false,
20048     slideOnTouch : false,
20049     showarrow : true,
20050     
20051     getAutoCreate : function()
20052     {
20053         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20054         
20055         cfg.cls += ' tab-content';
20056         
20057         if (this.carousel) {
20058             cfg.cls += ' carousel slide';
20059             
20060             cfg.cn = [{
20061                cls : 'carousel-inner',
20062                cn : []
20063             }];
20064         
20065             if(this.bullets  && !Roo.isTouch){
20066                 
20067                 var bullets = {
20068                     cls : 'carousel-bullets',
20069                     cn : []
20070                 };
20071                
20072                 if(this.bullets_cls){
20073                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20074                 }
20075                 
20076                 bullets.cn.push({
20077                     cls : 'clear'
20078                 });
20079                 
20080                 cfg.cn[0].cn.push(bullets);
20081             }
20082             
20083             if(this.showarrow){
20084                 cfg.cn[0].cn.push({
20085                     tag : 'div',
20086                     class : 'carousel-arrow',
20087                     cn : [
20088                         {
20089                             tag : 'div',
20090                             class : 'carousel-prev',
20091                             cn : [
20092                                 {
20093                                     tag : 'i',
20094                                     class : 'fa fa-chevron-left'
20095                                 }
20096                             ]
20097                         },
20098                         {
20099                             tag : 'div',
20100                             class : 'carousel-next',
20101                             cn : [
20102                                 {
20103                                     tag : 'i',
20104                                     class : 'fa fa-chevron-right'
20105                                 }
20106                             ]
20107                         }
20108                     ]
20109                 });
20110             }
20111             
20112         }
20113         
20114         return cfg;
20115     },
20116     
20117     initEvents:  function()
20118     {
20119 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20120 //            this.el.on("touchstart", this.onTouchStart, this);
20121 //        }
20122         
20123         if(this.autoslide){
20124             var _this = this;
20125             
20126             this.slideFn = window.setInterval(function() {
20127                 _this.showPanelNext();
20128             }, this.timer);
20129         }
20130         
20131         if(this.showarrow){
20132             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20133             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20134         }
20135         
20136         
20137     },
20138     
20139 //    onTouchStart : function(e, el, o)
20140 //    {
20141 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20142 //            return;
20143 //        }
20144 //        
20145 //        this.showPanelNext();
20146 //    },
20147     
20148     
20149     getChildContainer : function()
20150     {
20151         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20152     },
20153     
20154     /**
20155     * register a Navigation item
20156     * @param {Roo.bootstrap.NavItem} the navitem to add
20157     */
20158     register : function(item)
20159     {
20160         this.tabs.push( item);
20161         item.navId = this.navId; // not really needed..
20162         this.addBullet();
20163     
20164     },
20165     
20166     getActivePanel : function()
20167     {
20168         var r = false;
20169         Roo.each(this.tabs, function(t) {
20170             if (t.active) {
20171                 r = t;
20172                 return false;
20173             }
20174             return null;
20175         });
20176         return r;
20177         
20178     },
20179     getPanelByName : function(n)
20180     {
20181         var r = false;
20182         Roo.each(this.tabs, function(t) {
20183             if (t.tabId == n) {
20184                 r = t;
20185                 return false;
20186             }
20187             return null;
20188         });
20189         return r;
20190     },
20191     indexOfPanel : function(p)
20192     {
20193         var r = false;
20194         Roo.each(this.tabs, function(t,i) {
20195             if (t.tabId == p.tabId) {
20196                 r = i;
20197                 return false;
20198             }
20199             return null;
20200         });
20201         return r;
20202     },
20203     /**
20204      * show a specific panel
20205      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20206      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20207      */
20208     showPanel : function (pan)
20209     {
20210         if(this.transition || typeof(pan) == 'undefined'){
20211             Roo.log("waiting for the transitionend");
20212             return false;
20213         }
20214         
20215         if (typeof(pan) == 'number') {
20216             pan = this.tabs[pan];
20217         }
20218         
20219         if (typeof(pan) == 'string') {
20220             pan = this.getPanelByName(pan);
20221         }
20222         
20223         var cur = this.getActivePanel();
20224         
20225         if(!pan || !cur){
20226             Roo.log('pan or acitve pan is undefined');
20227             return false;
20228         }
20229         
20230         if (pan.tabId == this.getActivePanel().tabId) {
20231             return true;
20232         }
20233         
20234         if (false === cur.fireEvent('beforedeactivate')) {
20235             return false;
20236         }
20237         
20238         if(this.bullets > 0 && !Roo.isTouch){
20239             this.setActiveBullet(this.indexOfPanel(pan));
20240         }
20241         
20242         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20243             
20244             //class="carousel-item carousel-item-next carousel-item-left"
20245             
20246             this.transition = true;
20247             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20248             var lr = dir == 'next' ? 'left' : 'right';
20249             pan.el.addClass(dir); // or prev
20250             pan.el.addClass('carousel-item-' + dir); // or prev
20251             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20252             cur.el.addClass(lr); // or right
20253             pan.el.addClass(lr);
20254             cur.el.addClass('carousel-item-' +lr); // or right
20255             pan.el.addClass('carousel-item-' +lr);
20256             
20257             
20258             var _this = this;
20259             cur.el.on('transitionend', function() {
20260                 Roo.log("trans end?");
20261                 
20262                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20263                 pan.setActive(true);
20264                 
20265                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20266                 cur.setActive(false);
20267                 
20268                 _this.transition = false;
20269                 
20270             }, this, { single:  true } );
20271             
20272             return true;
20273         }
20274         
20275         cur.setActive(false);
20276         pan.setActive(true);
20277         
20278         return true;
20279         
20280     },
20281     showPanelNext : function()
20282     {
20283         var i = this.indexOfPanel(this.getActivePanel());
20284         
20285         if (i >= this.tabs.length - 1 && !this.autoslide) {
20286             return;
20287         }
20288         
20289         if (i >= this.tabs.length - 1 && this.autoslide) {
20290             i = -1;
20291         }
20292         
20293         this.showPanel(this.tabs[i+1]);
20294     },
20295     
20296     showPanelPrev : function()
20297     {
20298         var i = this.indexOfPanel(this.getActivePanel());
20299         
20300         if (i  < 1 && !this.autoslide) {
20301             return;
20302         }
20303         
20304         if (i < 1 && this.autoslide) {
20305             i = this.tabs.length;
20306         }
20307         
20308         this.showPanel(this.tabs[i-1]);
20309     },
20310     
20311     
20312     addBullet: function()
20313     {
20314         if(!this.bullets || Roo.isTouch){
20315             return;
20316         }
20317         var ctr = this.el.select('.carousel-bullets',true).first();
20318         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20319         var bullet = ctr.createChild({
20320             cls : 'bullet bullet-' + i
20321         },ctr.dom.lastChild);
20322         
20323         
20324         var _this = this;
20325         
20326         bullet.on('click', (function(e, el, o, ii, t){
20327
20328             e.preventDefault();
20329
20330             this.showPanel(ii);
20331
20332             if(this.autoslide && this.slideFn){
20333                 clearInterval(this.slideFn);
20334                 this.slideFn = window.setInterval(function() {
20335                     _this.showPanelNext();
20336                 }, this.timer);
20337             }
20338
20339         }).createDelegate(this, [i, bullet], true));
20340                 
20341         
20342     },
20343      
20344     setActiveBullet : function(i)
20345     {
20346         if(Roo.isTouch){
20347             return;
20348         }
20349         
20350         Roo.each(this.el.select('.bullet', true).elements, function(el){
20351             el.removeClass('selected');
20352         });
20353
20354         var bullet = this.el.select('.bullet-' + i, true).first();
20355         
20356         if(!bullet){
20357             return;
20358         }
20359         
20360         bullet.addClass('selected');
20361     }
20362     
20363     
20364   
20365 });
20366
20367  
20368
20369  
20370  
20371 Roo.apply(Roo.bootstrap.TabGroup, {
20372     
20373     groups: {},
20374      /**
20375     * register a Navigation Group
20376     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20377     */
20378     register : function(navgrp)
20379     {
20380         this.groups[navgrp.navId] = navgrp;
20381         
20382     },
20383     /**
20384     * fetch a Navigation Group based on the navigation ID
20385     * if one does not exist , it will get created.
20386     * @param {string} the navgroup to add
20387     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20388     */
20389     get: function(navId) {
20390         if (typeof(this.groups[navId]) == 'undefined') {
20391             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20392         }
20393         return this.groups[navId] ;
20394     }
20395     
20396     
20397     
20398 });
20399
20400  /*
20401  * - LGPL
20402  *
20403  * TabPanel
20404  * 
20405  */
20406
20407 /**
20408  * @class Roo.bootstrap.TabPanel
20409  * @extends Roo.bootstrap.Component
20410  * Bootstrap TabPanel class
20411  * @cfg {Boolean} active panel active
20412  * @cfg {String} html panel content
20413  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20414  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20415  * @cfg {String} href click to link..
20416  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20417  * 
20418  * 
20419  * @constructor
20420  * Create a new TabPanel
20421  * @param {Object} config The config object
20422  */
20423
20424 Roo.bootstrap.TabPanel = function(config){
20425     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20426     this.addEvents({
20427         /**
20428              * @event changed
20429              * Fires when the active status changes
20430              * @param {Roo.bootstrap.TabPanel} this
20431              * @param {Boolean} state the new state
20432             
20433          */
20434         'changed': true,
20435         /**
20436              * @event beforedeactivate
20437              * Fires before a tab is de-activated - can be used to do validation on a form.
20438              * @param {Roo.bootstrap.TabPanel} this
20439              * @return {Boolean} false if there is an error
20440             
20441          */
20442         'beforedeactivate': true
20443      });
20444     
20445     this.tabId = this.tabId || Roo.id();
20446   
20447 };
20448
20449 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20450     
20451     active: false,
20452     html: false,
20453     tabId: false,
20454     navId : false,
20455     href : '',
20456     touchSlide : false,
20457     getAutoCreate : function(){
20458         
20459         
20460         var cfg = {
20461             tag: 'div',
20462             // item is needed for carousel - not sure if it has any effect otherwise
20463             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20464             html: this.html || ''
20465         };
20466         
20467         if(this.active){
20468             cfg.cls += ' active';
20469         }
20470         
20471         if(this.tabId){
20472             cfg.tabId = this.tabId;
20473         }
20474         
20475         
20476         
20477         return cfg;
20478     },
20479     
20480     initEvents:  function()
20481     {
20482         var p = this.parent();
20483         
20484         this.navId = this.navId || p.navId;
20485         
20486         if (typeof(this.navId) != 'undefined') {
20487             // not really needed.. but just in case.. parent should be a NavGroup.
20488             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20489             
20490             tg.register(this);
20491             
20492             var i = tg.tabs.length - 1;
20493             
20494             if(this.active && tg.bullets > 0 && i < tg.bullets){
20495                 tg.setActiveBullet(i);
20496             }
20497         }
20498         
20499         this.el.on('click', this.onClick, this);
20500         
20501         if(Roo.isTouch && this.touchSlide){
20502             this.el.on("touchstart", this.onTouchStart, this);
20503             this.el.on("touchmove", this.onTouchMove, this);
20504             this.el.on("touchend", this.onTouchEnd, this);
20505         }
20506         
20507     },
20508     
20509     onRender : function(ct, position)
20510     {
20511         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20512     },
20513     
20514     setActive : function(state)
20515     {
20516         Roo.log("panel - set active " + this.tabId + "=" + state);
20517         
20518         this.active = state;
20519         if (!state) {
20520             this.el.removeClass('active');
20521             
20522         } else  if (!this.el.hasClass('active')) {
20523             this.el.addClass('active');
20524         }
20525         
20526         this.fireEvent('changed', this, state);
20527     },
20528     
20529     onClick : function(e)
20530     {
20531         e.preventDefault();
20532         
20533         if(!this.href.length){
20534             return;
20535         }
20536         
20537         window.location.href = this.href;
20538     },
20539     
20540     startX : 0,
20541     startY : 0,
20542     endX : 0,
20543     endY : 0,
20544     swiping : false,
20545     
20546     onTouchStart : function(e)
20547     {
20548         this.swiping = false;
20549         
20550         this.startX = e.browserEvent.touches[0].clientX;
20551         this.startY = e.browserEvent.touches[0].clientY;
20552     },
20553     
20554     onTouchMove : function(e)
20555     {
20556         this.swiping = true;
20557         
20558         this.endX = e.browserEvent.touches[0].clientX;
20559         this.endY = e.browserEvent.touches[0].clientY;
20560     },
20561     
20562     onTouchEnd : function(e)
20563     {
20564         if(!this.swiping){
20565             this.onClick(e);
20566             return;
20567         }
20568         
20569         var tabGroup = this.parent();
20570         
20571         if(this.endX > this.startX){ // swiping right
20572             tabGroup.showPanelPrev();
20573             return;
20574         }
20575         
20576         if(this.startX > this.endX){ // swiping left
20577             tabGroup.showPanelNext();
20578             return;
20579         }
20580     }
20581     
20582     
20583 });
20584  
20585
20586  
20587
20588  /*
20589  * - LGPL
20590  *
20591  * DateField
20592  * 
20593  */
20594
20595 /**
20596  * @class Roo.bootstrap.DateField
20597  * @extends Roo.bootstrap.Input
20598  * Bootstrap DateField class
20599  * @cfg {Number} weekStart default 0
20600  * @cfg {String} viewMode default empty, (months|years)
20601  * @cfg {String} minViewMode default empty, (months|years)
20602  * @cfg {Number} startDate default -Infinity
20603  * @cfg {Number} endDate default Infinity
20604  * @cfg {Boolean} todayHighlight default false
20605  * @cfg {Boolean} todayBtn default false
20606  * @cfg {Boolean} calendarWeeks default false
20607  * @cfg {Object} daysOfWeekDisabled default empty
20608  * @cfg {Boolean} singleMode default false (true | false)
20609  * 
20610  * @cfg {Boolean} keyboardNavigation default true
20611  * @cfg {String} language default en
20612  * 
20613  * @constructor
20614  * Create a new DateField
20615  * @param {Object} config The config object
20616  */
20617
20618 Roo.bootstrap.DateField = function(config){
20619     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20620      this.addEvents({
20621             /**
20622              * @event show
20623              * Fires when this field show.
20624              * @param {Roo.bootstrap.DateField} this
20625              * @param {Mixed} date The date value
20626              */
20627             show : true,
20628             /**
20629              * @event show
20630              * Fires when this field hide.
20631              * @param {Roo.bootstrap.DateField} this
20632              * @param {Mixed} date The date value
20633              */
20634             hide : true,
20635             /**
20636              * @event select
20637              * Fires when select a date.
20638              * @param {Roo.bootstrap.DateField} this
20639              * @param {Mixed} date The date value
20640              */
20641             select : true,
20642             /**
20643              * @event beforeselect
20644              * Fires when before select a date.
20645              * @param {Roo.bootstrap.DateField} this
20646              * @param {Mixed} date The date value
20647              */
20648             beforeselect : true
20649         });
20650 };
20651
20652 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20653     
20654     /**
20655      * @cfg {String} format
20656      * The default date format string which can be overriden for localization support.  The format must be
20657      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20658      */
20659     format : "m/d/y",
20660     /**
20661      * @cfg {String} altFormats
20662      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20663      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20664      */
20665     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20666     
20667     weekStart : 0,
20668     
20669     viewMode : '',
20670     
20671     minViewMode : '',
20672     
20673     todayHighlight : false,
20674     
20675     todayBtn: false,
20676     
20677     language: 'en',
20678     
20679     keyboardNavigation: true,
20680     
20681     calendarWeeks: false,
20682     
20683     startDate: -Infinity,
20684     
20685     endDate: Infinity,
20686     
20687     daysOfWeekDisabled: [],
20688     
20689     _events: [],
20690     
20691     singleMode : false,
20692     
20693     UTCDate: function()
20694     {
20695         return new Date(Date.UTC.apply(Date, arguments));
20696     },
20697     
20698     UTCToday: function()
20699     {
20700         var today = new Date();
20701         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20702     },
20703     
20704     getDate: function() {
20705             var d = this.getUTCDate();
20706             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20707     },
20708     
20709     getUTCDate: function() {
20710             return this.date;
20711     },
20712     
20713     setDate: function(d) {
20714             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20715     },
20716     
20717     setUTCDate: function(d) {
20718             this.date = d;
20719             this.setValue(this.formatDate(this.date));
20720     },
20721         
20722     onRender: function(ct, position)
20723     {
20724         
20725         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20726         
20727         this.language = this.language || 'en';
20728         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20729         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20730         
20731         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20732         this.format = this.format || 'm/d/y';
20733         this.isInline = false;
20734         this.isInput = true;
20735         this.component = this.el.select('.add-on', true).first() || false;
20736         this.component = (this.component && this.component.length === 0) ? false : this.component;
20737         this.hasInput = this.component && this.inputEl().length;
20738         
20739         if (typeof(this.minViewMode === 'string')) {
20740             switch (this.minViewMode) {
20741                 case 'months':
20742                     this.minViewMode = 1;
20743                     break;
20744                 case 'years':
20745                     this.minViewMode = 2;
20746                     break;
20747                 default:
20748                     this.minViewMode = 0;
20749                     break;
20750             }
20751         }
20752         
20753         if (typeof(this.viewMode === 'string')) {
20754             switch (this.viewMode) {
20755                 case 'months':
20756                     this.viewMode = 1;
20757                     break;
20758                 case 'years':
20759                     this.viewMode = 2;
20760                     break;
20761                 default:
20762                     this.viewMode = 0;
20763                     break;
20764             }
20765         }
20766                 
20767         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20768         
20769 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20770         
20771         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20772         
20773         this.picker().on('mousedown', this.onMousedown, this);
20774         this.picker().on('click', this.onClick, this);
20775         
20776         this.picker().addClass('datepicker-dropdown');
20777         
20778         this.startViewMode = this.viewMode;
20779         
20780         if(this.singleMode){
20781             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20782                 v.setVisibilityMode(Roo.Element.DISPLAY);
20783                 v.hide();
20784             });
20785             
20786             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20787                 v.setStyle('width', '189px');
20788             });
20789         }
20790         
20791         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20792             if(!this.calendarWeeks){
20793                 v.remove();
20794                 return;
20795             }
20796             
20797             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20798             v.attr('colspan', function(i, val){
20799                 return parseInt(val) + 1;
20800             });
20801         });
20802                         
20803         
20804         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20805         
20806         this.setStartDate(this.startDate);
20807         this.setEndDate(this.endDate);
20808         
20809         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20810         
20811         this.fillDow();
20812         this.fillMonths();
20813         this.update();
20814         this.showMode();
20815         
20816         if(this.isInline) {
20817             this.showPopup();
20818         }
20819     },
20820     
20821     picker : function()
20822     {
20823         return this.pickerEl;
20824 //        return this.el.select('.datepicker', true).first();
20825     },
20826     
20827     fillDow: function()
20828     {
20829         var dowCnt = this.weekStart;
20830         
20831         var dow = {
20832             tag: 'tr',
20833             cn: [
20834                 
20835             ]
20836         };
20837         
20838         if(this.calendarWeeks){
20839             dow.cn.push({
20840                 tag: 'th',
20841                 cls: 'cw',
20842                 html: '&nbsp;'
20843             })
20844         }
20845         
20846         while (dowCnt < this.weekStart + 7) {
20847             dow.cn.push({
20848                 tag: 'th',
20849                 cls: 'dow',
20850                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20851             });
20852         }
20853         
20854         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20855     },
20856     
20857     fillMonths: function()
20858     {    
20859         var i = 0;
20860         var months = this.picker().select('>.datepicker-months td', true).first();
20861         
20862         months.dom.innerHTML = '';
20863         
20864         while (i < 12) {
20865             var month = {
20866                 tag: 'span',
20867                 cls: 'month',
20868                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20869             };
20870             
20871             months.createChild(month);
20872         }
20873         
20874     },
20875     
20876     update: function()
20877     {
20878         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;
20879         
20880         if (this.date < this.startDate) {
20881             this.viewDate = new Date(this.startDate);
20882         } else if (this.date > this.endDate) {
20883             this.viewDate = new Date(this.endDate);
20884         } else {
20885             this.viewDate = new Date(this.date);
20886         }
20887         
20888         this.fill();
20889     },
20890     
20891     fill: function() 
20892     {
20893         var d = new Date(this.viewDate),
20894                 year = d.getUTCFullYear(),
20895                 month = d.getUTCMonth(),
20896                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20897                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20898                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20899                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20900                 currentDate = this.date && this.date.valueOf(),
20901                 today = this.UTCToday();
20902         
20903         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20904         
20905 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20906         
20907 //        this.picker.select('>tfoot th.today').
20908 //                                              .text(dates[this.language].today)
20909 //                                              .toggle(this.todayBtn !== false);
20910     
20911         this.updateNavArrows();
20912         this.fillMonths();
20913                                                 
20914         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20915         
20916         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20917          
20918         prevMonth.setUTCDate(day);
20919         
20920         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20921         
20922         var nextMonth = new Date(prevMonth);
20923         
20924         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20925         
20926         nextMonth = nextMonth.valueOf();
20927         
20928         var fillMonths = false;
20929         
20930         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20931         
20932         while(prevMonth.valueOf() <= nextMonth) {
20933             var clsName = '';
20934             
20935             if (prevMonth.getUTCDay() === this.weekStart) {
20936                 if(fillMonths){
20937                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20938                 }
20939                     
20940                 fillMonths = {
20941                     tag: 'tr',
20942                     cn: []
20943                 };
20944                 
20945                 if(this.calendarWeeks){
20946                     // ISO 8601: First week contains first thursday.
20947                     // ISO also states week starts on Monday, but we can be more abstract here.
20948                     var
20949                     // Start of current week: based on weekstart/current date
20950                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20951                     // Thursday of this week
20952                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20953                     // First Thursday of year, year from thursday
20954                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20955                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20956                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20957                     
20958                     fillMonths.cn.push({
20959                         tag: 'td',
20960                         cls: 'cw',
20961                         html: calWeek
20962                     });
20963                 }
20964             }
20965             
20966             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20967                 clsName += ' old';
20968             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20969                 clsName += ' new';
20970             }
20971             if (this.todayHighlight &&
20972                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20973                 prevMonth.getUTCMonth() == today.getMonth() &&
20974                 prevMonth.getUTCDate() == today.getDate()) {
20975                 clsName += ' today';
20976             }
20977             
20978             if (currentDate && prevMonth.valueOf() === currentDate) {
20979                 clsName += ' active';
20980             }
20981             
20982             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20983                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20984                     clsName += ' disabled';
20985             }
20986             
20987             fillMonths.cn.push({
20988                 tag: 'td',
20989                 cls: 'day ' + clsName,
20990                 html: prevMonth.getDate()
20991             });
20992             
20993             prevMonth.setDate(prevMonth.getDate()+1);
20994         }
20995           
20996         var currentYear = this.date && this.date.getUTCFullYear();
20997         var currentMonth = this.date && this.date.getUTCMonth();
20998         
20999         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21000         
21001         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21002             v.removeClass('active');
21003             
21004             if(currentYear === year && k === currentMonth){
21005                 v.addClass('active');
21006             }
21007             
21008             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21009                 v.addClass('disabled');
21010             }
21011             
21012         });
21013         
21014         
21015         year = parseInt(year/10, 10) * 10;
21016         
21017         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21018         
21019         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21020         
21021         year -= 1;
21022         for (var i = -1; i < 11; i++) {
21023             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21024                 tag: 'span',
21025                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21026                 html: year
21027             });
21028             
21029             year += 1;
21030         }
21031     },
21032     
21033     showMode: function(dir) 
21034     {
21035         if (dir) {
21036             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21037         }
21038         
21039         Roo.each(this.picker().select('>div',true).elements, function(v){
21040             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21041             v.hide();
21042         });
21043         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21044     },
21045     
21046     place: function()
21047     {
21048         if(this.isInline) {
21049             return;
21050         }
21051         
21052         this.picker().removeClass(['bottom', 'top']);
21053         
21054         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21055             /*
21056              * place to the top of element!
21057              *
21058              */
21059             
21060             this.picker().addClass('top');
21061             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21062             
21063             return;
21064         }
21065         
21066         this.picker().addClass('bottom');
21067         
21068         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21069     },
21070     
21071     parseDate : function(value)
21072     {
21073         if(!value || value instanceof Date){
21074             return value;
21075         }
21076         var v = Date.parseDate(value, this.format);
21077         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21078             v = Date.parseDate(value, 'Y-m-d');
21079         }
21080         if(!v && this.altFormats){
21081             if(!this.altFormatsArray){
21082                 this.altFormatsArray = this.altFormats.split("|");
21083             }
21084             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21085                 v = Date.parseDate(value, this.altFormatsArray[i]);
21086             }
21087         }
21088         return v;
21089     },
21090     
21091     formatDate : function(date, fmt)
21092     {   
21093         return (!date || !(date instanceof Date)) ?
21094         date : date.dateFormat(fmt || this.format);
21095     },
21096     
21097     onFocus : function()
21098     {
21099         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21100         this.showPopup();
21101     },
21102     
21103     onBlur : function()
21104     {
21105         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21106         
21107         var d = this.inputEl().getValue();
21108         
21109         this.setValue(d);
21110                 
21111         this.hidePopup();
21112     },
21113     
21114     showPopup : function()
21115     {
21116         this.picker().show();
21117         this.update();
21118         this.place();
21119         
21120         this.fireEvent('showpopup', this, this.date);
21121     },
21122     
21123     hidePopup : function()
21124     {
21125         if(this.isInline) {
21126             return;
21127         }
21128         this.picker().hide();
21129         this.viewMode = this.startViewMode;
21130         this.showMode();
21131         
21132         this.fireEvent('hidepopup', this, this.date);
21133         
21134     },
21135     
21136     onMousedown: function(e)
21137     {
21138         e.stopPropagation();
21139         e.preventDefault();
21140     },
21141     
21142     keyup: function(e)
21143     {
21144         Roo.bootstrap.DateField.superclass.keyup.call(this);
21145         this.update();
21146     },
21147
21148     setValue: function(v)
21149     {
21150         if(this.fireEvent('beforeselect', this, v) !== false){
21151             var d = new Date(this.parseDate(v) ).clearTime();
21152         
21153             if(isNaN(d.getTime())){
21154                 this.date = this.viewDate = '';
21155                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21156                 return;
21157             }
21158
21159             v = this.formatDate(d);
21160
21161             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21162
21163             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21164
21165             this.update();
21166
21167             this.fireEvent('select', this, this.date);
21168         }
21169     },
21170     
21171     getValue: function()
21172     {
21173         return this.formatDate(this.date);
21174     },
21175     
21176     fireKey: function(e)
21177     {
21178         if (!this.picker().isVisible()){
21179             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21180                 this.showPopup();
21181             }
21182             return;
21183         }
21184         
21185         var dateChanged = false,
21186         dir, day, month,
21187         newDate, newViewDate;
21188         
21189         switch(e.keyCode){
21190             case 27: // escape
21191                 this.hidePopup();
21192                 e.preventDefault();
21193                 break;
21194             case 37: // left
21195             case 39: // right
21196                 if (!this.keyboardNavigation) {
21197                     break;
21198                 }
21199                 dir = e.keyCode == 37 ? -1 : 1;
21200                 
21201                 if (e.ctrlKey){
21202                     newDate = this.moveYear(this.date, dir);
21203                     newViewDate = this.moveYear(this.viewDate, dir);
21204                 } else if (e.shiftKey){
21205                     newDate = this.moveMonth(this.date, dir);
21206                     newViewDate = this.moveMonth(this.viewDate, dir);
21207                 } else {
21208                     newDate = new Date(this.date);
21209                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21210                     newViewDate = new Date(this.viewDate);
21211                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21212                 }
21213                 if (this.dateWithinRange(newDate)){
21214                     this.date = newDate;
21215                     this.viewDate = newViewDate;
21216                     this.setValue(this.formatDate(this.date));
21217 //                    this.update();
21218                     e.preventDefault();
21219                     dateChanged = true;
21220                 }
21221                 break;
21222             case 38: // up
21223             case 40: // down
21224                 if (!this.keyboardNavigation) {
21225                     break;
21226                 }
21227                 dir = e.keyCode == 38 ? -1 : 1;
21228                 if (e.ctrlKey){
21229                     newDate = this.moveYear(this.date, dir);
21230                     newViewDate = this.moveYear(this.viewDate, dir);
21231                 } else if (e.shiftKey){
21232                     newDate = this.moveMonth(this.date, dir);
21233                     newViewDate = this.moveMonth(this.viewDate, dir);
21234                 } else {
21235                     newDate = new Date(this.date);
21236                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21237                     newViewDate = new Date(this.viewDate);
21238                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21239                 }
21240                 if (this.dateWithinRange(newDate)){
21241                     this.date = newDate;
21242                     this.viewDate = newViewDate;
21243                     this.setValue(this.formatDate(this.date));
21244 //                    this.update();
21245                     e.preventDefault();
21246                     dateChanged = true;
21247                 }
21248                 break;
21249             case 13: // enter
21250                 this.setValue(this.formatDate(this.date));
21251                 this.hidePopup();
21252                 e.preventDefault();
21253                 break;
21254             case 9: // tab
21255                 this.setValue(this.formatDate(this.date));
21256                 this.hidePopup();
21257                 break;
21258             case 16: // shift
21259             case 17: // ctrl
21260             case 18: // alt
21261                 break;
21262             default :
21263                 this.hidePopup();
21264                 
21265         }
21266     },
21267     
21268     
21269     onClick: function(e) 
21270     {
21271         e.stopPropagation();
21272         e.preventDefault();
21273         
21274         var target = e.getTarget();
21275         
21276         if(target.nodeName.toLowerCase() === 'i'){
21277             target = Roo.get(target).dom.parentNode;
21278         }
21279         
21280         var nodeName = target.nodeName;
21281         var className = target.className;
21282         var html = target.innerHTML;
21283         //Roo.log(nodeName);
21284         
21285         switch(nodeName.toLowerCase()) {
21286             case 'th':
21287                 switch(className) {
21288                     case 'switch':
21289                         this.showMode(1);
21290                         break;
21291                     case 'prev':
21292                     case 'next':
21293                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21294                         switch(this.viewMode){
21295                                 case 0:
21296                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21297                                         break;
21298                                 case 1:
21299                                 case 2:
21300                                         this.viewDate = this.moveYear(this.viewDate, dir);
21301                                         break;
21302                         }
21303                         this.fill();
21304                         break;
21305                     case 'today':
21306                         var date = new Date();
21307                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21308 //                        this.fill()
21309                         this.setValue(this.formatDate(this.date));
21310                         
21311                         this.hidePopup();
21312                         break;
21313                 }
21314                 break;
21315             case 'span':
21316                 if (className.indexOf('disabled') < 0) {
21317                     this.viewDate.setUTCDate(1);
21318                     if (className.indexOf('month') > -1) {
21319                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21320                     } else {
21321                         var year = parseInt(html, 10) || 0;
21322                         this.viewDate.setUTCFullYear(year);
21323                         
21324                     }
21325                     
21326                     if(this.singleMode){
21327                         this.setValue(this.formatDate(this.viewDate));
21328                         this.hidePopup();
21329                         return;
21330                     }
21331                     
21332                     this.showMode(-1);
21333                     this.fill();
21334                 }
21335                 break;
21336                 
21337             case 'td':
21338                 //Roo.log(className);
21339                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21340                     var day = parseInt(html, 10) || 1;
21341                     var year = this.viewDate.getUTCFullYear(),
21342                         month = this.viewDate.getUTCMonth();
21343
21344                     if (className.indexOf('old') > -1) {
21345                         if(month === 0 ){
21346                             month = 11;
21347                             year -= 1;
21348                         }else{
21349                             month -= 1;
21350                         }
21351                     } else if (className.indexOf('new') > -1) {
21352                         if (month == 11) {
21353                             month = 0;
21354                             year += 1;
21355                         } else {
21356                             month += 1;
21357                         }
21358                     }
21359                     //Roo.log([year,month,day]);
21360                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21361                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21362 //                    this.fill();
21363                     //Roo.log(this.formatDate(this.date));
21364                     this.setValue(this.formatDate(this.date));
21365                     this.hidePopup();
21366                 }
21367                 break;
21368         }
21369     },
21370     
21371     setStartDate: function(startDate)
21372     {
21373         this.startDate = startDate || -Infinity;
21374         if (this.startDate !== -Infinity) {
21375             this.startDate = this.parseDate(this.startDate);
21376         }
21377         this.update();
21378         this.updateNavArrows();
21379     },
21380
21381     setEndDate: function(endDate)
21382     {
21383         this.endDate = endDate || Infinity;
21384         if (this.endDate !== Infinity) {
21385             this.endDate = this.parseDate(this.endDate);
21386         }
21387         this.update();
21388         this.updateNavArrows();
21389     },
21390     
21391     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21392     {
21393         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21394         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21395             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21396         }
21397         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21398             return parseInt(d, 10);
21399         });
21400         this.update();
21401         this.updateNavArrows();
21402     },
21403     
21404     updateNavArrows: function() 
21405     {
21406         if(this.singleMode){
21407             return;
21408         }
21409         
21410         var d = new Date(this.viewDate),
21411         year = d.getUTCFullYear(),
21412         month = d.getUTCMonth();
21413         
21414         Roo.each(this.picker().select('.prev', true).elements, function(v){
21415             v.show();
21416             switch (this.viewMode) {
21417                 case 0:
21418
21419                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21420                         v.hide();
21421                     }
21422                     break;
21423                 case 1:
21424                 case 2:
21425                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21426                         v.hide();
21427                     }
21428                     break;
21429             }
21430         });
21431         
21432         Roo.each(this.picker().select('.next', true).elements, function(v){
21433             v.show();
21434             switch (this.viewMode) {
21435                 case 0:
21436
21437                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21438                         v.hide();
21439                     }
21440                     break;
21441                 case 1:
21442                 case 2:
21443                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21444                         v.hide();
21445                     }
21446                     break;
21447             }
21448         })
21449     },
21450     
21451     moveMonth: function(date, dir)
21452     {
21453         if (!dir) {
21454             return date;
21455         }
21456         var new_date = new Date(date.valueOf()),
21457         day = new_date.getUTCDate(),
21458         month = new_date.getUTCMonth(),
21459         mag = Math.abs(dir),
21460         new_month, test;
21461         dir = dir > 0 ? 1 : -1;
21462         if (mag == 1){
21463             test = dir == -1
21464             // If going back one month, make sure month is not current month
21465             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21466             ? function(){
21467                 return new_date.getUTCMonth() == month;
21468             }
21469             // If going forward one month, make sure month is as expected
21470             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21471             : function(){
21472                 return new_date.getUTCMonth() != new_month;
21473             };
21474             new_month = month + dir;
21475             new_date.setUTCMonth(new_month);
21476             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21477             if (new_month < 0 || new_month > 11) {
21478                 new_month = (new_month + 12) % 12;
21479             }
21480         } else {
21481             // For magnitudes >1, move one month at a time...
21482             for (var i=0; i<mag; i++) {
21483                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21484                 new_date = this.moveMonth(new_date, dir);
21485             }
21486             // ...then reset the day, keeping it in the new month
21487             new_month = new_date.getUTCMonth();
21488             new_date.setUTCDate(day);
21489             test = function(){
21490                 return new_month != new_date.getUTCMonth();
21491             };
21492         }
21493         // Common date-resetting loop -- if date is beyond end of month, make it
21494         // end of month
21495         while (test()){
21496             new_date.setUTCDate(--day);
21497             new_date.setUTCMonth(new_month);
21498         }
21499         return new_date;
21500     },
21501
21502     moveYear: function(date, dir)
21503     {
21504         return this.moveMonth(date, dir*12);
21505     },
21506
21507     dateWithinRange: function(date)
21508     {
21509         return date >= this.startDate && date <= this.endDate;
21510     },
21511
21512     
21513     remove: function() 
21514     {
21515         this.picker().remove();
21516     },
21517     
21518     validateValue : function(value)
21519     {
21520         if(this.getVisibilityEl().hasClass('hidden')){
21521             return true;
21522         }
21523         
21524         if(value.length < 1)  {
21525             if(this.allowBlank){
21526                 return true;
21527             }
21528             return false;
21529         }
21530         
21531         if(value.length < this.minLength){
21532             return false;
21533         }
21534         if(value.length > this.maxLength){
21535             return false;
21536         }
21537         if(this.vtype){
21538             var vt = Roo.form.VTypes;
21539             if(!vt[this.vtype](value, this)){
21540                 return false;
21541             }
21542         }
21543         if(typeof this.validator == "function"){
21544             var msg = this.validator(value);
21545             if(msg !== true){
21546                 return false;
21547             }
21548         }
21549         
21550         if(this.regex && !this.regex.test(value)){
21551             return false;
21552         }
21553         
21554         if(typeof(this.parseDate(value)) == 'undefined'){
21555             return false;
21556         }
21557         
21558         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21559             return false;
21560         }      
21561         
21562         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21563             return false;
21564         } 
21565         
21566         
21567         return true;
21568     },
21569     
21570     reset : function()
21571     {
21572         this.date = this.viewDate = '';
21573         
21574         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21575     }
21576    
21577 });
21578
21579 Roo.apply(Roo.bootstrap.DateField,  {
21580     
21581     head : {
21582         tag: 'thead',
21583         cn: [
21584         {
21585             tag: 'tr',
21586             cn: [
21587             {
21588                 tag: 'th',
21589                 cls: 'prev',
21590                 html: '<i class="fa fa-arrow-left"/>'
21591             },
21592             {
21593                 tag: 'th',
21594                 cls: 'switch',
21595                 colspan: '5'
21596             },
21597             {
21598                 tag: 'th',
21599                 cls: 'next',
21600                 html: '<i class="fa fa-arrow-right"/>'
21601             }
21602
21603             ]
21604         }
21605         ]
21606     },
21607     
21608     content : {
21609         tag: 'tbody',
21610         cn: [
21611         {
21612             tag: 'tr',
21613             cn: [
21614             {
21615                 tag: 'td',
21616                 colspan: '7'
21617             }
21618             ]
21619         }
21620         ]
21621     },
21622     
21623     footer : {
21624         tag: 'tfoot',
21625         cn: [
21626         {
21627             tag: 'tr',
21628             cn: [
21629             {
21630                 tag: 'th',
21631                 colspan: '7',
21632                 cls: 'today'
21633             }
21634                     
21635             ]
21636         }
21637         ]
21638     },
21639     
21640     dates:{
21641         en: {
21642             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21643             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21644             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21645             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21646             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21647             today: "Today"
21648         }
21649     },
21650     
21651     modes: [
21652     {
21653         clsName: 'days',
21654         navFnc: 'Month',
21655         navStep: 1
21656     },
21657     {
21658         clsName: 'months',
21659         navFnc: 'FullYear',
21660         navStep: 1
21661     },
21662     {
21663         clsName: 'years',
21664         navFnc: 'FullYear',
21665         navStep: 10
21666     }]
21667 });
21668
21669 Roo.apply(Roo.bootstrap.DateField,  {
21670   
21671     template : {
21672         tag: 'div',
21673         cls: 'datepicker dropdown-menu roo-dynamic',
21674         cn: [
21675         {
21676             tag: 'div',
21677             cls: 'datepicker-days',
21678             cn: [
21679             {
21680                 tag: 'table',
21681                 cls: 'table-condensed',
21682                 cn:[
21683                 Roo.bootstrap.DateField.head,
21684                 {
21685                     tag: 'tbody'
21686                 },
21687                 Roo.bootstrap.DateField.footer
21688                 ]
21689             }
21690             ]
21691         },
21692         {
21693             tag: 'div',
21694             cls: 'datepicker-months',
21695             cn: [
21696             {
21697                 tag: 'table',
21698                 cls: 'table-condensed',
21699                 cn:[
21700                 Roo.bootstrap.DateField.head,
21701                 Roo.bootstrap.DateField.content,
21702                 Roo.bootstrap.DateField.footer
21703                 ]
21704             }
21705             ]
21706         },
21707         {
21708             tag: 'div',
21709             cls: 'datepicker-years',
21710             cn: [
21711             {
21712                 tag: 'table',
21713                 cls: 'table-condensed',
21714                 cn:[
21715                 Roo.bootstrap.DateField.head,
21716                 Roo.bootstrap.DateField.content,
21717                 Roo.bootstrap.DateField.footer
21718                 ]
21719             }
21720             ]
21721         }
21722         ]
21723     }
21724 });
21725
21726  
21727
21728  /*
21729  * - LGPL
21730  *
21731  * TimeField
21732  * 
21733  */
21734
21735 /**
21736  * @class Roo.bootstrap.TimeField
21737  * @extends Roo.bootstrap.Input
21738  * Bootstrap DateField class
21739  * 
21740  * 
21741  * @constructor
21742  * Create a new TimeField
21743  * @param {Object} config The config object
21744  */
21745
21746 Roo.bootstrap.TimeField = function(config){
21747     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21748     this.addEvents({
21749             /**
21750              * @event show
21751              * Fires when this field show.
21752              * @param {Roo.bootstrap.DateField} thisthis
21753              * @param {Mixed} date The date value
21754              */
21755             show : true,
21756             /**
21757              * @event show
21758              * Fires when this field hide.
21759              * @param {Roo.bootstrap.DateField} this
21760              * @param {Mixed} date The date value
21761              */
21762             hide : true,
21763             /**
21764              * @event select
21765              * Fires when select a date.
21766              * @param {Roo.bootstrap.DateField} this
21767              * @param {Mixed} date The date value
21768              */
21769             select : true
21770         });
21771 };
21772
21773 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21774     
21775     /**
21776      * @cfg {String} format
21777      * The default time format string which can be overriden for localization support.  The format must be
21778      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21779      */
21780     format : "H:i",
21781        
21782     onRender: function(ct, position)
21783     {
21784         
21785         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21786                 
21787         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21788         
21789         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21790         
21791         this.pop = this.picker().select('>.datepicker-time',true).first();
21792         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21793         
21794         this.picker().on('mousedown', this.onMousedown, this);
21795         this.picker().on('click', this.onClick, this);
21796         
21797         this.picker().addClass('datepicker-dropdown');
21798     
21799         this.fillTime();
21800         this.update();
21801             
21802         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21803         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21804         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21805         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21806         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21807         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21808
21809     },
21810     
21811     fireKey: function(e){
21812         if (!this.picker().isVisible()){
21813             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21814                 this.show();
21815             }
21816             return;
21817         }
21818
21819         e.preventDefault();
21820         
21821         switch(e.keyCode){
21822             case 27: // escape
21823                 this.hide();
21824                 break;
21825             case 37: // left
21826             case 39: // right
21827                 this.onTogglePeriod();
21828                 break;
21829             case 38: // up
21830                 this.onIncrementMinutes();
21831                 break;
21832             case 40: // down
21833                 this.onDecrementMinutes();
21834                 break;
21835             case 13: // enter
21836             case 9: // tab
21837                 this.setTime();
21838                 break;
21839         }
21840     },
21841     
21842     onClick: function(e) {
21843         e.stopPropagation();
21844         e.preventDefault();
21845     },
21846     
21847     picker : function()
21848     {
21849         return this.el.select('.datepicker', true).first();
21850     },
21851     
21852     fillTime: function()
21853     {    
21854         var time = this.pop.select('tbody', true).first();
21855         
21856         time.dom.innerHTML = '';
21857         
21858         time.createChild({
21859             tag: 'tr',
21860             cn: [
21861                 {
21862                     tag: 'td',
21863                     cn: [
21864                         {
21865                             tag: 'a',
21866                             href: '#',
21867                             cls: 'btn',
21868                             cn: [
21869                                 {
21870                                     tag: 'span',
21871                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21872                                 }
21873                             ]
21874                         } 
21875                     ]
21876                 },
21877                 {
21878                     tag: 'td',
21879                     cls: 'separator'
21880                 },
21881                 {
21882                     tag: 'td',
21883                     cn: [
21884                         {
21885                             tag: 'a',
21886                             href: '#',
21887                             cls: 'btn',
21888                             cn: [
21889                                 {
21890                                     tag: 'span',
21891                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21892                                 }
21893                             ]
21894                         }
21895                     ]
21896                 },
21897                 {
21898                     tag: 'td',
21899                     cls: 'separator'
21900                 }
21901             ]
21902         });
21903         
21904         time.createChild({
21905             tag: 'tr',
21906             cn: [
21907                 {
21908                     tag: 'td',
21909                     cn: [
21910                         {
21911                             tag: 'span',
21912                             cls: 'timepicker-hour',
21913                             html: '00'
21914                         }  
21915                     ]
21916                 },
21917                 {
21918                     tag: 'td',
21919                     cls: 'separator',
21920                     html: ':'
21921                 },
21922                 {
21923                     tag: 'td',
21924                     cn: [
21925                         {
21926                             tag: 'span',
21927                             cls: 'timepicker-minute',
21928                             html: '00'
21929                         }  
21930                     ]
21931                 },
21932                 {
21933                     tag: 'td',
21934                     cls: 'separator'
21935                 },
21936                 {
21937                     tag: 'td',
21938                     cn: [
21939                         {
21940                             tag: 'button',
21941                             type: 'button',
21942                             cls: 'btn btn-primary period',
21943                             html: 'AM'
21944                             
21945                         }
21946                     ]
21947                 }
21948             ]
21949         });
21950         
21951         time.createChild({
21952             tag: 'tr',
21953             cn: [
21954                 {
21955                     tag: 'td',
21956                     cn: [
21957                         {
21958                             tag: 'a',
21959                             href: '#',
21960                             cls: 'btn',
21961                             cn: [
21962                                 {
21963                                     tag: 'span',
21964                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21965                                 }
21966                             ]
21967                         }
21968                     ]
21969                 },
21970                 {
21971                     tag: 'td',
21972                     cls: 'separator'
21973                 },
21974                 {
21975                     tag: 'td',
21976                     cn: [
21977                         {
21978                             tag: 'a',
21979                             href: '#',
21980                             cls: 'btn',
21981                             cn: [
21982                                 {
21983                                     tag: 'span',
21984                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21985                                 }
21986                             ]
21987                         }
21988                     ]
21989                 },
21990                 {
21991                     tag: 'td',
21992                     cls: 'separator'
21993                 }
21994             ]
21995         });
21996         
21997     },
21998     
21999     update: function()
22000     {
22001         
22002         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22003         
22004         this.fill();
22005     },
22006     
22007     fill: function() 
22008     {
22009         var hours = this.time.getHours();
22010         var minutes = this.time.getMinutes();
22011         var period = 'AM';
22012         
22013         if(hours > 11){
22014             period = 'PM';
22015         }
22016         
22017         if(hours == 0){
22018             hours = 12;
22019         }
22020         
22021         
22022         if(hours > 12){
22023             hours = hours - 12;
22024         }
22025         
22026         if(hours < 10){
22027             hours = '0' + hours;
22028         }
22029         
22030         if(minutes < 10){
22031             minutes = '0' + minutes;
22032         }
22033         
22034         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22035         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22036         this.pop.select('button', true).first().dom.innerHTML = period;
22037         
22038     },
22039     
22040     place: function()
22041     {   
22042         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22043         
22044         var cls = ['bottom'];
22045         
22046         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22047             cls.pop();
22048             cls.push('top');
22049         }
22050         
22051         cls.push('right');
22052         
22053         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22054             cls.pop();
22055             cls.push('left');
22056         }
22057         
22058         this.picker().addClass(cls.join('-'));
22059         
22060         var _this = this;
22061         
22062         Roo.each(cls, function(c){
22063             if(c == 'bottom'){
22064                 _this.picker().setTop(_this.inputEl().getHeight());
22065                 return;
22066             }
22067             if(c == 'top'){
22068                 _this.picker().setTop(0 - _this.picker().getHeight());
22069                 return;
22070             }
22071             
22072             if(c == 'left'){
22073                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22074                 return;
22075             }
22076             if(c == 'right'){
22077                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22078                 return;
22079             }
22080         });
22081         
22082     },
22083   
22084     onFocus : function()
22085     {
22086         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22087         this.show();
22088     },
22089     
22090     onBlur : function()
22091     {
22092         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22093         this.hide();
22094     },
22095     
22096     show : function()
22097     {
22098         this.picker().show();
22099         this.pop.show();
22100         this.update();
22101         this.place();
22102         
22103         this.fireEvent('show', this, this.date);
22104     },
22105     
22106     hide : function()
22107     {
22108         this.picker().hide();
22109         this.pop.hide();
22110         
22111         this.fireEvent('hide', this, this.date);
22112     },
22113     
22114     setTime : function()
22115     {
22116         this.hide();
22117         this.setValue(this.time.format(this.format));
22118         
22119         this.fireEvent('select', this, this.date);
22120         
22121         
22122     },
22123     
22124     onMousedown: function(e){
22125         e.stopPropagation();
22126         e.preventDefault();
22127     },
22128     
22129     onIncrementHours: function()
22130     {
22131         Roo.log('onIncrementHours');
22132         this.time = this.time.add(Date.HOUR, 1);
22133         this.update();
22134         
22135     },
22136     
22137     onDecrementHours: function()
22138     {
22139         Roo.log('onDecrementHours');
22140         this.time = this.time.add(Date.HOUR, -1);
22141         this.update();
22142     },
22143     
22144     onIncrementMinutes: function()
22145     {
22146         Roo.log('onIncrementMinutes');
22147         this.time = this.time.add(Date.MINUTE, 1);
22148         this.update();
22149     },
22150     
22151     onDecrementMinutes: function()
22152     {
22153         Roo.log('onDecrementMinutes');
22154         this.time = this.time.add(Date.MINUTE, -1);
22155         this.update();
22156     },
22157     
22158     onTogglePeriod: function()
22159     {
22160         Roo.log('onTogglePeriod');
22161         this.time = this.time.add(Date.HOUR, 12);
22162         this.update();
22163     }
22164     
22165    
22166 });
22167
22168 Roo.apply(Roo.bootstrap.TimeField,  {
22169     
22170     content : {
22171         tag: 'tbody',
22172         cn: [
22173             {
22174                 tag: 'tr',
22175                 cn: [
22176                 {
22177                     tag: 'td',
22178                     colspan: '7'
22179                 }
22180                 ]
22181             }
22182         ]
22183     },
22184     
22185     footer : {
22186         tag: 'tfoot',
22187         cn: [
22188             {
22189                 tag: 'tr',
22190                 cn: [
22191                 {
22192                     tag: 'th',
22193                     colspan: '7',
22194                     cls: '',
22195                     cn: [
22196                         {
22197                             tag: 'button',
22198                             cls: 'btn btn-info ok',
22199                             html: 'OK'
22200                         }
22201                     ]
22202                 }
22203
22204                 ]
22205             }
22206         ]
22207     }
22208 });
22209
22210 Roo.apply(Roo.bootstrap.TimeField,  {
22211   
22212     template : {
22213         tag: 'div',
22214         cls: 'datepicker dropdown-menu',
22215         cn: [
22216             {
22217                 tag: 'div',
22218                 cls: 'datepicker-time',
22219                 cn: [
22220                 {
22221                     tag: 'table',
22222                     cls: 'table-condensed',
22223                     cn:[
22224                     Roo.bootstrap.TimeField.content,
22225                     Roo.bootstrap.TimeField.footer
22226                     ]
22227                 }
22228                 ]
22229             }
22230         ]
22231     }
22232 });
22233
22234  
22235
22236  /*
22237  * - LGPL
22238  *
22239  * MonthField
22240  * 
22241  */
22242
22243 /**
22244  * @class Roo.bootstrap.MonthField
22245  * @extends Roo.bootstrap.Input
22246  * Bootstrap MonthField class
22247  * 
22248  * @cfg {String} language default en
22249  * 
22250  * @constructor
22251  * Create a new MonthField
22252  * @param {Object} config The config object
22253  */
22254
22255 Roo.bootstrap.MonthField = function(config){
22256     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22257     
22258     this.addEvents({
22259         /**
22260          * @event show
22261          * Fires when this field show.
22262          * @param {Roo.bootstrap.MonthField} this
22263          * @param {Mixed} date The date value
22264          */
22265         show : true,
22266         /**
22267          * @event show
22268          * Fires when this field hide.
22269          * @param {Roo.bootstrap.MonthField} this
22270          * @param {Mixed} date The date value
22271          */
22272         hide : true,
22273         /**
22274          * @event select
22275          * Fires when select a date.
22276          * @param {Roo.bootstrap.MonthField} this
22277          * @param {String} oldvalue The old value
22278          * @param {String} newvalue The new value
22279          */
22280         select : true
22281     });
22282 };
22283
22284 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22285     
22286     onRender: function(ct, position)
22287     {
22288         
22289         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22290         
22291         this.language = this.language || 'en';
22292         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22293         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22294         
22295         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22296         this.isInline = false;
22297         this.isInput = true;
22298         this.component = this.el.select('.add-on', true).first() || false;
22299         this.component = (this.component && this.component.length === 0) ? false : this.component;
22300         this.hasInput = this.component && this.inputEL().length;
22301         
22302         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22303         
22304         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22305         
22306         this.picker().on('mousedown', this.onMousedown, this);
22307         this.picker().on('click', this.onClick, this);
22308         
22309         this.picker().addClass('datepicker-dropdown');
22310         
22311         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22312             v.setStyle('width', '189px');
22313         });
22314         
22315         this.fillMonths();
22316         
22317         this.update();
22318         
22319         if(this.isInline) {
22320             this.show();
22321         }
22322         
22323     },
22324     
22325     setValue: function(v, suppressEvent)
22326     {   
22327         var o = this.getValue();
22328         
22329         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22330         
22331         this.update();
22332
22333         if(suppressEvent !== true){
22334             this.fireEvent('select', this, o, v);
22335         }
22336         
22337     },
22338     
22339     getValue: function()
22340     {
22341         return this.value;
22342     },
22343     
22344     onClick: function(e) 
22345     {
22346         e.stopPropagation();
22347         e.preventDefault();
22348         
22349         var target = e.getTarget();
22350         
22351         if(target.nodeName.toLowerCase() === 'i'){
22352             target = Roo.get(target).dom.parentNode;
22353         }
22354         
22355         var nodeName = target.nodeName;
22356         var className = target.className;
22357         var html = target.innerHTML;
22358         
22359         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22360             return;
22361         }
22362         
22363         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22364         
22365         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22366         
22367         this.hide();
22368                         
22369     },
22370     
22371     picker : function()
22372     {
22373         return this.pickerEl;
22374     },
22375     
22376     fillMonths: function()
22377     {    
22378         var i = 0;
22379         var months = this.picker().select('>.datepicker-months td', true).first();
22380         
22381         months.dom.innerHTML = '';
22382         
22383         while (i < 12) {
22384             var month = {
22385                 tag: 'span',
22386                 cls: 'month',
22387                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22388             };
22389             
22390             months.createChild(month);
22391         }
22392         
22393     },
22394     
22395     update: function()
22396     {
22397         var _this = this;
22398         
22399         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22400             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22401         }
22402         
22403         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22404             e.removeClass('active');
22405             
22406             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22407                 e.addClass('active');
22408             }
22409         })
22410     },
22411     
22412     place: function()
22413     {
22414         if(this.isInline) {
22415             return;
22416         }
22417         
22418         this.picker().removeClass(['bottom', 'top']);
22419         
22420         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22421             /*
22422              * place to the top of element!
22423              *
22424              */
22425             
22426             this.picker().addClass('top');
22427             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22428             
22429             return;
22430         }
22431         
22432         this.picker().addClass('bottom');
22433         
22434         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22435     },
22436     
22437     onFocus : function()
22438     {
22439         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22440         this.show();
22441     },
22442     
22443     onBlur : function()
22444     {
22445         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22446         
22447         var d = this.inputEl().getValue();
22448         
22449         this.setValue(d);
22450                 
22451         this.hide();
22452     },
22453     
22454     show : function()
22455     {
22456         this.picker().show();
22457         this.picker().select('>.datepicker-months', true).first().show();
22458         this.update();
22459         this.place();
22460         
22461         this.fireEvent('show', this, this.date);
22462     },
22463     
22464     hide : function()
22465     {
22466         if(this.isInline) {
22467             return;
22468         }
22469         this.picker().hide();
22470         this.fireEvent('hide', this, this.date);
22471         
22472     },
22473     
22474     onMousedown: function(e)
22475     {
22476         e.stopPropagation();
22477         e.preventDefault();
22478     },
22479     
22480     keyup: function(e)
22481     {
22482         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22483         this.update();
22484     },
22485
22486     fireKey: function(e)
22487     {
22488         if (!this.picker().isVisible()){
22489             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22490                 this.show();
22491             }
22492             return;
22493         }
22494         
22495         var dir;
22496         
22497         switch(e.keyCode){
22498             case 27: // escape
22499                 this.hide();
22500                 e.preventDefault();
22501                 break;
22502             case 37: // left
22503             case 39: // right
22504                 dir = e.keyCode == 37 ? -1 : 1;
22505                 
22506                 this.vIndex = this.vIndex + dir;
22507                 
22508                 if(this.vIndex < 0){
22509                     this.vIndex = 0;
22510                 }
22511                 
22512                 if(this.vIndex > 11){
22513                     this.vIndex = 11;
22514                 }
22515                 
22516                 if(isNaN(this.vIndex)){
22517                     this.vIndex = 0;
22518                 }
22519                 
22520                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22521                 
22522                 break;
22523             case 38: // up
22524             case 40: // down
22525                 
22526                 dir = e.keyCode == 38 ? -1 : 1;
22527                 
22528                 this.vIndex = this.vIndex + dir * 4;
22529                 
22530                 if(this.vIndex < 0){
22531                     this.vIndex = 0;
22532                 }
22533                 
22534                 if(this.vIndex > 11){
22535                     this.vIndex = 11;
22536                 }
22537                 
22538                 if(isNaN(this.vIndex)){
22539                     this.vIndex = 0;
22540                 }
22541                 
22542                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22543                 break;
22544                 
22545             case 13: // enter
22546                 
22547                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22548                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22549                 }
22550                 
22551                 this.hide();
22552                 e.preventDefault();
22553                 break;
22554             case 9: // tab
22555                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22556                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22557                 }
22558                 this.hide();
22559                 break;
22560             case 16: // shift
22561             case 17: // ctrl
22562             case 18: // alt
22563                 break;
22564             default :
22565                 this.hide();
22566                 
22567         }
22568     },
22569     
22570     remove: function() 
22571     {
22572         this.picker().remove();
22573     }
22574    
22575 });
22576
22577 Roo.apply(Roo.bootstrap.MonthField,  {
22578     
22579     content : {
22580         tag: 'tbody',
22581         cn: [
22582         {
22583             tag: 'tr',
22584             cn: [
22585             {
22586                 tag: 'td',
22587                 colspan: '7'
22588             }
22589             ]
22590         }
22591         ]
22592     },
22593     
22594     dates:{
22595         en: {
22596             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22597             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22598         }
22599     }
22600 });
22601
22602 Roo.apply(Roo.bootstrap.MonthField,  {
22603   
22604     template : {
22605         tag: 'div',
22606         cls: 'datepicker dropdown-menu roo-dynamic',
22607         cn: [
22608             {
22609                 tag: 'div',
22610                 cls: 'datepicker-months',
22611                 cn: [
22612                 {
22613                     tag: 'table',
22614                     cls: 'table-condensed',
22615                     cn:[
22616                         Roo.bootstrap.DateField.content
22617                     ]
22618                 }
22619                 ]
22620             }
22621         ]
22622     }
22623 });
22624
22625  
22626
22627  
22628  /*
22629  * - LGPL
22630  *
22631  * CheckBox
22632  * 
22633  */
22634
22635 /**
22636  * @class Roo.bootstrap.CheckBox
22637  * @extends Roo.bootstrap.Input
22638  * Bootstrap CheckBox class
22639  * 
22640  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22641  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22642  * @cfg {String} boxLabel The text that appears beside the checkbox
22643  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22644  * @cfg {Boolean} checked initnal the element
22645  * @cfg {Boolean} inline inline the element (default false)
22646  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22647  * @cfg {String} tooltip label tooltip
22648  * 
22649  * @constructor
22650  * Create a new CheckBox
22651  * @param {Object} config The config object
22652  */
22653
22654 Roo.bootstrap.CheckBox = function(config){
22655     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22656    
22657     this.addEvents({
22658         /**
22659         * @event check
22660         * Fires when the element is checked or unchecked.
22661         * @param {Roo.bootstrap.CheckBox} this This input
22662         * @param {Boolean} checked The new checked value
22663         */
22664        check : true,
22665        /**
22666         * @event click
22667         * Fires when the element is click.
22668         * @param {Roo.bootstrap.CheckBox} this This input
22669         */
22670        click : true
22671     });
22672     
22673 };
22674
22675 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22676   
22677     inputType: 'checkbox',
22678     inputValue: 1,
22679     valueOff: 0,
22680     boxLabel: false,
22681     checked: false,
22682     weight : false,
22683     inline: false,
22684     tooltip : '',
22685     
22686     // checkbox success does not make any sense really.. 
22687     invalidClass : "",
22688     validClass : "",
22689     
22690     
22691     getAutoCreate : function()
22692     {
22693         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22694         
22695         var id = Roo.id();
22696         
22697         var cfg = {};
22698         
22699         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22700         
22701         if(this.inline){
22702             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22703         }
22704         
22705         var input =  {
22706             tag: 'input',
22707             id : id,
22708             type : this.inputType,
22709             value : this.inputValue,
22710             cls : 'roo-' + this.inputType, //'form-box',
22711             placeholder : this.placeholder || ''
22712             
22713         };
22714         
22715         if(this.inputType != 'radio'){
22716             var hidden =  {
22717                 tag: 'input',
22718                 type : 'hidden',
22719                 cls : 'roo-hidden-value',
22720                 value : this.checked ? this.inputValue : this.valueOff
22721             };
22722         }
22723         
22724             
22725         if (this.weight) { // Validity check?
22726             cfg.cls += " " + this.inputType + "-" + this.weight;
22727         }
22728         
22729         if (this.disabled) {
22730             input.disabled=true;
22731         }
22732         
22733         if(this.checked){
22734             input.checked = this.checked;
22735         }
22736         
22737         if (this.name) {
22738             
22739             input.name = this.name;
22740             
22741             if(this.inputType != 'radio'){
22742                 hidden.name = this.name;
22743                 input.name = '_hidden_' + this.name;
22744             }
22745         }
22746         
22747         if (this.size) {
22748             input.cls += ' input-' + this.size;
22749         }
22750         
22751         var settings=this;
22752         
22753         ['xs','sm','md','lg'].map(function(size){
22754             if (settings[size]) {
22755                 cfg.cls += ' col-' + size + '-' + settings[size];
22756             }
22757         });
22758         
22759         var inputblock = input;
22760          
22761         if (this.before || this.after) {
22762             
22763             inputblock = {
22764                 cls : 'input-group',
22765                 cn :  [] 
22766             };
22767             
22768             if (this.before) {
22769                 inputblock.cn.push({
22770                     tag :'span',
22771                     cls : 'input-group-addon',
22772                     html : this.before
22773                 });
22774             }
22775             
22776             inputblock.cn.push(input);
22777             
22778             if(this.inputType != 'radio'){
22779                 inputblock.cn.push(hidden);
22780             }
22781             
22782             if (this.after) {
22783                 inputblock.cn.push({
22784                     tag :'span',
22785                     cls : 'input-group-addon',
22786                     html : this.after
22787                 });
22788             }
22789             
22790         }
22791         var boxLabelCfg = false;
22792         
22793         if(this.boxLabel){
22794            
22795             boxLabelCfg = {
22796                 tag: 'label',
22797                 //'for': id, // box label is handled by onclick - so no for...
22798                 cls: 'box-label',
22799                 html: this.boxLabel
22800             };
22801             if(this.tooltip){
22802                 boxLabelCfg.tooltip = this.tooltip;
22803             }
22804              
22805         }
22806         
22807         
22808         if (align ==='left' && this.fieldLabel.length) {
22809 //                Roo.log("left and has label");
22810             cfg.cn = [
22811                 {
22812                     tag: 'label',
22813                     'for' :  id,
22814                     cls : 'control-label',
22815                     html : this.fieldLabel
22816                 },
22817                 {
22818                     cls : "", 
22819                     cn: [
22820                         inputblock
22821                     ]
22822                 }
22823             ];
22824             
22825             if (boxLabelCfg) {
22826                 cfg.cn[1].cn.push(boxLabelCfg);
22827             }
22828             
22829             if(this.labelWidth > 12){
22830                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22831             }
22832             
22833             if(this.labelWidth < 13 && this.labelmd == 0){
22834                 this.labelmd = this.labelWidth;
22835             }
22836             
22837             if(this.labellg > 0){
22838                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22839                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22840             }
22841             
22842             if(this.labelmd > 0){
22843                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22844                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22845             }
22846             
22847             if(this.labelsm > 0){
22848                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22849                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22850             }
22851             
22852             if(this.labelxs > 0){
22853                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22854                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22855             }
22856             
22857         } else if ( this.fieldLabel.length) {
22858 //                Roo.log(" label");
22859                 cfg.cn = [
22860                    
22861                     {
22862                         tag: this.boxLabel ? 'span' : 'label',
22863                         'for': id,
22864                         cls: 'control-label box-input-label',
22865                         //cls : 'input-group-addon',
22866                         html : this.fieldLabel
22867                     },
22868                     
22869                     inputblock
22870                     
22871                 ];
22872                 if (boxLabelCfg) {
22873                     cfg.cn.push(boxLabelCfg);
22874                 }
22875
22876         } else {
22877             
22878 //                Roo.log(" no label && no align");
22879                 cfg.cn = [  inputblock ] ;
22880                 if (boxLabelCfg) {
22881                     cfg.cn.push(boxLabelCfg);
22882                 }
22883
22884                 
22885         }
22886         
22887        
22888         
22889         if(this.inputType != 'radio'){
22890             cfg.cn.push(hidden);
22891         }
22892         
22893         return cfg;
22894         
22895     },
22896     
22897     /**
22898      * return the real input element.
22899      */
22900     inputEl: function ()
22901     {
22902         return this.el.select('input.roo-' + this.inputType,true).first();
22903     },
22904     hiddenEl: function ()
22905     {
22906         return this.el.select('input.roo-hidden-value',true).first();
22907     },
22908     
22909     labelEl: function()
22910     {
22911         return this.el.select('label.control-label',true).first();
22912     },
22913     /* depricated... */
22914     
22915     label: function()
22916     {
22917         return this.labelEl();
22918     },
22919     
22920     boxLabelEl: function()
22921     {
22922         return this.el.select('label.box-label',true).first();
22923     },
22924     
22925     initEvents : function()
22926     {
22927 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22928         
22929         this.inputEl().on('click', this.onClick,  this);
22930         
22931         if (this.boxLabel) { 
22932             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22933         }
22934         
22935         this.startValue = this.getValue();
22936         
22937         if(this.groupId){
22938             Roo.bootstrap.CheckBox.register(this);
22939         }
22940     },
22941     
22942     onClick : function(e)
22943     {   
22944         if(this.fireEvent('click', this, e) !== false){
22945             this.setChecked(!this.checked);
22946         }
22947         
22948     },
22949     
22950     setChecked : function(state,suppressEvent)
22951     {
22952         this.startValue = this.getValue();
22953
22954         if(this.inputType == 'radio'){
22955             
22956             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22957                 e.dom.checked = false;
22958             });
22959             
22960             this.inputEl().dom.checked = true;
22961             
22962             this.inputEl().dom.value = this.inputValue;
22963             
22964             if(suppressEvent !== true){
22965                 this.fireEvent('check', this, true);
22966             }
22967             
22968             this.validate();
22969             
22970             return;
22971         }
22972         
22973         this.checked = state;
22974         
22975         this.inputEl().dom.checked = state;
22976         
22977         
22978         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22979         
22980         if(suppressEvent !== true){
22981             this.fireEvent('check', this, state);
22982         }
22983         
22984         this.validate();
22985     },
22986     
22987     getValue : function()
22988     {
22989         if(this.inputType == 'radio'){
22990             return this.getGroupValue();
22991         }
22992         
22993         return this.hiddenEl().dom.value;
22994         
22995     },
22996     
22997     getGroupValue : function()
22998     {
22999         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23000             return '';
23001         }
23002         
23003         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23004     },
23005     
23006     setValue : function(v,suppressEvent)
23007     {
23008         if(this.inputType == 'radio'){
23009             this.setGroupValue(v, suppressEvent);
23010             return;
23011         }
23012         
23013         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23014         
23015         this.validate();
23016     },
23017     
23018     setGroupValue : function(v, suppressEvent)
23019     {
23020         this.startValue = this.getValue();
23021         
23022         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23023             e.dom.checked = false;
23024             
23025             if(e.dom.value == v){
23026                 e.dom.checked = true;
23027             }
23028         });
23029         
23030         if(suppressEvent !== true){
23031             this.fireEvent('check', this, true);
23032         }
23033
23034         this.validate();
23035         
23036         return;
23037     },
23038     
23039     validate : function()
23040     {
23041         if(this.getVisibilityEl().hasClass('hidden')){
23042             return true;
23043         }
23044         
23045         if(
23046                 this.disabled || 
23047                 (this.inputType == 'radio' && this.validateRadio()) ||
23048                 (this.inputType == 'checkbox' && this.validateCheckbox())
23049         ){
23050             this.markValid();
23051             return true;
23052         }
23053         
23054         this.markInvalid();
23055         return false;
23056     },
23057     
23058     validateRadio : function()
23059     {
23060         if(this.getVisibilityEl().hasClass('hidden')){
23061             return true;
23062         }
23063         
23064         if(this.allowBlank){
23065             return true;
23066         }
23067         
23068         var valid = false;
23069         
23070         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23071             if(!e.dom.checked){
23072                 return;
23073             }
23074             
23075             valid = true;
23076             
23077             return false;
23078         });
23079         
23080         return valid;
23081     },
23082     
23083     validateCheckbox : function()
23084     {
23085         if(!this.groupId){
23086             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23087             //return (this.getValue() == this.inputValue) ? true : false;
23088         }
23089         
23090         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23091         
23092         if(!group){
23093             return false;
23094         }
23095         
23096         var r = false;
23097         
23098         for(var i in group){
23099             if(group[i].el.isVisible(true)){
23100                 r = false;
23101                 break;
23102             }
23103             
23104             r = true;
23105         }
23106         
23107         for(var i in group){
23108             if(r){
23109                 break;
23110             }
23111             
23112             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23113         }
23114         
23115         return r;
23116     },
23117     
23118     /**
23119      * Mark this field as valid
23120      */
23121     markValid : function()
23122     {
23123         var _this = this;
23124         
23125         this.fireEvent('valid', this);
23126         
23127         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23128         
23129         if(this.groupId){
23130             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23131         }
23132         
23133         if(label){
23134             label.markValid();
23135         }
23136
23137         if(this.inputType == 'radio'){
23138             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23139                 var fg = e.findParent('.form-group', false, true);
23140                 if (Roo.bootstrap.version == 3) {
23141                     fg.removeClass([_this.invalidClass, _this.validClass]);
23142                     fg.addClass(_this.validClass);
23143                 } else {
23144                     fg.removeClass(['is-valid', 'is-invalid']);
23145                     fg.addClass('is-valid');
23146                 }
23147             });
23148             
23149             return;
23150         }
23151
23152         if(!this.groupId){
23153             var fg = this.el.findParent('.form-group', false, true);
23154             if (Roo.bootstrap.version == 3) {
23155                 fg.removeClass([this.invalidClass, this.validClass]);
23156                 fg.addClass(this.validClass);
23157             } else {
23158                 fg.removeClass(['is-valid', 'is-invalid']);
23159                 fg.addClass('is-valid');
23160             }
23161             return;
23162         }
23163         
23164         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23165         
23166         if(!group){
23167             return;
23168         }
23169         
23170         for(var i in group){
23171             var fg = group[i].el.findParent('.form-group', false, true);
23172             if (Roo.bootstrap.version == 3) {
23173                 fg.removeClass([this.invalidClass, this.validClass]);
23174                 fg.addClass(this.validClass);
23175             } else {
23176                 fg.removeClass(['is-valid', 'is-invalid']);
23177                 fg.addClass('is-valid');
23178             }
23179         }
23180     },
23181     
23182      /**
23183      * Mark this field as invalid
23184      * @param {String} msg The validation message
23185      */
23186     markInvalid : function(msg)
23187     {
23188         if(this.allowBlank){
23189             return;
23190         }
23191         
23192         var _this = this;
23193         
23194         this.fireEvent('invalid', this, msg);
23195         
23196         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23197         
23198         if(this.groupId){
23199             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23200         }
23201         
23202         if(label){
23203             label.markInvalid();
23204         }
23205             
23206         if(this.inputType == 'radio'){
23207             
23208             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23209                 var fg = e.findParent('.form-group', false, true);
23210                 if (Roo.bootstrap.version == 3) {
23211                     fg.removeClass([_this.invalidClass, _this.validClass]);
23212                     fg.addClass(_this.invalidClass);
23213                 } else {
23214                     fg.removeClass(['is-invalid', 'is-valid']);
23215                     fg.addClass('is-invalid');
23216                 }
23217             });
23218             
23219             return;
23220         }
23221         
23222         if(!this.groupId){
23223             var fg = this.el.findParent('.form-group', false, true);
23224             if (Roo.bootstrap.version == 3) {
23225                 fg.removeClass([_this.invalidClass, _this.validClass]);
23226                 fg.addClass(_this.invalidClass);
23227             } else {
23228                 fg.removeClass(['is-invalid', 'is-valid']);
23229                 fg.addClass('is-invalid');
23230             }
23231             return;
23232         }
23233         
23234         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23235         
23236         if(!group){
23237             return;
23238         }
23239         
23240         for(var i in group){
23241             var fg = group[i].el.findParent('.form-group', false, true);
23242             if (Roo.bootstrap.version == 3) {
23243                 fg.removeClass([_this.invalidClass, _this.validClass]);
23244                 fg.addClass(_this.invalidClass);
23245             } else {
23246                 fg.removeClass(['is-invalid', 'is-valid']);
23247                 fg.addClass('is-invalid');
23248             }
23249         }
23250         
23251     },
23252     
23253     clearInvalid : function()
23254     {
23255         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23256         
23257         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23258         
23259         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23260         
23261         if (label && label.iconEl) {
23262             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23263             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23264         }
23265     },
23266     
23267     disable : function()
23268     {
23269         if(this.inputType != 'radio'){
23270             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23271             return;
23272         }
23273         
23274         var _this = this;
23275         
23276         if(this.rendered){
23277             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23278                 _this.getActionEl().addClass(this.disabledClass);
23279                 e.dom.disabled = true;
23280             });
23281         }
23282         
23283         this.disabled = true;
23284         this.fireEvent("disable", this);
23285         return this;
23286     },
23287
23288     enable : function()
23289     {
23290         if(this.inputType != 'radio'){
23291             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23292             return;
23293         }
23294         
23295         var _this = this;
23296         
23297         if(this.rendered){
23298             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23299                 _this.getActionEl().removeClass(this.disabledClass);
23300                 e.dom.disabled = false;
23301             });
23302         }
23303         
23304         this.disabled = false;
23305         this.fireEvent("enable", this);
23306         return this;
23307     },
23308     
23309     setBoxLabel : function(v)
23310     {
23311         this.boxLabel = v;
23312         
23313         if(this.rendered){
23314             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23315         }
23316     }
23317
23318 });
23319
23320 Roo.apply(Roo.bootstrap.CheckBox, {
23321     
23322     groups: {},
23323     
23324      /**
23325     * register a CheckBox Group
23326     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23327     */
23328     register : function(checkbox)
23329     {
23330         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23331             this.groups[checkbox.groupId] = {};
23332         }
23333         
23334         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23335             return;
23336         }
23337         
23338         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23339         
23340     },
23341     /**
23342     * fetch a CheckBox Group based on the group ID
23343     * @param {string} the group ID
23344     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23345     */
23346     get: function(groupId) {
23347         if (typeof(this.groups[groupId]) == 'undefined') {
23348             return false;
23349         }
23350         
23351         return this.groups[groupId] ;
23352     }
23353     
23354     
23355 });
23356 /*
23357  * - LGPL
23358  *
23359  * RadioItem
23360  * 
23361  */
23362
23363 /**
23364  * @class Roo.bootstrap.Radio
23365  * @extends Roo.bootstrap.Component
23366  * Bootstrap Radio class
23367  * @cfg {String} boxLabel - the label associated
23368  * @cfg {String} value - the value of radio
23369  * 
23370  * @constructor
23371  * Create a new Radio
23372  * @param {Object} config The config object
23373  */
23374 Roo.bootstrap.Radio = function(config){
23375     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23376     
23377 };
23378
23379 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23380     
23381     boxLabel : '',
23382     
23383     value : '',
23384     
23385     getAutoCreate : function()
23386     {
23387         var cfg = {
23388             tag : 'div',
23389             cls : 'form-group radio',
23390             cn : [
23391                 {
23392                     tag : 'label',
23393                     cls : 'box-label',
23394                     html : this.boxLabel
23395                 }
23396             ]
23397         };
23398         
23399         return cfg;
23400     },
23401     
23402     initEvents : function() 
23403     {
23404         this.parent().register(this);
23405         
23406         this.el.on('click', this.onClick, this);
23407         
23408     },
23409     
23410     onClick : function(e)
23411     {
23412         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23413             this.setChecked(true);
23414         }
23415     },
23416     
23417     setChecked : function(state, suppressEvent)
23418     {
23419         this.parent().setValue(this.value, suppressEvent);
23420         
23421     },
23422     
23423     setBoxLabel : function(v)
23424     {
23425         this.boxLabel = v;
23426         
23427         if(this.rendered){
23428             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23429         }
23430     }
23431     
23432 });
23433  
23434
23435  /*
23436  * - LGPL
23437  *
23438  * Input
23439  * 
23440  */
23441
23442 /**
23443  * @class Roo.bootstrap.SecurePass
23444  * @extends Roo.bootstrap.Input
23445  * Bootstrap SecurePass class
23446  *
23447  * 
23448  * @constructor
23449  * Create a new SecurePass
23450  * @param {Object} config The config object
23451  */
23452  
23453 Roo.bootstrap.SecurePass = function (config) {
23454     // these go here, so the translation tool can replace them..
23455     this.errors = {
23456         PwdEmpty: "Please type a password, and then retype it to confirm.",
23457         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23458         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23459         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23460         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23461         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23462         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23463         TooWeak: "Your password is Too Weak."
23464     },
23465     this.meterLabel = "Password strength:";
23466     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23467     this.meterClass = [
23468         "roo-password-meter-tooweak", 
23469         "roo-password-meter-weak", 
23470         "roo-password-meter-medium", 
23471         "roo-password-meter-strong", 
23472         "roo-password-meter-grey"
23473     ];
23474     
23475     this.errors = {};
23476     
23477     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23478 }
23479
23480 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23481     /**
23482      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23483      * {
23484      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23485      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23486      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23487      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23488      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23489      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23490      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23491      * })
23492      */
23493     // private
23494     
23495     meterWidth: 300,
23496     errorMsg :'',    
23497     errors: false,
23498     imageRoot: '/',
23499     /**
23500      * @cfg {String/Object} Label for the strength meter (defaults to
23501      * 'Password strength:')
23502      */
23503     // private
23504     meterLabel: '',
23505     /**
23506      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23507      * ['Weak', 'Medium', 'Strong'])
23508      */
23509     // private    
23510     pwdStrengths: false,    
23511     // private
23512     strength: 0,
23513     // private
23514     _lastPwd: null,
23515     // private
23516     kCapitalLetter: 0,
23517     kSmallLetter: 1,
23518     kDigit: 2,
23519     kPunctuation: 3,
23520     
23521     insecure: false,
23522     // private
23523     initEvents: function ()
23524     {
23525         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23526
23527         if (this.el.is('input[type=password]') && Roo.isSafari) {
23528             this.el.on('keydown', this.SafariOnKeyDown, this);
23529         }
23530
23531         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23532     },
23533     // private
23534     onRender: function (ct, position)
23535     {
23536         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23537         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23538         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23539
23540         this.trigger.createChild({
23541                    cn: [
23542                     {
23543                     //id: 'PwdMeter',
23544                     tag: 'div',
23545                     cls: 'roo-password-meter-grey col-xs-12',
23546                     style: {
23547                         //width: 0,
23548                         //width: this.meterWidth + 'px'                                                
23549                         }
23550                     },
23551                     {                            
23552                          cls: 'roo-password-meter-text'                          
23553                     }
23554                 ]            
23555         });
23556
23557          
23558         if (this.hideTrigger) {
23559             this.trigger.setDisplayed(false);
23560         }
23561         this.setSize(this.width || '', this.height || '');
23562     },
23563     // private
23564     onDestroy: function ()
23565     {
23566         if (this.trigger) {
23567             this.trigger.removeAllListeners();
23568             this.trigger.remove();
23569         }
23570         if (this.wrap) {
23571             this.wrap.remove();
23572         }
23573         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23574     },
23575     // private
23576     checkStrength: function ()
23577     {
23578         var pwd = this.inputEl().getValue();
23579         if (pwd == this._lastPwd) {
23580             return;
23581         }
23582
23583         var strength;
23584         if (this.ClientSideStrongPassword(pwd)) {
23585             strength = 3;
23586         } else if (this.ClientSideMediumPassword(pwd)) {
23587             strength = 2;
23588         } else if (this.ClientSideWeakPassword(pwd)) {
23589             strength = 1;
23590         } else {
23591             strength = 0;
23592         }
23593         
23594         Roo.log('strength1: ' + strength);
23595         
23596         //var pm = this.trigger.child('div/div/div').dom;
23597         var pm = this.trigger.child('div/div');
23598         pm.removeClass(this.meterClass);
23599         pm.addClass(this.meterClass[strength]);
23600                 
23601         
23602         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23603                 
23604         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23605         
23606         this._lastPwd = pwd;
23607     },
23608     reset: function ()
23609     {
23610         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23611         
23612         this._lastPwd = '';
23613         
23614         var pm = this.trigger.child('div/div');
23615         pm.removeClass(this.meterClass);
23616         pm.addClass('roo-password-meter-grey');        
23617         
23618         
23619         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23620         
23621         pt.innerHTML = '';
23622         this.inputEl().dom.type='password';
23623     },
23624     // private
23625     validateValue: function (value)
23626     {
23627         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23628             return false;
23629         }
23630         if (value.length == 0) {
23631             if (this.allowBlank) {
23632                 this.clearInvalid();
23633                 return true;
23634             }
23635
23636             this.markInvalid(this.errors.PwdEmpty);
23637             this.errorMsg = this.errors.PwdEmpty;
23638             return false;
23639         }
23640         
23641         if(this.insecure){
23642             return true;
23643         }
23644         
23645         if (!value.match(/[\x21-\x7e]+/)) {
23646             this.markInvalid(this.errors.PwdBadChar);
23647             this.errorMsg = this.errors.PwdBadChar;
23648             return false;
23649         }
23650         if (value.length < 6) {
23651             this.markInvalid(this.errors.PwdShort);
23652             this.errorMsg = this.errors.PwdShort;
23653             return false;
23654         }
23655         if (value.length > 16) {
23656             this.markInvalid(this.errors.PwdLong);
23657             this.errorMsg = this.errors.PwdLong;
23658             return false;
23659         }
23660         var strength;
23661         if (this.ClientSideStrongPassword(value)) {
23662             strength = 3;
23663         } else if (this.ClientSideMediumPassword(value)) {
23664             strength = 2;
23665         } else if (this.ClientSideWeakPassword(value)) {
23666             strength = 1;
23667         } else {
23668             strength = 0;
23669         }
23670
23671         
23672         if (strength < 2) {
23673             //this.markInvalid(this.errors.TooWeak);
23674             this.errorMsg = this.errors.TooWeak;
23675             //return false;
23676         }
23677         
23678         
23679         console.log('strength2: ' + strength);
23680         
23681         //var pm = this.trigger.child('div/div/div').dom;
23682         
23683         var pm = this.trigger.child('div/div');
23684         pm.removeClass(this.meterClass);
23685         pm.addClass(this.meterClass[strength]);
23686                 
23687         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23688                 
23689         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23690         
23691         this.errorMsg = ''; 
23692         return true;
23693     },
23694     // private
23695     CharacterSetChecks: function (type)
23696     {
23697         this.type = type;
23698         this.fResult = false;
23699     },
23700     // private
23701     isctype: function (character, type)
23702     {
23703         switch (type) {  
23704             case this.kCapitalLetter:
23705                 if (character >= 'A' && character <= 'Z') {
23706                     return true;
23707                 }
23708                 break;
23709             
23710             case this.kSmallLetter:
23711                 if (character >= 'a' && character <= 'z') {
23712                     return true;
23713                 }
23714                 break;
23715             
23716             case this.kDigit:
23717                 if (character >= '0' && character <= '9') {
23718                     return true;
23719                 }
23720                 break;
23721             
23722             case this.kPunctuation:
23723                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23724                     return true;
23725                 }
23726                 break;
23727             
23728             default:
23729                 return false;
23730         }
23731
23732     },
23733     // private
23734     IsLongEnough: function (pwd, size)
23735     {
23736         return !(pwd == null || isNaN(size) || pwd.length < size);
23737     },
23738     // private
23739     SpansEnoughCharacterSets: function (word, nb)
23740     {
23741         if (!this.IsLongEnough(word, nb))
23742         {
23743             return false;
23744         }
23745
23746         var characterSetChecks = new Array(
23747             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23748             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23749         );
23750         
23751         for (var index = 0; index < word.length; ++index) {
23752             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23753                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23754                     characterSetChecks[nCharSet].fResult = true;
23755                     break;
23756                 }
23757             }
23758         }
23759
23760         var nCharSets = 0;
23761         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23762             if (characterSetChecks[nCharSet].fResult) {
23763                 ++nCharSets;
23764             }
23765         }
23766
23767         if (nCharSets < nb) {
23768             return false;
23769         }
23770         return true;
23771     },
23772     // private
23773     ClientSideStrongPassword: function (pwd)
23774     {
23775         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23776     },
23777     // private
23778     ClientSideMediumPassword: function (pwd)
23779     {
23780         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23781     },
23782     // private
23783     ClientSideWeakPassword: function (pwd)
23784     {
23785         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23786     }
23787           
23788 })//<script type="text/javascript">
23789
23790 /*
23791  * Based  Ext JS Library 1.1.1
23792  * Copyright(c) 2006-2007, Ext JS, LLC.
23793  * LGPL
23794  *
23795  */
23796  
23797 /**
23798  * @class Roo.HtmlEditorCore
23799  * @extends Roo.Component
23800  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23801  *
23802  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23803  */
23804
23805 Roo.HtmlEditorCore = function(config){
23806     
23807     
23808     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23809     
23810     
23811     this.addEvents({
23812         /**
23813          * @event initialize
23814          * Fires when the editor is fully initialized (including the iframe)
23815          * @param {Roo.HtmlEditorCore} this
23816          */
23817         initialize: true,
23818         /**
23819          * @event activate
23820          * Fires when the editor is first receives the focus. Any insertion must wait
23821          * until after this event.
23822          * @param {Roo.HtmlEditorCore} this
23823          */
23824         activate: true,
23825          /**
23826          * @event beforesync
23827          * Fires before the textarea is updated with content from the editor iframe. Return false
23828          * to cancel the sync.
23829          * @param {Roo.HtmlEditorCore} this
23830          * @param {String} html
23831          */
23832         beforesync: true,
23833          /**
23834          * @event beforepush
23835          * Fires before the iframe editor is updated with content from the textarea. Return false
23836          * to cancel the push.
23837          * @param {Roo.HtmlEditorCore} this
23838          * @param {String} html
23839          */
23840         beforepush: true,
23841          /**
23842          * @event sync
23843          * Fires when the textarea is updated with content from the editor iframe.
23844          * @param {Roo.HtmlEditorCore} this
23845          * @param {String} html
23846          */
23847         sync: true,
23848          /**
23849          * @event push
23850          * Fires when the iframe editor is updated with content from the textarea.
23851          * @param {Roo.HtmlEditorCore} this
23852          * @param {String} html
23853          */
23854         push: true,
23855         
23856         /**
23857          * @event editorevent
23858          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23859          * @param {Roo.HtmlEditorCore} this
23860          */
23861         editorevent: true
23862         
23863     });
23864     
23865     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23866     
23867     // defaults : white / black...
23868     this.applyBlacklists();
23869     
23870     
23871     
23872 };
23873
23874
23875 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23876
23877
23878      /**
23879      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23880      */
23881     
23882     owner : false,
23883     
23884      /**
23885      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23886      *                        Roo.resizable.
23887      */
23888     resizable : false,
23889      /**
23890      * @cfg {Number} height (in pixels)
23891      */   
23892     height: 300,
23893    /**
23894      * @cfg {Number} width (in pixels)
23895      */   
23896     width: 500,
23897     
23898     /**
23899      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23900      * 
23901      */
23902     stylesheets: false,
23903     
23904     // id of frame..
23905     frameId: false,
23906     
23907     // private properties
23908     validationEvent : false,
23909     deferHeight: true,
23910     initialized : false,
23911     activated : false,
23912     sourceEditMode : false,
23913     onFocus : Roo.emptyFn,
23914     iframePad:3,
23915     hideMode:'offsets',
23916     
23917     clearUp: true,
23918     
23919     // blacklist + whitelisted elements..
23920     black: false,
23921     white: false,
23922      
23923     bodyCls : '',
23924
23925     /**
23926      * Protected method that will not generally be called directly. It
23927      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23928      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23929      */
23930     getDocMarkup : function(){
23931         // body styles..
23932         var st = '';
23933         
23934         // inherit styels from page...?? 
23935         if (this.stylesheets === false) {
23936             
23937             Roo.get(document.head).select('style').each(function(node) {
23938                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23939             });
23940             
23941             Roo.get(document.head).select('link').each(function(node) { 
23942                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23943             });
23944             
23945         } else if (!this.stylesheets.length) {
23946                 // simple..
23947                 st = '<style type="text/css">' +
23948                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23949                    '</style>';
23950         } else {
23951             for (var i in this.stylesheets) { 
23952                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23953             }
23954             
23955         }
23956         
23957         st +=  '<style type="text/css">' +
23958             'IMG { cursor: pointer } ' +
23959         '</style>';
23960
23961         var cls = 'roo-htmleditor-body';
23962         
23963         if(this.bodyCls.length){
23964             cls += ' ' + this.bodyCls;
23965         }
23966         
23967         return '<html><head>' + st  +
23968             //<style type="text/css">' +
23969             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23970             //'</style>' +
23971             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23972     },
23973
23974     // private
23975     onRender : function(ct, position)
23976     {
23977         var _t = this;
23978         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23979         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23980         
23981         
23982         this.el.dom.style.border = '0 none';
23983         this.el.dom.setAttribute('tabIndex', -1);
23984         this.el.addClass('x-hidden hide');
23985         
23986         
23987         
23988         if(Roo.isIE){ // fix IE 1px bogus margin
23989             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23990         }
23991        
23992         
23993         this.frameId = Roo.id();
23994         
23995          
23996         
23997         var iframe = this.owner.wrap.createChild({
23998             tag: 'iframe',
23999             cls: 'form-control', // bootstrap..
24000             id: this.frameId,
24001             name: this.frameId,
24002             frameBorder : 'no',
24003             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24004         }, this.el
24005         );
24006         
24007         
24008         this.iframe = iframe.dom;
24009
24010          this.assignDocWin();
24011         
24012         this.doc.designMode = 'on';
24013        
24014         this.doc.open();
24015         this.doc.write(this.getDocMarkup());
24016         this.doc.close();
24017
24018         
24019         var task = { // must defer to wait for browser to be ready
24020             run : function(){
24021                 //console.log("run task?" + this.doc.readyState);
24022                 this.assignDocWin();
24023                 if(this.doc.body || this.doc.readyState == 'complete'){
24024                     try {
24025                         this.doc.designMode="on";
24026                     } catch (e) {
24027                         return;
24028                     }
24029                     Roo.TaskMgr.stop(task);
24030                     this.initEditor.defer(10, this);
24031                 }
24032             },
24033             interval : 10,
24034             duration: 10000,
24035             scope: this
24036         };
24037         Roo.TaskMgr.start(task);
24038
24039     },
24040
24041     // private
24042     onResize : function(w, h)
24043     {
24044          Roo.log('resize: ' +w + ',' + h );
24045         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24046         if(!this.iframe){
24047             return;
24048         }
24049         if(typeof w == 'number'){
24050             
24051             this.iframe.style.width = w + 'px';
24052         }
24053         if(typeof h == 'number'){
24054             
24055             this.iframe.style.height = h + 'px';
24056             if(this.doc){
24057                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24058             }
24059         }
24060         
24061     },
24062
24063     /**
24064      * Toggles the editor between standard and source edit mode.
24065      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24066      */
24067     toggleSourceEdit : function(sourceEditMode){
24068         
24069         this.sourceEditMode = sourceEditMode === true;
24070         
24071         if(this.sourceEditMode){
24072  
24073             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24074             
24075         }else{
24076             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24077             //this.iframe.className = '';
24078             this.deferFocus();
24079         }
24080         //this.setSize(this.owner.wrap.getSize());
24081         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24082     },
24083
24084     
24085   
24086
24087     /**
24088      * Protected method that will not generally be called directly. If you need/want
24089      * custom HTML cleanup, this is the method you should override.
24090      * @param {String} html The HTML to be cleaned
24091      * return {String} The cleaned HTML
24092      */
24093     cleanHtml : function(html){
24094         html = String(html);
24095         if(html.length > 5){
24096             if(Roo.isSafari){ // strip safari nonsense
24097                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24098             }
24099         }
24100         if(html == '&nbsp;'){
24101             html = '';
24102         }
24103         return html;
24104     },
24105
24106     /**
24107      * HTML Editor -> Textarea
24108      * Protected method that will not generally be called directly. Syncs the contents
24109      * of the editor iframe with the textarea.
24110      */
24111     syncValue : function(){
24112         if(this.initialized){
24113             var bd = (this.doc.body || this.doc.documentElement);
24114             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24115             var html = bd.innerHTML;
24116             if(Roo.isSafari){
24117                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24118                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24119                 if(m && m[1]){
24120                     html = '<div style="'+m[0]+'">' + html + '</div>';
24121                 }
24122             }
24123             html = this.cleanHtml(html);
24124             // fix up the special chars.. normaly like back quotes in word...
24125             // however we do not want to do this with chinese..
24126             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24127                 
24128                 var cc = match.charCodeAt();
24129
24130                 // Get the character value, handling surrogate pairs
24131                 if (match.length == 2) {
24132                     // It's a surrogate pair, calculate the Unicode code point
24133                     var high = match.charCodeAt(0) - 0xD800;
24134                     var low  = match.charCodeAt(1) - 0xDC00;
24135                     cc = (high * 0x400) + low + 0x10000;
24136                 }  else if (
24137                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24138                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24139                     (cc >= 0xf900 && cc < 0xfb00 )
24140                 ) {
24141                         return match;
24142                 }  
24143          
24144                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24145                 return "&#" + cc + ";";
24146                 
24147                 
24148             });
24149             
24150             
24151              
24152             if(this.owner.fireEvent('beforesync', this, html) !== false){
24153                 this.el.dom.value = html;
24154                 this.owner.fireEvent('sync', this, html);
24155             }
24156         }
24157     },
24158
24159     /**
24160      * Protected method that will not generally be called directly. Pushes the value of the textarea
24161      * into the iframe editor.
24162      */
24163     pushValue : function(){
24164         if(this.initialized){
24165             var v = this.el.dom.value.trim();
24166             
24167 //            if(v.length < 1){
24168 //                v = '&#160;';
24169 //            }
24170             
24171             if(this.owner.fireEvent('beforepush', this, v) !== false){
24172                 var d = (this.doc.body || this.doc.documentElement);
24173                 d.innerHTML = v;
24174                 this.cleanUpPaste();
24175                 this.el.dom.value = d.innerHTML;
24176                 this.owner.fireEvent('push', this, v);
24177             }
24178         }
24179     },
24180
24181     // private
24182     deferFocus : function(){
24183         this.focus.defer(10, this);
24184     },
24185
24186     // doc'ed in Field
24187     focus : function(){
24188         if(this.win && !this.sourceEditMode){
24189             this.win.focus();
24190         }else{
24191             this.el.focus();
24192         }
24193     },
24194     
24195     assignDocWin: function()
24196     {
24197         var iframe = this.iframe;
24198         
24199          if(Roo.isIE){
24200             this.doc = iframe.contentWindow.document;
24201             this.win = iframe.contentWindow;
24202         } else {
24203 //            if (!Roo.get(this.frameId)) {
24204 //                return;
24205 //            }
24206 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24207 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24208             
24209             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24210                 return;
24211             }
24212             
24213             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24214             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24215         }
24216     },
24217     
24218     // private
24219     initEditor : function(){
24220         //console.log("INIT EDITOR");
24221         this.assignDocWin();
24222         
24223         
24224         
24225         this.doc.designMode="on";
24226         this.doc.open();
24227         this.doc.write(this.getDocMarkup());
24228         this.doc.close();
24229         
24230         var dbody = (this.doc.body || this.doc.documentElement);
24231         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24232         // this copies styles from the containing element into thsi one..
24233         // not sure why we need all of this..
24234         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24235         
24236         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24237         //ss['background-attachment'] = 'fixed'; // w3c
24238         dbody.bgProperties = 'fixed'; // ie
24239         //Roo.DomHelper.applyStyles(dbody, ss);
24240         Roo.EventManager.on(this.doc, {
24241             //'mousedown': this.onEditorEvent,
24242             'mouseup': this.onEditorEvent,
24243             'dblclick': this.onEditorEvent,
24244             'click': this.onEditorEvent,
24245             'keyup': this.onEditorEvent,
24246             buffer:100,
24247             scope: this
24248         });
24249         if(Roo.isGecko){
24250             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24251         }
24252         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24253             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24254         }
24255         this.initialized = true;
24256
24257         this.owner.fireEvent('initialize', this);
24258         this.pushValue();
24259     },
24260
24261     // private
24262     onDestroy : function(){
24263         
24264         
24265         
24266         if(this.rendered){
24267             
24268             //for (var i =0; i < this.toolbars.length;i++) {
24269             //    // fixme - ask toolbars for heights?
24270             //    this.toolbars[i].onDestroy();
24271            // }
24272             
24273             //this.wrap.dom.innerHTML = '';
24274             //this.wrap.remove();
24275         }
24276     },
24277
24278     // private
24279     onFirstFocus : function(){
24280         
24281         this.assignDocWin();
24282         
24283         
24284         this.activated = true;
24285          
24286     
24287         if(Roo.isGecko){ // prevent silly gecko errors
24288             this.win.focus();
24289             var s = this.win.getSelection();
24290             if(!s.focusNode || s.focusNode.nodeType != 3){
24291                 var r = s.getRangeAt(0);
24292                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24293                 r.collapse(true);
24294                 this.deferFocus();
24295             }
24296             try{
24297                 this.execCmd('useCSS', true);
24298                 this.execCmd('styleWithCSS', false);
24299             }catch(e){}
24300         }
24301         this.owner.fireEvent('activate', this);
24302     },
24303
24304     // private
24305     adjustFont: function(btn){
24306         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24307         //if(Roo.isSafari){ // safari
24308         //    adjust *= 2;
24309        // }
24310         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24311         if(Roo.isSafari){ // safari
24312             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24313             v =  (v < 10) ? 10 : v;
24314             v =  (v > 48) ? 48 : v;
24315             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24316             
24317         }
24318         
24319         
24320         v = Math.max(1, v+adjust);
24321         
24322         this.execCmd('FontSize', v  );
24323     },
24324
24325     onEditorEvent : function(e)
24326     {
24327         this.owner.fireEvent('editorevent', this, e);
24328       //  this.updateToolbar();
24329         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24330     },
24331
24332     insertTag : function(tg)
24333     {
24334         // could be a bit smarter... -> wrap the current selected tRoo..
24335         if (tg.toLowerCase() == 'span' ||
24336             tg.toLowerCase() == 'code' ||
24337             tg.toLowerCase() == 'sup' ||
24338             tg.toLowerCase() == 'sub' 
24339             ) {
24340             
24341             range = this.createRange(this.getSelection());
24342             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24343             wrappingNode.appendChild(range.extractContents());
24344             range.insertNode(wrappingNode);
24345
24346             return;
24347             
24348             
24349             
24350         }
24351         this.execCmd("formatblock",   tg);
24352         
24353     },
24354     
24355     insertText : function(txt)
24356     {
24357         
24358         
24359         var range = this.createRange();
24360         range.deleteContents();
24361                //alert(Sender.getAttribute('label'));
24362                
24363         range.insertNode(this.doc.createTextNode(txt));
24364     } ,
24365     
24366      
24367
24368     /**
24369      * Executes a Midas editor command on the editor document and performs necessary focus and
24370      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24371      * @param {String} cmd The Midas command
24372      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24373      */
24374     relayCmd : function(cmd, value){
24375         this.win.focus();
24376         this.execCmd(cmd, value);
24377         this.owner.fireEvent('editorevent', this);
24378         //this.updateToolbar();
24379         this.owner.deferFocus();
24380     },
24381
24382     /**
24383      * Executes a Midas editor command directly on the editor document.
24384      * For visual commands, you should use {@link #relayCmd} instead.
24385      * <b>This should only be called after the editor is initialized.</b>
24386      * @param {String} cmd The Midas command
24387      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24388      */
24389     execCmd : function(cmd, value){
24390         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24391         this.syncValue();
24392     },
24393  
24394  
24395    
24396     /**
24397      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24398      * to insert tRoo.
24399      * @param {String} text | dom node.. 
24400      */
24401     insertAtCursor : function(text)
24402     {
24403         
24404         if(!this.activated){
24405             return;
24406         }
24407         /*
24408         if(Roo.isIE){
24409             this.win.focus();
24410             var r = this.doc.selection.createRange();
24411             if(r){
24412                 r.collapse(true);
24413                 r.pasteHTML(text);
24414                 this.syncValue();
24415                 this.deferFocus();
24416             
24417             }
24418             return;
24419         }
24420         */
24421         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24422             this.win.focus();
24423             
24424             
24425             // from jquery ui (MIT licenced)
24426             var range, node;
24427             var win = this.win;
24428             
24429             if (win.getSelection && win.getSelection().getRangeAt) {
24430                 range = win.getSelection().getRangeAt(0);
24431                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24432                 range.insertNode(node);
24433             } else if (win.document.selection && win.document.selection.createRange) {
24434                 // no firefox support
24435                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24436                 win.document.selection.createRange().pasteHTML(txt);
24437             } else {
24438                 // no firefox support
24439                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24440                 this.execCmd('InsertHTML', txt);
24441             } 
24442             
24443             this.syncValue();
24444             
24445             this.deferFocus();
24446         }
24447     },
24448  // private
24449     mozKeyPress : function(e){
24450         if(e.ctrlKey){
24451             var c = e.getCharCode(), cmd;
24452           
24453             if(c > 0){
24454                 c = String.fromCharCode(c).toLowerCase();
24455                 switch(c){
24456                     case 'b':
24457                         cmd = 'bold';
24458                         break;
24459                     case 'i':
24460                         cmd = 'italic';
24461                         break;
24462                     
24463                     case 'u':
24464                         cmd = 'underline';
24465                         break;
24466                     
24467                     case 'v':
24468                         this.cleanUpPaste.defer(100, this);
24469                         return;
24470                         
24471                 }
24472                 if(cmd){
24473                     this.win.focus();
24474                     this.execCmd(cmd);
24475                     this.deferFocus();
24476                     e.preventDefault();
24477                 }
24478                 
24479             }
24480         }
24481     },
24482
24483     // private
24484     fixKeys : function(){ // load time branching for fastest keydown performance
24485         if(Roo.isIE){
24486             return function(e){
24487                 var k = e.getKey(), r;
24488                 if(k == e.TAB){
24489                     e.stopEvent();
24490                     r = this.doc.selection.createRange();
24491                     if(r){
24492                         r.collapse(true);
24493                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24494                         this.deferFocus();
24495                     }
24496                     return;
24497                 }
24498                 
24499                 if(k == e.ENTER){
24500                     r = this.doc.selection.createRange();
24501                     if(r){
24502                         var target = r.parentElement();
24503                         if(!target || target.tagName.toLowerCase() != 'li'){
24504                             e.stopEvent();
24505                             r.pasteHTML('<br />');
24506                             r.collapse(false);
24507                             r.select();
24508                         }
24509                     }
24510                 }
24511                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24512                     this.cleanUpPaste.defer(100, this);
24513                     return;
24514                 }
24515                 
24516                 
24517             };
24518         }else if(Roo.isOpera){
24519             return function(e){
24520                 var k = e.getKey();
24521                 if(k == e.TAB){
24522                     e.stopEvent();
24523                     this.win.focus();
24524                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24525                     this.deferFocus();
24526                 }
24527                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24528                     this.cleanUpPaste.defer(100, this);
24529                     return;
24530                 }
24531                 
24532             };
24533         }else if(Roo.isSafari){
24534             return function(e){
24535                 var k = e.getKey();
24536                 
24537                 if(k == e.TAB){
24538                     e.stopEvent();
24539                     this.execCmd('InsertText','\t');
24540                     this.deferFocus();
24541                     return;
24542                 }
24543                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24544                     this.cleanUpPaste.defer(100, this);
24545                     return;
24546                 }
24547                 
24548              };
24549         }
24550     }(),
24551     
24552     getAllAncestors: function()
24553     {
24554         var p = this.getSelectedNode();
24555         var a = [];
24556         if (!p) {
24557             a.push(p); // push blank onto stack..
24558             p = this.getParentElement();
24559         }
24560         
24561         
24562         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24563             a.push(p);
24564             p = p.parentNode;
24565         }
24566         a.push(this.doc.body);
24567         return a;
24568     },
24569     lastSel : false,
24570     lastSelNode : false,
24571     
24572     
24573     getSelection : function() 
24574     {
24575         this.assignDocWin();
24576         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24577     },
24578     
24579     getSelectedNode: function() 
24580     {
24581         // this may only work on Gecko!!!
24582         
24583         // should we cache this!!!!
24584         
24585         
24586         
24587          
24588         var range = this.createRange(this.getSelection()).cloneRange();
24589         
24590         if (Roo.isIE) {
24591             var parent = range.parentElement();
24592             while (true) {
24593                 var testRange = range.duplicate();
24594                 testRange.moveToElementText(parent);
24595                 if (testRange.inRange(range)) {
24596                     break;
24597                 }
24598                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24599                     break;
24600                 }
24601                 parent = parent.parentElement;
24602             }
24603             return parent;
24604         }
24605         
24606         // is ancestor a text element.
24607         var ac =  range.commonAncestorContainer;
24608         if (ac.nodeType == 3) {
24609             ac = ac.parentNode;
24610         }
24611         
24612         var ar = ac.childNodes;
24613          
24614         var nodes = [];
24615         var other_nodes = [];
24616         var has_other_nodes = false;
24617         for (var i=0;i<ar.length;i++) {
24618             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24619                 continue;
24620             }
24621             // fullly contained node.
24622             
24623             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24624                 nodes.push(ar[i]);
24625                 continue;
24626             }
24627             
24628             // probably selected..
24629             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24630                 other_nodes.push(ar[i]);
24631                 continue;
24632             }
24633             // outer..
24634             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24635                 continue;
24636             }
24637             
24638             
24639             has_other_nodes = true;
24640         }
24641         if (!nodes.length && other_nodes.length) {
24642             nodes= other_nodes;
24643         }
24644         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24645             return false;
24646         }
24647         
24648         return nodes[0];
24649     },
24650     createRange: function(sel)
24651     {
24652         // this has strange effects when using with 
24653         // top toolbar - not sure if it's a great idea.
24654         //this.editor.contentWindow.focus();
24655         if (typeof sel != "undefined") {
24656             try {
24657                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24658             } catch(e) {
24659                 return this.doc.createRange();
24660             }
24661         } else {
24662             return this.doc.createRange();
24663         }
24664     },
24665     getParentElement: function()
24666     {
24667         
24668         this.assignDocWin();
24669         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24670         
24671         var range = this.createRange(sel);
24672          
24673         try {
24674             var p = range.commonAncestorContainer;
24675             while (p.nodeType == 3) { // text node
24676                 p = p.parentNode;
24677             }
24678             return p;
24679         } catch (e) {
24680             return null;
24681         }
24682     
24683     },
24684     /***
24685      *
24686      * Range intersection.. the hard stuff...
24687      *  '-1' = before
24688      *  '0' = hits..
24689      *  '1' = after.
24690      *         [ -- selected range --- ]
24691      *   [fail]                        [fail]
24692      *
24693      *    basically..
24694      *      if end is before start or  hits it. fail.
24695      *      if start is after end or hits it fail.
24696      *
24697      *   if either hits (but other is outside. - then it's not 
24698      *   
24699      *    
24700      **/
24701     
24702     
24703     // @see http://www.thismuchiknow.co.uk/?p=64.
24704     rangeIntersectsNode : function(range, node)
24705     {
24706         var nodeRange = node.ownerDocument.createRange();
24707         try {
24708             nodeRange.selectNode(node);
24709         } catch (e) {
24710             nodeRange.selectNodeContents(node);
24711         }
24712     
24713         var rangeStartRange = range.cloneRange();
24714         rangeStartRange.collapse(true);
24715     
24716         var rangeEndRange = range.cloneRange();
24717         rangeEndRange.collapse(false);
24718     
24719         var nodeStartRange = nodeRange.cloneRange();
24720         nodeStartRange.collapse(true);
24721     
24722         var nodeEndRange = nodeRange.cloneRange();
24723         nodeEndRange.collapse(false);
24724     
24725         return rangeStartRange.compareBoundaryPoints(
24726                  Range.START_TO_START, nodeEndRange) == -1 &&
24727                rangeEndRange.compareBoundaryPoints(
24728                  Range.START_TO_START, nodeStartRange) == 1;
24729         
24730          
24731     },
24732     rangeCompareNode : function(range, node)
24733     {
24734         var nodeRange = node.ownerDocument.createRange();
24735         try {
24736             nodeRange.selectNode(node);
24737         } catch (e) {
24738             nodeRange.selectNodeContents(node);
24739         }
24740         
24741         
24742         range.collapse(true);
24743     
24744         nodeRange.collapse(true);
24745      
24746         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24747         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24748          
24749         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24750         
24751         var nodeIsBefore   =  ss == 1;
24752         var nodeIsAfter    = ee == -1;
24753         
24754         if (nodeIsBefore && nodeIsAfter) {
24755             return 0; // outer
24756         }
24757         if (!nodeIsBefore && nodeIsAfter) {
24758             return 1; //right trailed.
24759         }
24760         
24761         if (nodeIsBefore && !nodeIsAfter) {
24762             return 2;  // left trailed.
24763         }
24764         // fully contined.
24765         return 3;
24766     },
24767
24768     // private? - in a new class?
24769     cleanUpPaste :  function()
24770     {
24771         // cleans up the whole document..
24772         Roo.log('cleanuppaste');
24773         
24774         this.cleanUpChildren(this.doc.body);
24775         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24776         if (clean != this.doc.body.innerHTML) {
24777             this.doc.body.innerHTML = clean;
24778         }
24779         
24780     },
24781     
24782     cleanWordChars : function(input) {// change the chars to hex code
24783         var he = Roo.HtmlEditorCore;
24784         
24785         var output = input;
24786         Roo.each(he.swapCodes, function(sw) { 
24787             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24788             
24789             output = output.replace(swapper, sw[1]);
24790         });
24791         
24792         return output;
24793     },
24794     
24795     
24796     cleanUpChildren : function (n)
24797     {
24798         if (!n.childNodes.length) {
24799             return;
24800         }
24801         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24802            this.cleanUpChild(n.childNodes[i]);
24803         }
24804     },
24805     
24806     
24807         
24808     
24809     cleanUpChild : function (node)
24810     {
24811         var ed = this;
24812         //console.log(node);
24813         if (node.nodeName == "#text") {
24814             // clean up silly Windows -- stuff?
24815             return; 
24816         }
24817         if (node.nodeName == "#comment") {
24818             node.parentNode.removeChild(node);
24819             // clean up silly Windows -- stuff?
24820             return; 
24821         }
24822         var lcname = node.tagName.toLowerCase();
24823         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24824         // whitelist of tags..
24825         
24826         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24827             // remove node.
24828             node.parentNode.removeChild(node);
24829             return;
24830             
24831         }
24832         
24833         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24834         
24835         // spans with no attributes - just remove them..
24836         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24837             remove_keep_children = true;
24838         }
24839         
24840         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24841         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24842         
24843         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24844         //    remove_keep_children = true;
24845         //}
24846         
24847         if (remove_keep_children) {
24848             this.cleanUpChildren(node);
24849             // inserts everything just before this node...
24850             while (node.childNodes.length) {
24851                 var cn = node.childNodes[0];
24852                 node.removeChild(cn);
24853                 node.parentNode.insertBefore(cn, node);
24854             }
24855             node.parentNode.removeChild(node);
24856             return;
24857         }
24858         
24859         if (!node.attributes || !node.attributes.length) {
24860             
24861           
24862             
24863             
24864             this.cleanUpChildren(node);
24865             return;
24866         }
24867         
24868         function cleanAttr(n,v)
24869         {
24870             
24871             if (v.match(/^\./) || v.match(/^\//)) {
24872                 return;
24873             }
24874             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24875                 return;
24876             }
24877             if (v.match(/^#/)) {
24878                 return;
24879             }
24880             if (v.match(/^\{/)) { // allow template editing.
24881                 return;
24882             }
24883 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24884             node.removeAttribute(n);
24885             
24886         }
24887         
24888         var cwhite = this.cwhite;
24889         var cblack = this.cblack;
24890             
24891         function cleanStyle(n,v)
24892         {
24893             if (v.match(/expression/)) { //XSS?? should we even bother..
24894                 node.removeAttribute(n);
24895                 return;
24896             }
24897             
24898             var parts = v.split(/;/);
24899             var clean = [];
24900             
24901             Roo.each(parts, function(p) {
24902                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24903                 if (!p.length) {
24904                     return true;
24905                 }
24906                 var l = p.split(':').shift().replace(/\s+/g,'');
24907                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24908                 
24909                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24910 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24911                     //node.removeAttribute(n);
24912                     return true;
24913                 }
24914                 //Roo.log()
24915                 // only allow 'c whitelisted system attributes'
24916                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24917 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24918                     //node.removeAttribute(n);
24919                     return true;
24920                 }
24921                 
24922                 
24923                  
24924                 
24925                 clean.push(p);
24926                 return true;
24927             });
24928             if (clean.length) { 
24929                 node.setAttribute(n, clean.join(';'));
24930             } else {
24931                 node.removeAttribute(n);
24932             }
24933             
24934         }
24935         
24936         
24937         for (var i = node.attributes.length-1; i > -1 ; i--) {
24938             var a = node.attributes[i];
24939             //console.log(a);
24940             
24941             if (a.name.toLowerCase().substr(0,2)=='on')  {
24942                 node.removeAttribute(a.name);
24943                 continue;
24944             }
24945             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24946                 node.removeAttribute(a.name);
24947                 continue;
24948             }
24949             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24950                 cleanAttr(a.name,a.value); // fixme..
24951                 continue;
24952             }
24953             if (a.name == 'style') {
24954                 cleanStyle(a.name,a.value);
24955                 continue;
24956             }
24957             /// clean up MS crap..
24958             // tecnically this should be a list of valid class'es..
24959             
24960             
24961             if (a.name == 'class') {
24962                 if (a.value.match(/^Mso/)) {
24963                     node.removeAttribute('class');
24964                 }
24965                 
24966                 if (a.value.match(/^body$/)) {
24967                     node.removeAttribute('class');
24968                 }
24969                 continue;
24970             }
24971             
24972             // style cleanup!?
24973             // class cleanup?
24974             
24975         }
24976         
24977         
24978         this.cleanUpChildren(node);
24979         
24980         
24981     },
24982     
24983     /**
24984      * Clean up MS wordisms...
24985      */
24986     cleanWord : function(node)
24987     {
24988         if (!node) {
24989             this.cleanWord(this.doc.body);
24990             return;
24991         }
24992         
24993         if(
24994                 node.nodeName == 'SPAN' &&
24995                 !node.hasAttributes() &&
24996                 node.childNodes.length == 1 &&
24997                 node.firstChild.nodeName == "#text"  
24998         ) {
24999             var textNode = node.firstChild;
25000             node.removeChild(textNode);
25001             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25002                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25003             }
25004             node.parentNode.insertBefore(textNode, node);
25005             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25006                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25007             }
25008             node.parentNode.removeChild(node);
25009         }
25010         
25011         if (node.nodeName == "#text") {
25012             // clean up silly Windows -- stuff?
25013             return; 
25014         }
25015         if (node.nodeName == "#comment") {
25016             node.parentNode.removeChild(node);
25017             // clean up silly Windows -- stuff?
25018             return; 
25019         }
25020         
25021         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25022             node.parentNode.removeChild(node);
25023             return;
25024         }
25025         //Roo.log(node.tagName);
25026         // remove - but keep children..
25027         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25028             //Roo.log('-- removed');
25029             while (node.childNodes.length) {
25030                 var cn = node.childNodes[0];
25031                 node.removeChild(cn);
25032                 node.parentNode.insertBefore(cn, node);
25033                 // move node to parent - and clean it..
25034                 this.cleanWord(cn);
25035             }
25036             node.parentNode.removeChild(node);
25037             /// no need to iterate chidlren = it's got none..
25038             //this.iterateChildren(node, this.cleanWord);
25039             return;
25040         }
25041         // clean styles
25042         if (node.className.length) {
25043             
25044             var cn = node.className.split(/\W+/);
25045             var cna = [];
25046             Roo.each(cn, function(cls) {
25047                 if (cls.match(/Mso[a-zA-Z]+/)) {
25048                     return;
25049                 }
25050                 cna.push(cls);
25051             });
25052             node.className = cna.length ? cna.join(' ') : '';
25053             if (!cna.length) {
25054                 node.removeAttribute("class");
25055             }
25056         }
25057         
25058         if (node.hasAttribute("lang")) {
25059             node.removeAttribute("lang");
25060         }
25061         
25062         if (node.hasAttribute("style")) {
25063             
25064             var styles = node.getAttribute("style").split(";");
25065             var nstyle = [];
25066             Roo.each(styles, function(s) {
25067                 if (!s.match(/:/)) {
25068                     return;
25069                 }
25070                 var kv = s.split(":");
25071                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25072                     return;
25073                 }
25074                 // what ever is left... we allow.
25075                 nstyle.push(s);
25076             });
25077             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25078             if (!nstyle.length) {
25079                 node.removeAttribute('style');
25080             }
25081         }
25082         this.iterateChildren(node, this.cleanWord);
25083         
25084         
25085         
25086     },
25087     /**
25088      * iterateChildren of a Node, calling fn each time, using this as the scole..
25089      * @param {DomNode} node node to iterate children of.
25090      * @param {Function} fn method of this class to call on each item.
25091      */
25092     iterateChildren : function(node, fn)
25093     {
25094         if (!node.childNodes.length) {
25095                 return;
25096         }
25097         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25098            fn.call(this, node.childNodes[i])
25099         }
25100     },
25101     
25102     
25103     /**
25104      * cleanTableWidths.
25105      *
25106      * Quite often pasting from word etc.. results in tables with column and widths.
25107      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25108      *
25109      */
25110     cleanTableWidths : function(node)
25111     {
25112          
25113          
25114         if (!node) {
25115             this.cleanTableWidths(this.doc.body);
25116             return;
25117         }
25118         
25119         // ignore list...
25120         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25121             return; 
25122         }
25123         Roo.log(node.tagName);
25124         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25125             this.iterateChildren(node, this.cleanTableWidths);
25126             return;
25127         }
25128         if (node.hasAttribute('width')) {
25129             node.removeAttribute('width');
25130         }
25131         
25132          
25133         if (node.hasAttribute("style")) {
25134             // pretty basic...
25135             
25136             var styles = node.getAttribute("style").split(";");
25137             var nstyle = [];
25138             Roo.each(styles, function(s) {
25139                 if (!s.match(/:/)) {
25140                     return;
25141                 }
25142                 var kv = s.split(":");
25143                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25144                     return;
25145                 }
25146                 // what ever is left... we allow.
25147                 nstyle.push(s);
25148             });
25149             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25150             if (!nstyle.length) {
25151                 node.removeAttribute('style');
25152             }
25153         }
25154         
25155         this.iterateChildren(node, this.cleanTableWidths);
25156         
25157         
25158     },
25159     
25160     
25161     
25162     
25163     domToHTML : function(currentElement, depth, nopadtext) {
25164         
25165         depth = depth || 0;
25166         nopadtext = nopadtext || false;
25167     
25168         if (!currentElement) {
25169             return this.domToHTML(this.doc.body);
25170         }
25171         
25172         //Roo.log(currentElement);
25173         var j;
25174         var allText = false;
25175         var nodeName = currentElement.nodeName;
25176         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25177         
25178         if  (nodeName == '#text') {
25179             
25180             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25181         }
25182         
25183         
25184         var ret = '';
25185         if (nodeName != 'BODY') {
25186              
25187             var i = 0;
25188             // Prints the node tagName, such as <A>, <IMG>, etc
25189             if (tagName) {
25190                 var attr = [];
25191                 for(i = 0; i < currentElement.attributes.length;i++) {
25192                     // quoting?
25193                     var aname = currentElement.attributes.item(i).name;
25194                     if (!currentElement.attributes.item(i).value.length) {
25195                         continue;
25196                     }
25197                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25198                 }
25199                 
25200                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25201             } 
25202             else {
25203                 
25204                 // eack
25205             }
25206         } else {
25207             tagName = false;
25208         }
25209         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25210             return ret;
25211         }
25212         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25213             nopadtext = true;
25214         }
25215         
25216         
25217         // Traverse the tree
25218         i = 0;
25219         var currentElementChild = currentElement.childNodes.item(i);
25220         var allText = true;
25221         var innerHTML  = '';
25222         lastnode = '';
25223         while (currentElementChild) {
25224             // Formatting code (indent the tree so it looks nice on the screen)
25225             var nopad = nopadtext;
25226             if (lastnode == 'SPAN') {
25227                 nopad  = true;
25228             }
25229             // text
25230             if  (currentElementChild.nodeName == '#text') {
25231                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25232                 toadd = nopadtext ? toadd : toadd.trim();
25233                 if (!nopad && toadd.length > 80) {
25234                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25235                 }
25236                 innerHTML  += toadd;
25237                 
25238                 i++;
25239                 currentElementChild = currentElement.childNodes.item(i);
25240                 lastNode = '';
25241                 continue;
25242             }
25243             allText = false;
25244             
25245             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25246                 
25247             // Recursively traverse the tree structure of the child node
25248             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25249             lastnode = currentElementChild.nodeName;
25250             i++;
25251             currentElementChild=currentElement.childNodes.item(i);
25252         }
25253         
25254         ret += innerHTML;
25255         
25256         if (!allText) {
25257                 // The remaining code is mostly for formatting the tree
25258             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25259         }
25260         
25261         
25262         if (tagName) {
25263             ret+= "</"+tagName+">";
25264         }
25265         return ret;
25266         
25267     },
25268         
25269     applyBlacklists : function()
25270     {
25271         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25272         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25273         
25274         this.white = [];
25275         this.black = [];
25276         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25277             if (b.indexOf(tag) > -1) {
25278                 return;
25279             }
25280             this.white.push(tag);
25281             
25282         }, this);
25283         
25284         Roo.each(w, function(tag) {
25285             if (b.indexOf(tag) > -1) {
25286                 return;
25287             }
25288             if (this.white.indexOf(tag) > -1) {
25289                 return;
25290             }
25291             this.white.push(tag);
25292             
25293         }, this);
25294         
25295         
25296         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25297             if (w.indexOf(tag) > -1) {
25298                 return;
25299             }
25300             this.black.push(tag);
25301             
25302         }, this);
25303         
25304         Roo.each(b, function(tag) {
25305             if (w.indexOf(tag) > -1) {
25306                 return;
25307             }
25308             if (this.black.indexOf(tag) > -1) {
25309                 return;
25310             }
25311             this.black.push(tag);
25312             
25313         }, this);
25314         
25315         
25316         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25317         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25318         
25319         this.cwhite = [];
25320         this.cblack = [];
25321         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25322             if (b.indexOf(tag) > -1) {
25323                 return;
25324             }
25325             this.cwhite.push(tag);
25326             
25327         }, this);
25328         
25329         Roo.each(w, function(tag) {
25330             if (b.indexOf(tag) > -1) {
25331                 return;
25332             }
25333             if (this.cwhite.indexOf(tag) > -1) {
25334                 return;
25335             }
25336             this.cwhite.push(tag);
25337             
25338         }, this);
25339         
25340         
25341         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25342             if (w.indexOf(tag) > -1) {
25343                 return;
25344             }
25345             this.cblack.push(tag);
25346             
25347         }, this);
25348         
25349         Roo.each(b, function(tag) {
25350             if (w.indexOf(tag) > -1) {
25351                 return;
25352             }
25353             if (this.cblack.indexOf(tag) > -1) {
25354                 return;
25355             }
25356             this.cblack.push(tag);
25357             
25358         }, this);
25359     },
25360     
25361     setStylesheets : function(stylesheets)
25362     {
25363         if(typeof(stylesheets) == 'string'){
25364             Roo.get(this.iframe.contentDocument.head).createChild({
25365                 tag : 'link',
25366                 rel : 'stylesheet',
25367                 type : 'text/css',
25368                 href : stylesheets
25369             });
25370             
25371             return;
25372         }
25373         var _this = this;
25374      
25375         Roo.each(stylesheets, function(s) {
25376             if(!s.length){
25377                 return;
25378             }
25379             
25380             Roo.get(_this.iframe.contentDocument.head).createChild({
25381                 tag : 'link',
25382                 rel : 'stylesheet',
25383                 type : 'text/css',
25384                 href : s
25385             });
25386         });
25387
25388         
25389     },
25390     
25391     removeStylesheets : function()
25392     {
25393         var _this = this;
25394         
25395         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25396             s.remove();
25397         });
25398     },
25399     
25400     setStyle : function(style)
25401     {
25402         Roo.get(this.iframe.contentDocument.head).createChild({
25403             tag : 'style',
25404             type : 'text/css',
25405             html : style
25406         });
25407
25408         return;
25409     }
25410     
25411     // hide stuff that is not compatible
25412     /**
25413      * @event blur
25414      * @hide
25415      */
25416     /**
25417      * @event change
25418      * @hide
25419      */
25420     /**
25421      * @event focus
25422      * @hide
25423      */
25424     /**
25425      * @event specialkey
25426      * @hide
25427      */
25428     /**
25429      * @cfg {String} fieldClass @hide
25430      */
25431     /**
25432      * @cfg {String} focusClass @hide
25433      */
25434     /**
25435      * @cfg {String} autoCreate @hide
25436      */
25437     /**
25438      * @cfg {String} inputType @hide
25439      */
25440     /**
25441      * @cfg {String} invalidClass @hide
25442      */
25443     /**
25444      * @cfg {String} invalidText @hide
25445      */
25446     /**
25447      * @cfg {String} msgFx @hide
25448      */
25449     /**
25450      * @cfg {String} validateOnBlur @hide
25451      */
25452 });
25453
25454 Roo.HtmlEditorCore.white = [
25455         'area', 'br', 'img', 'input', 'hr', 'wbr',
25456         
25457        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25458        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25459        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25460        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25461        'table',   'ul',         'xmp', 
25462        
25463        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25464       'thead',   'tr', 
25465      
25466       'dir', 'menu', 'ol', 'ul', 'dl',
25467        
25468       'embed',  'object'
25469 ];
25470
25471
25472 Roo.HtmlEditorCore.black = [
25473     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25474         'applet', // 
25475         'base',   'basefont', 'bgsound', 'blink',  'body', 
25476         'frame',  'frameset', 'head',    'html',   'ilayer', 
25477         'iframe', 'layer',  'link',     'meta',    'object',   
25478         'script', 'style' ,'title',  'xml' // clean later..
25479 ];
25480 Roo.HtmlEditorCore.clean = [
25481     'script', 'style', 'title', 'xml'
25482 ];
25483 Roo.HtmlEditorCore.remove = [
25484     'font'
25485 ];
25486 // attributes..
25487
25488 Roo.HtmlEditorCore.ablack = [
25489     'on'
25490 ];
25491     
25492 Roo.HtmlEditorCore.aclean = [ 
25493     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25494 ];
25495
25496 // protocols..
25497 Roo.HtmlEditorCore.pwhite= [
25498         'http',  'https',  'mailto'
25499 ];
25500
25501 // white listed style attributes.
25502 Roo.HtmlEditorCore.cwhite= [
25503       //  'text-align', /// default is to allow most things..
25504       
25505          
25506 //        'font-size'//??
25507 ];
25508
25509 // black listed style attributes.
25510 Roo.HtmlEditorCore.cblack= [
25511       //  'font-size' -- this can be set by the project 
25512 ];
25513
25514
25515 Roo.HtmlEditorCore.swapCodes   =[ 
25516     [    8211, "--" ], 
25517     [    8212, "--" ], 
25518     [    8216,  "'" ],  
25519     [    8217, "'" ],  
25520     [    8220, '"' ],  
25521     [    8221, '"' ],  
25522     [    8226, "*" ],  
25523     [    8230, "..." ]
25524 ]; 
25525
25526     /*
25527  * - LGPL
25528  *
25529  * HtmlEditor
25530  * 
25531  */
25532
25533 /**
25534  * @class Roo.bootstrap.HtmlEditor
25535  * @extends Roo.bootstrap.TextArea
25536  * Bootstrap HtmlEditor class
25537
25538  * @constructor
25539  * Create a new HtmlEditor
25540  * @param {Object} config The config object
25541  */
25542
25543 Roo.bootstrap.HtmlEditor = function(config){
25544     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25545     if (!this.toolbars) {
25546         this.toolbars = [];
25547     }
25548     
25549     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25550     this.addEvents({
25551             /**
25552              * @event initialize
25553              * Fires when the editor is fully initialized (including the iframe)
25554              * @param {HtmlEditor} this
25555              */
25556             initialize: true,
25557             /**
25558              * @event activate
25559              * Fires when the editor is first receives the focus. Any insertion must wait
25560              * until after this event.
25561              * @param {HtmlEditor} this
25562              */
25563             activate: true,
25564              /**
25565              * @event beforesync
25566              * Fires before the textarea is updated with content from the editor iframe. Return false
25567              * to cancel the sync.
25568              * @param {HtmlEditor} this
25569              * @param {String} html
25570              */
25571             beforesync: true,
25572              /**
25573              * @event beforepush
25574              * Fires before the iframe editor is updated with content from the textarea. Return false
25575              * to cancel the push.
25576              * @param {HtmlEditor} this
25577              * @param {String} html
25578              */
25579             beforepush: true,
25580              /**
25581              * @event sync
25582              * Fires when the textarea is updated with content from the editor iframe.
25583              * @param {HtmlEditor} this
25584              * @param {String} html
25585              */
25586             sync: true,
25587              /**
25588              * @event push
25589              * Fires when the iframe editor is updated with content from the textarea.
25590              * @param {HtmlEditor} this
25591              * @param {String} html
25592              */
25593             push: true,
25594              /**
25595              * @event editmodechange
25596              * Fires when the editor switches edit modes
25597              * @param {HtmlEditor} this
25598              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25599              */
25600             editmodechange: true,
25601             /**
25602              * @event editorevent
25603              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25604              * @param {HtmlEditor} this
25605              */
25606             editorevent: true,
25607             /**
25608              * @event firstfocus
25609              * Fires when on first focus - needed by toolbars..
25610              * @param {HtmlEditor} this
25611              */
25612             firstfocus: true,
25613             /**
25614              * @event autosave
25615              * Auto save the htmlEditor value as a file into Events
25616              * @param {HtmlEditor} this
25617              */
25618             autosave: true,
25619             /**
25620              * @event savedpreview
25621              * preview the saved version of htmlEditor
25622              * @param {HtmlEditor} this
25623              */
25624             savedpreview: true
25625         });
25626 };
25627
25628
25629 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25630     
25631     
25632       /**
25633      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25634      */
25635     toolbars : false,
25636     
25637      /**
25638     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25639     */
25640     btns : [],
25641    
25642      /**
25643      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25644      *                        Roo.resizable.
25645      */
25646     resizable : false,
25647      /**
25648      * @cfg {Number} height (in pixels)
25649      */   
25650     height: 300,
25651    /**
25652      * @cfg {Number} width (in pixels)
25653      */   
25654     width: false,
25655     
25656     /**
25657      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25658      * 
25659      */
25660     stylesheets: false,
25661     
25662     // id of frame..
25663     frameId: false,
25664     
25665     // private properties
25666     validationEvent : false,
25667     deferHeight: true,
25668     initialized : false,
25669     activated : false,
25670     
25671     onFocus : Roo.emptyFn,
25672     iframePad:3,
25673     hideMode:'offsets',
25674     
25675     tbContainer : false,
25676     
25677     bodyCls : '',
25678     
25679     toolbarContainer :function() {
25680         return this.wrap.select('.x-html-editor-tb',true).first();
25681     },
25682
25683     /**
25684      * Protected method that will not generally be called directly. It
25685      * is called when the editor creates its toolbar. Override this method if you need to
25686      * add custom toolbar buttons.
25687      * @param {HtmlEditor} editor
25688      */
25689     createToolbar : function(){
25690         Roo.log('renewing');
25691         Roo.log("create toolbars");
25692         
25693         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25694         this.toolbars[0].render(this.toolbarContainer());
25695         
25696         return;
25697         
25698 //        if (!editor.toolbars || !editor.toolbars.length) {
25699 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25700 //        }
25701 //        
25702 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25703 //            editor.toolbars[i] = Roo.factory(
25704 //                    typeof(editor.toolbars[i]) == 'string' ?
25705 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25706 //                Roo.bootstrap.HtmlEditor);
25707 //            editor.toolbars[i].init(editor);
25708 //        }
25709     },
25710
25711      
25712     // private
25713     onRender : function(ct, position)
25714     {
25715        // Roo.log("Call onRender: " + this.xtype);
25716         var _t = this;
25717         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25718       
25719         this.wrap = this.inputEl().wrap({
25720             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25721         });
25722         
25723         this.editorcore.onRender(ct, position);
25724          
25725         if (this.resizable) {
25726             this.resizeEl = new Roo.Resizable(this.wrap, {
25727                 pinned : true,
25728                 wrap: true,
25729                 dynamic : true,
25730                 minHeight : this.height,
25731                 height: this.height,
25732                 handles : this.resizable,
25733                 width: this.width,
25734                 listeners : {
25735                     resize : function(r, w, h) {
25736                         _t.onResize(w,h); // -something
25737                     }
25738                 }
25739             });
25740             
25741         }
25742         this.createToolbar(this);
25743        
25744         
25745         if(!this.width && this.resizable){
25746             this.setSize(this.wrap.getSize());
25747         }
25748         if (this.resizeEl) {
25749             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25750             // should trigger onReize..
25751         }
25752         
25753     },
25754
25755     // private
25756     onResize : function(w, h)
25757     {
25758         Roo.log('resize: ' +w + ',' + h );
25759         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25760         var ew = false;
25761         var eh = false;
25762         
25763         if(this.inputEl() ){
25764             if(typeof w == 'number'){
25765                 var aw = w - this.wrap.getFrameWidth('lr');
25766                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25767                 ew = aw;
25768             }
25769             if(typeof h == 'number'){
25770                  var tbh = -11;  // fixme it needs to tool bar size!
25771                 for (var i =0; i < this.toolbars.length;i++) {
25772                     // fixme - ask toolbars for heights?
25773                     tbh += this.toolbars[i].el.getHeight();
25774                     //if (this.toolbars[i].footer) {
25775                     //    tbh += this.toolbars[i].footer.el.getHeight();
25776                     //}
25777                 }
25778               
25779                 
25780                 
25781                 
25782                 
25783                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25784                 ah -= 5; // knock a few pixes off for look..
25785                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25786                 var eh = ah;
25787             }
25788         }
25789         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25790         this.editorcore.onResize(ew,eh);
25791         
25792     },
25793
25794     /**
25795      * Toggles the editor between standard and source edit mode.
25796      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25797      */
25798     toggleSourceEdit : function(sourceEditMode)
25799     {
25800         this.editorcore.toggleSourceEdit(sourceEditMode);
25801         
25802         if(this.editorcore.sourceEditMode){
25803             Roo.log('editor - showing textarea');
25804             
25805 //            Roo.log('in');
25806 //            Roo.log(this.syncValue());
25807             this.syncValue();
25808             this.inputEl().removeClass(['hide', 'x-hidden']);
25809             this.inputEl().dom.removeAttribute('tabIndex');
25810             this.inputEl().focus();
25811         }else{
25812             Roo.log('editor - hiding textarea');
25813 //            Roo.log('out')
25814 //            Roo.log(this.pushValue()); 
25815             this.pushValue();
25816             
25817             this.inputEl().addClass(['hide', 'x-hidden']);
25818             this.inputEl().dom.setAttribute('tabIndex', -1);
25819             //this.deferFocus();
25820         }
25821          
25822         if(this.resizable){
25823             this.setSize(this.wrap.getSize());
25824         }
25825         
25826         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25827     },
25828  
25829     // private (for BoxComponent)
25830     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25831
25832     // private (for BoxComponent)
25833     getResizeEl : function(){
25834         return this.wrap;
25835     },
25836
25837     // private (for BoxComponent)
25838     getPositionEl : function(){
25839         return this.wrap;
25840     },
25841
25842     // private
25843     initEvents : function(){
25844         this.originalValue = this.getValue();
25845     },
25846
25847 //    /**
25848 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25849 //     * @method
25850 //     */
25851 //    markInvalid : Roo.emptyFn,
25852 //    /**
25853 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25854 //     * @method
25855 //     */
25856 //    clearInvalid : Roo.emptyFn,
25857
25858     setValue : function(v){
25859         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25860         this.editorcore.pushValue();
25861     },
25862
25863      
25864     // private
25865     deferFocus : function(){
25866         this.focus.defer(10, this);
25867     },
25868
25869     // doc'ed in Field
25870     focus : function(){
25871         this.editorcore.focus();
25872         
25873     },
25874       
25875
25876     // private
25877     onDestroy : function(){
25878         
25879         
25880         
25881         if(this.rendered){
25882             
25883             for (var i =0; i < this.toolbars.length;i++) {
25884                 // fixme - ask toolbars for heights?
25885                 this.toolbars[i].onDestroy();
25886             }
25887             
25888             this.wrap.dom.innerHTML = '';
25889             this.wrap.remove();
25890         }
25891     },
25892
25893     // private
25894     onFirstFocus : function(){
25895         //Roo.log("onFirstFocus");
25896         this.editorcore.onFirstFocus();
25897          for (var i =0; i < this.toolbars.length;i++) {
25898             this.toolbars[i].onFirstFocus();
25899         }
25900         
25901     },
25902     
25903     // private
25904     syncValue : function()
25905     {   
25906         this.editorcore.syncValue();
25907     },
25908     
25909     pushValue : function()
25910     {   
25911         this.editorcore.pushValue();
25912     }
25913      
25914     
25915     // hide stuff that is not compatible
25916     /**
25917      * @event blur
25918      * @hide
25919      */
25920     /**
25921      * @event change
25922      * @hide
25923      */
25924     /**
25925      * @event focus
25926      * @hide
25927      */
25928     /**
25929      * @event specialkey
25930      * @hide
25931      */
25932     /**
25933      * @cfg {String} fieldClass @hide
25934      */
25935     /**
25936      * @cfg {String} focusClass @hide
25937      */
25938     /**
25939      * @cfg {String} autoCreate @hide
25940      */
25941     /**
25942      * @cfg {String} inputType @hide
25943      */
25944      
25945     /**
25946      * @cfg {String} invalidText @hide
25947      */
25948     /**
25949      * @cfg {String} msgFx @hide
25950      */
25951     /**
25952      * @cfg {String} validateOnBlur @hide
25953      */
25954 });
25955  
25956     
25957    
25958    
25959    
25960       
25961 Roo.namespace('Roo.bootstrap.htmleditor');
25962 /**
25963  * @class Roo.bootstrap.HtmlEditorToolbar1
25964  * Basic Toolbar
25965  * 
25966  * @example
25967  * Usage:
25968  *
25969  new Roo.bootstrap.HtmlEditor({
25970     ....
25971     toolbars : [
25972         new Roo.bootstrap.HtmlEditorToolbar1({
25973             disable : { fonts: 1 , format: 1, ..., ... , ...],
25974             btns : [ .... ]
25975         })
25976     }
25977      
25978  * 
25979  * @cfg {Object} disable List of elements to disable..
25980  * @cfg {Array} btns List of additional buttons.
25981  * 
25982  * 
25983  * NEEDS Extra CSS? 
25984  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25985  */
25986  
25987 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25988 {
25989     
25990     Roo.apply(this, config);
25991     
25992     // default disabled, based on 'good practice'..
25993     this.disable = this.disable || {};
25994     Roo.applyIf(this.disable, {
25995         fontSize : true,
25996         colors : true,
25997         specialElements : true
25998     });
25999     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26000     
26001     this.editor = config.editor;
26002     this.editorcore = config.editor.editorcore;
26003     
26004     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26005     
26006     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26007     // dont call parent... till later.
26008 }
26009 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26010      
26011     bar : true,
26012     
26013     editor : false,
26014     editorcore : false,
26015     
26016     
26017     formats : [
26018         "p" ,  
26019         "h1","h2","h3","h4","h5","h6", 
26020         "pre", "code", 
26021         "abbr", "acronym", "address", "cite", "samp", "var",
26022         'div','span'
26023     ],
26024     
26025     onRender : function(ct, position)
26026     {
26027        // Roo.log("Call onRender: " + this.xtype);
26028         
26029        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26030        Roo.log(this.el);
26031        this.el.dom.style.marginBottom = '0';
26032        var _this = this;
26033        var editorcore = this.editorcore;
26034        var editor= this.editor;
26035        
26036        var children = [];
26037        var btn = function(id,cmd , toggle, handler, html){
26038        
26039             var  event = toggle ? 'toggle' : 'click';
26040        
26041             var a = {
26042                 size : 'sm',
26043                 xtype: 'Button',
26044                 xns: Roo.bootstrap,
26045                 //glyphicon : id,
26046                 fa: id,
26047                 cmd : id || cmd,
26048                 enableToggle:toggle !== false,
26049                 html : html || '',
26050                 pressed : toggle ? false : null,
26051                 listeners : {}
26052             };
26053             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26054                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26055             };
26056             children.push(a);
26057             return a;
26058        }
26059        
26060     //    var cb_box = function...
26061         
26062         var style = {
26063                 xtype: 'Button',
26064                 size : 'sm',
26065                 xns: Roo.bootstrap,
26066                 fa : 'font',
26067                 //html : 'submit'
26068                 menu : {
26069                     xtype: 'Menu',
26070                     xns: Roo.bootstrap,
26071                     items:  []
26072                 }
26073         };
26074         Roo.each(this.formats, function(f) {
26075             style.menu.items.push({
26076                 xtype :'MenuItem',
26077                 xns: Roo.bootstrap,
26078                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26079                 tagname : f,
26080                 listeners : {
26081                     click : function()
26082                     {
26083                         editorcore.insertTag(this.tagname);
26084                         editor.focus();
26085                     }
26086                 }
26087                 
26088             });
26089         });
26090         children.push(style);   
26091         
26092         btn('bold',false,true);
26093         btn('italic',false,true);
26094         btn('align-left', 'justifyleft',true);
26095         btn('align-center', 'justifycenter',true);
26096         btn('align-right' , 'justifyright',true);
26097         btn('link', false, false, function(btn) {
26098             //Roo.log("create link?");
26099             var url = prompt(this.createLinkText, this.defaultLinkValue);
26100             if(url && url != 'http:/'+'/'){
26101                 this.editorcore.relayCmd('createlink', url);
26102             }
26103         }),
26104         btn('list','insertunorderedlist',true);
26105         btn('pencil', false,true, function(btn){
26106                 Roo.log(this);
26107                 this.toggleSourceEdit(btn.pressed);
26108         });
26109         
26110         if (this.editor.btns.length > 0) {
26111             for (var i = 0; i<this.editor.btns.length; i++) {
26112                 children.push(this.editor.btns[i]);
26113             }
26114         }
26115         
26116         /*
26117         var cog = {
26118                 xtype: 'Button',
26119                 size : 'sm',
26120                 xns: Roo.bootstrap,
26121                 glyphicon : 'cog',
26122                 //html : 'submit'
26123                 menu : {
26124                     xtype: 'Menu',
26125                     xns: Roo.bootstrap,
26126                     items:  []
26127                 }
26128         };
26129         
26130         cog.menu.items.push({
26131             xtype :'MenuItem',
26132             xns: Roo.bootstrap,
26133             html : Clean styles,
26134             tagname : f,
26135             listeners : {
26136                 click : function()
26137                 {
26138                     editorcore.insertTag(this.tagname);
26139                     editor.focus();
26140                 }
26141             }
26142             
26143         });
26144        */
26145         
26146          
26147        this.xtype = 'NavSimplebar';
26148         
26149         for(var i=0;i< children.length;i++) {
26150             
26151             this.buttons.add(this.addxtypeChild(children[i]));
26152             
26153         }
26154         
26155         editor.on('editorevent', this.updateToolbar, this);
26156     },
26157     onBtnClick : function(id)
26158     {
26159        this.editorcore.relayCmd(id);
26160        this.editorcore.focus();
26161     },
26162     
26163     /**
26164      * Protected method that will not generally be called directly. It triggers
26165      * a toolbar update by reading the markup state of the current selection in the editor.
26166      */
26167     updateToolbar: function(){
26168
26169         if(!this.editorcore.activated){
26170             this.editor.onFirstFocus(); // is this neeed?
26171             return;
26172         }
26173
26174         var btns = this.buttons; 
26175         var doc = this.editorcore.doc;
26176         btns.get('bold').setActive(doc.queryCommandState('bold'));
26177         btns.get('italic').setActive(doc.queryCommandState('italic'));
26178         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26179         
26180         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26181         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26182         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26183         
26184         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26185         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26186          /*
26187         
26188         var ans = this.editorcore.getAllAncestors();
26189         if (this.formatCombo) {
26190             
26191             
26192             var store = this.formatCombo.store;
26193             this.formatCombo.setValue("");
26194             for (var i =0; i < ans.length;i++) {
26195                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26196                     // select it..
26197                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26198                     break;
26199                 }
26200             }
26201         }
26202         
26203         
26204         
26205         // hides menus... - so this cant be on a menu...
26206         Roo.bootstrap.MenuMgr.hideAll();
26207         */
26208         Roo.bootstrap.MenuMgr.hideAll();
26209         //this.editorsyncValue();
26210     },
26211     onFirstFocus: function() {
26212         this.buttons.each(function(item){
26213            item.enable();
26214         });
26215     },
26216     toggleSourceEdit : function(sourceEditMode){
26217         
26218           
26219         if(sourceEditMode){
26220             Roo.log("disabling buttons");
26221            this.buttons.each( function(item){
26222                 if(item.cmd != 'pencil'){
26223                     item.disable();
26224                 }
26225             });
26226           
26227         }else{
26228             Roo.log("enabling buttons");
26229             if(this.editorcore.initialized){
26230                 this.buttons.each( function(item){
26231                     item.enable();
26232                 });
26233             }
26234             
26235         }
26236         Roo.log("calling toggole on editor");
26237         // tell the editor that it's been pressed..
26238         this.editor.toggleSourceEdit(sourceEditMode);
26239        
26240     }
26241 });
26242
26243
26244
26245
26246  
26247 /*
26248  * - LGPL
26249  */
26250
26251 /**
26252  * @class Roo.bootstrap.Markdown
26253  * @extends Roo.bootstrap.TextArea
26254  * Bootstrap Showdown editable area
26255  * @cfg {string} content
26256  * 
26257  * @constructor
26258  * Create a new Showdown
26259  */
26260
26261 Roo.bootstrap.Markdown = function(config){
26262     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26263    
26264 };
26265
26266 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26267     
26268     editing :false,
26269     
26270     initEvents : function()
26271     {
26272         
26273         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26274         this.markdownEl = this.el.createChild({
26275             cls : 'roo-markdown-area'
26276         });
26277         this.inputEl().addClass('d-none');
26278         if (this.getValue() == '') {
26279             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26280             
26281         } else {
26282             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26283         }
26284         this.markdownEl.on('click', this.toggleTextEdit, this);
26285         this.on('blur', this.toggleTextEdit, this);
26286         this.on('specialkey', this.resizeTextArea, this);
26287     },
26288     
26289     toggleTextEdit : function()
26290     {
26291         var sh = this.markdownEl.getHeight();
26292         this.inputEl().addClass('d-none');
26293         this.markdownEl.addClass('d-none');
26294         if (!this.editing) {
26295             // show editor?
26296             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26297             this.inputEl().removeClass('d-none');
26298             this.inputEl().focus();
26299             this.editing = true;
26300             return;
26301         }
26302         // show showdown...
26303         this.updateMarkdown();
26304         this.markdownEl.removeClass('d-none');
26305         this.editing = false;
26306         return;
26307     },
26308     updateMarkdown : function()
26309     {
26310         if (this.getValue() == '') {
26311             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26312             return;
26313         }
26314  
26315         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26316     },
26317     
26318     resizeTextArea: function () {
26319         
26320         var sh = 100;
26321         Roo.log([sh, this.getValue().split("\n").length * 30]);
26322         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26323     },
26324     setValue : function(val)
26325     {
26326         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26327         if (!this.editing) {
26328             this.updateMarkdown();
26329         }
26330         
26331     },
26332     focus : function()
26333     {
26334         if (!this.editing) {
26335             this.toggleTextEdit();
26336         }
26337         
26338     }
26339
26340
26341 });
26342 /**
26343  * @class Roo.bootstrap.Table.AbstractSelectionModel
26344  * @extends Roo.util.Observable
26345  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26346  * implemented by descendant classes.  This class should not be directly instantiated.
26347  * @constructor
26348  */
26349 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26350     this.locked = false;
26351     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26352 };
26353
26354
26355 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26356     /** @ignore Called by the grid automatically. Do not call directly. */
26357     init : function(grid){
26358         this.grid = grid;
26359         this.initEvents();
26360     },
26361
26362     /**
26363      * Locks the selections.
26364      */
26365     lock : function(){
26366         this.locked = true;
26367     },
26368
26369     /**
26370      * Unlocks the selections.
26371      */
26372     unlock : function(){
26373         this.locked = false;
26374     },
26375
26376     /**
26377      * Returns true if the selections are locked.
26378      * @return {Boolean}
26379      */
26380     isLocked : function(){
26381         return this.locked;
26382     },
26383     
26384     
26385     initEvents : function ()
26386     {
26387         
26388     }
26389 });
26390 /**
26391  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26392  * @class Roo.bootstrap.Table.RowSelectionModel
26393  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26394  * It supports multiple selections and keyboard selection/navigation. 
26395  * @constructor
26396  * @param {Object} config
26397  */
26398
26399 Roo.bootstrap.Table.RowSelectionModel = function(config){
26400     Roo.apply(this, config);
26401     this.selections = new Roo.util.MixedCollection(false, function(o){
26402         return o.id;
26403     });
26404
26405     this.last = false;
26406     this.lastActive = false;
26407
26408     this.addEvents({
26409         /**
26410              * @event selectionchange
26411              * Fires when the selection changes
26412              * @param {SelectionModel} this
26413              */
26414             "selectionchange" : true,
26415         /**
26416              * @event afterselectionchange
26417              * Fires after the selection changes (eg. by key press or clicking)
26418              * @param {SelectionModel} this
26419              */
26420             "afterselectionchange" : true,
26421         /**
26422              * @event beforerowselect
26423              * Fires when a row is selected being selected, return false to cancel.
26424              * @param {SelectionModel} this
26425              * @param {Number} rowIndex The selected index
26426              * @param {Boolean} keepExisting False if other selections will be cleared
26427              */
26428             "beforerowselect" : true,
26429         /**
26430              * @event rowselect
26431              * Fires when a row is selected.
26432              * @param {SelectionModel} this
26433              * @param {Number} rowIndex The selected index
26434              * @param {Roo.data.Record} r The record
26435              */
26436             "rowselect" : true,
26437         /**
26438              * @event rowdeselect
26439              * Fires when a row is deselected.
26440              * @param {SelectionModel} this
26441              * @param {Number} rowIndex The selected index
26442              */
26443         "rowdeselect" : true
26444     });
26445     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26446     this.locked = false;
26447  };
26448
26449 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26450     /**
26451      * @cfg {Boolean} singleSelect
26452      * True to allow selection of only one row at a time (defaults to false)
26453      */
26454     singleSelect : false,
26455
26456     // private
26457     initEvents : function()
26458     {
26459
26460         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26461         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26462         //}else{ // allow click to work like normal
26463          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26464         //}
26465         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26466         this.grid.on("rowclick", this.handleMouseDown, this);
26467         
26468         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26469             "up" : function(e){
26470                 if(!e.shiftKey){
26471                     this.selectPrevious(e.shiftKey);
26472                 }else if(this.last !== false && this.lastActive !== false){
26473                     var last = this.last;
26474                     this.selectRange(this.last,  this.lastActive-1);
26475                     this.grid.getView().focusRow(this.lastActive);
26476                     if(last !== false){
26477                         this.last = last;
26478                     }
26479                 }else{
26480                     this.selectFirstRow();
26481                 }
26482                 this.fireEvent("afterselectionchange", this);
26483             },
26484             "down" : function(e){
26485                 if(!e.shiftKey){
26486                     this.selectNext(e.shiftKey);
26487                 }else if(this.last !== false && this.lastActive !== false){
26488                     var last = this.last;
26489                     this.selectRange(this.last,  this.lastActive+1);
26490                     this.grid.getView().focusRow(this.lastActive);
26491                     if(last !== false){
26492                         this.last = last;
26493                     }
26494                 }else{
26495                     this.selectFirstRow();
26496                 }
26497                 this.fireEvent("afterselectionchange", this);
26498             },
26499             scope: this
26500         });
26501         this.grid.store.on('load', function(){
26502             this.selections.clear();
26503         },this);
26504         /*
26505         var view = this.grid.view;
26506         view.on("refresh", this.onRefresh, this);
26507         view.on("rowupdated", this.onRowUpdated, this);
26508         view.on("rowremoved", this.onRemove, this);
26509         */
26510     },
26511
26512     // private
26513     onRefresh : function()
26514     {
26515         var ds = this.grid.store, i, v = this.grid.view;
26516         var s = this.selections;
26517         s.each(function(r){
26518             if((i = ds.indexOfId(r.id)) != -1){
26519                 v.onRowSelect(i);
26520             }else{
26521                 s.remove(r);
26522             }
26523         });
26524     },
26525
26526     // private
26527     onRemove : function(v, index, r){
26528         this.selections.remove(r);
26529     },
26530
26531     // private
26532     onRowUpdated : function(v, index, r){
26533         if(this.isSelected(r)){
26534             v.onRowSelect(index);
26535         }
26536     },
26537
26538     /**
26539      * Select records.
26540      * @param {Array} records The records to select
26541      * @param {Boolean} keepExisting (optional) True to keep existing selections
26542      */
26543     selectRecords : function(records, keepExisting)
26544     {
26545         if(!keepExisting){
26546             this.clearSelections();
26547         }
26548             var ds = this.grid.store;
26549         for(var i = 0, len = records.length; i < len; i++){
26550             this.selectRow(ds.indexOf(records[i]), true);
26551         }
26552     },
26553
26554     /**
26555      * Gets the number of selected rows.
26556      * @return {Number}
26557      */
26558     getCount : function(){
26559         return this.selections.length;
26560     },
26561
26562     /**
26563      * Selects the first row in the grid.
26564      */
26565     selectFirstRow : function(){
26566         this.selectRow(0);
26567     },
26568
26569     /**
26570      * Select the last row.
26571      * @param {Boolean} keepExisting (optional) True to keep existing selections
26572      */
26573     selectLastRow : function(keepExisting){
26574         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26575         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26576     },
26577
26578     /**
26579      * Selects the row immediately following the last selected row.
26580      * @param {Boolean} keepExisting (optional) True to keep existing selections
26581      */
26582     selectNext : function(keepExisting)
26583     {
26584             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26585             this.selectRow(this.last+1, keepExisting);
26586             this.grid.getView().focusRow(this.last);
26587         }
26588     },
26589
26590     /**
26591      * Selects the row that precedes the last selected row.
26592      * @param {Boolean} keepExisting (optional) True to keep existing selections
26593      */
26594     selectPrevious : function(keepExisting){
26595         if(this.last){
26596             this.selectRow(this.last-1, keepExisting);
26597             this.grid.getView().focusRow(this.last);
26598         }
26599     },
26600
26601     /**
26602      * Returns the selected records
26603      * @return {Array} Array of selected records
26604      */
26605     getSelections : function(){
26606         return [].concat(this.selections.items);
26607     },
26608
26609     /**
26610      * Returns the first selected record.
26611      * @return {Record}
26612      */
26613     getSelected : function(){
26614         return this.selections.itemAt(0);
26615     },
26616
26617
26618     /**
26619      * Clears all selections.
26620      */
26621     clearSelections : function(fast)
26622     {
26623         if(this.locked) {
26624             return;
26625         }
26626         if(fast !== true){
26627                 var ds = this.grid.store;
26628             var s = this.selections;
26629             s.each(function(r){
26630                 this.deselectRow(ds.indexOfId(r.id));
26631             }, this);
26632             s.clear();
26633         }else{
26634             this.selections.clear();
26635         }
26636         this.last = false;
26637     },
26638
26639
26640     /**
26641      * Selects all rows.
26642      */
26643     selectAll : function(){
26644         if(this.locked) {
26645             return;
26646         }
26647         this.selections.clear();
26648         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26649             this.selectRow(i, true);
26650         }
26651     },
26652
26653     /**
26654      * Returns True if there is a selection.
26655      * @return {Boolean}
26656      */
26657     hasSelection : function(){
26658         return this.selections.length > 0;
26659     },
26660
26661     /**
26662      * Returns True if the specified row is selected.
26663      * @param {Number/Record} record The record or index of the record to check
26664      * @return {Boolean}
26665      */
26666     isSelected : function(index){
26667             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26668         return (r && this.selections.key(r.id) ? true : false);
26669     },
26670
26671     /**
26672      * Returns True if the specified record id is selected.
26673      * @param {String} id The id of record to check
26674      * @return {Boolean}
26675      */
26676     isIdSelected : function(id){
26677         return (this.selections.key(id) ? true : false);
26678     },
26679
26680
26681     // private
26682     handleMouseDBClick : function(e, t){
26683         
26684     },
26685     // private
26686     handleMouseDown : function(e, t)
26687     {
26688             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26689         if(this.isLocked() || rowIndex < 0 ){
26690             return;
26691         };
26692         if(e.shiftKey && this.last !== false){
26693             var last = this.last;
26694             this.selectRange(last, rowIndex, e.ctrlKey);
26695             this.last = last; // reset the last
26696             t.focus();
26697     
26698         }else{
26699             var isSelected = this.isSelected(rowIndex);
26700             //Roo.log("select row:" + rowIndex);
26701             if(isSelected){
26702                 this.deselectRow(rowIndex);
26703             } else {
26704                         this.selectRow(rowIndex, true);
26705             }
26706     
26707             /*
26708                 if(e.button !== 0 && isSelected){
26709                 alert('rowIndex 2: ' + rowIndex);
26710                     view.focusRow(rowIndex);
26711                 }else if(e.ctrlKey && isSelected){
26712                     this.deselectRow(rowIndex);
26713                 }else if(!isSelected){
26714                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26715                     view.focusRow(rowIndex);
26716                 }
26717             */
26718         }
26719         this.fireEvent("afterselectionchange", this);
26720     },
26721     // private
26722     handleDragableRowClick :  function(grid, rowIndex, e) 
26723     {
26724         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26725             this.selectRow(rowIndex, false);
26726             grid.view.focusRow(rowIndex);
26727              this.fireEvent("afterselectionchange", this);
26728         }
26729     },
26730     
26731     /**
26732      * Selects multiple rows.
26733      * @param {Array} rows Array of the indexes of the row to select
26734      * @param {Boolean} keepExisting (optional) True to keep existing selections
26735      */
26736     selectRows : function(rows, keepExisting){
26737         if(!keepExisting){
26738             this.clearSelections();
26739         }
26740         for(var i = 0, len = rows.length; i < len; i++){
26741             this.selectRow(rows[i], true);
26742         }
26743     },
26744
26745     /**
26746      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26747      * @param {Number} startRow The index of the first row in the range
26748      * @param {Number} endRow The index of the last row in the range
26749      * @param {Boolean} keepExisting (optional) True to retain existing selections
26750      */
26751     selectRange : function(startRow, endRow, keepExisting){
26752         if(this.locked) {
26753             return;
26754         }
26755         if(!keepExisting){
26756             this.clearSelections();
26757         }
26758         if(startRow <= endRow){
26759             for(var i = startRow; i <= endRow; i++){
26760                 this.selectRow(i, true);
26761             }
26762         }else{
26763             for(var i = startRow; i >= endRow; i--){
26764                 this.selectRow(i, true);
26765             }
26766         }
26767     },
26768
26769     /**
26770      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26771      * @param {Number} startRow The index of the first row in the range
26772      * @param {Number} endRow The index of the last row in the range
26773      */
26774     deselectRange : function(startRow, endRow, preventViewNotify){
26775         if(this.locked) {
26776             return;
26777         }
26778         for(var i = startRow; i <= endRow; i++){
26779             this.deselectRow(i, preventViewNotify);
26780         }
26781     },
26782
26783     /**
26784      * Selects a row.
26785      * @param {Number} row The index of the row to select
26786      * @param {Boolean} keepExisting (optional) True to keep existing selections
26787      */
26788     selectRow : function(index, keepExisting, preventViewNotify)
26789     {
26790             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26791             return;
26792         }
26793         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26794             if(!keepExisting || this.singleSelect){
26795                 this.clearSelections();
26796             }
26797             
26798             var r = this.grid.store.getAt(index);
26799             //console.log('selectRow - record id :' + r.id);
26800             
26801             this.selections.add(r);
26802             this.last = this.lastActive = index;
26803             if(!preventViewNotify){
26804                 var proxy = new Roo.Element(
26805                                 this.grid.getRowDom(index)
26806                 );
26807                 proxy.addClass('bg-info info');
26808             }
26809             this.fireEvent("rowselect", this, index, r);
26810             this.fireEvent("selectionchange", this);
26811         }
26812     },
26813
26814     /**
26815      * Deselects a row.
26816      * @param {Number} row The index of the row to deselect
26817      */
26818     deselectRow : function(index, preventViewNotify)
26819     {
26820         if(this.locked) {
26821             return;
26822         }
26823         if(this.last == index){
26824             this.last = false;
26825         }
26826         if(this.lastActive == index){
26827             this.lastActive = false;
26828         }
26829         
26830         var r = this.grid.store.getAt(index);
26831         if (!r) {
26832             return;
26833         }
26834         
26835         this.selections.remove(r);
26836         //.console.log('deselectRow - record id :' + r.id);
26837         if(!preventViewNotify){
26838         
26839             var proxy = new Roo.Element(
26840                 this.grid.getRowDom(index)
26841             );
26842             proxy.removeClass('bg-info info');
26843         }
26844         this.fireEvent("rowdeselect", this, index);
26845         this.fireEvent("selectionchange", this);
26846     },
26847
26848     // private
26849     restoreLast : function(){
26850         if(this._last){
26851             this.last = this._last;
26852         }
26853     },
26854
26855     // private
26856     acceptsNav : function(row, col, cm){
26857         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26858     },
26859
26860     // private
26861     onEditorKey : function(field, e){
26862         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26863         if(k == e.TAB){
26864             e.stopEvent();
26865             ed.completeEdit();
26866             if(e.shiftKey){
26867                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26868             }else{
26869                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26870             }
26871         }else if(k == e.ENTER && !e.ctrlKey){
26872             e.stopEvent();
26873             ed.completeEdit();
26874             if(e.shiftKey){
26875                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26876             }else{
26877                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26878             }
26879         }else if(k == e.ESC){
26880             ed.cancelEdit();
26881         }
26882         if(newCell){
26883             g.startEditing(newCell[0], newCell[1]);
26884         }
26885     }
26886 });
26887 /*
26888  * Based on:
26889  * Ext JS Library 1.1.1
26890  * Copyright(c) 2006-2007, Ext JS, LLC.
26891  *
26892  * Originally Released Under LGPL - original licence link has changed is not relivant.
26893  *
26894  * Fork - LGPL
26895  * <script type="text/javascript">
26896  */
26897  
26898 /**
26899  * @class Roo.bootstrap.PagingToolbar
26900  * @extends Roo.bootstrap.NavSimplebar
26901  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26902  * @constructor
26903  * Create a new PagingToolbar
26904  * @param {Object} config The config object
26905  * @param {Roo.data.Store} store
26906  */
26907 Roo.bootstrap.PagingToolbar = function(config)
26908 {
26909     // old args format still supported... - xtype is prefered..
26910         // created from xtype...
26911     
26912     this.ds = config.dataSource;
26913     
26914     if (config.store && !this.ds) {
26915         this.store= Roo.factory(config.store, Roo.data);
26916         this.ds = this.store;
26917         this.ds.xmodule = this.xmodule || false;
26918     }
26919     
26920     this.toolbarItems = [];
26921     if (config.items) {
26922         this.toolbarItems = config.items;
26923     }
26924     
26925     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26926     
26927     this.cursor = 0;
26928     
26929     if (this.ds) { 
26930         this.bind(this.ds);
26931     }
26932     
26933     if (Roo.bootstrap.version == 4) {
26934         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26935     } else {
26936         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26937     }
26938     
26939 };
26940
26941 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26942     /**
26943      * @cfg {Roo.data.Store} dataSource
26944      * The underlying data store providing the paged data
26945      */
26946     /**
26947      * @cfg {String/HTMLElement/Element} container
26948      * container The id or element that will contain the toolbar
26949      */
26950     /**
26951      * @cfg {Boolean} displayInfo
26952      * True to display the displayMsg (defaults to false)
26953      */
26954     /**
26955      * @cfg {Number} pageSize
26956      * The number of records to display per page (defaults to 20)
26957      */
26958     pageSize: 20,
26959     /**
26960      * @cfg {String} displayMsg
26961      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26962      */
26963     displayMsg : 'Displaying {0} - {1} of {2}',
26964     /**
26965      * @cfg {String} emptyMsg
26966      * The message to display when no records are found (defaults to "No data to display")
26967      */
26968     emptyMsg : 'No data to display',
26969     /**
26970      * Customizable piece of the default paging text (defaults to "Page")
26971      * @type String
26972      */
26973     beforePageText : "Page",
26974     /**
26975      * Customizable piece of the default paging text (defaults to "of %0")
26976      * @type String
26977      */
26978     afterPageText : "of {0}",
26979     /**
26980      * Customizable piece of the default paging text (defaults to "First Page")
26981      * @type String
26982      */
26983     firstText : "First Page",
26984     /**
26985      * Customizable piece of the default paging text (defaults to "Previous Page")
26986      * @type String
26987      */
26988     prevText : "Previous Page",
26989     /**
26990      * Customizable piece of the default paging text (defaults to "Next Page")
26991      * @type String
26992      */
26993     nextText : "Next Page",
26994     /**
26995      * Customizable piece of the default paging text (defaults to "Last Page")
26996      * @type String
26997      */
26998     lastText : "Last Page",
26999     /**
27000      * Customizable piece of the default paging text (defaults to "Refresh")
27001      * @type String
27002      */
27003     refreshText : "Refresh",
27004
27005     buttons : false,
27006     // private
27007     onRender : function(ct, position) 
27008     {
27009         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27010         this.navgroup.parentId = this.id;
27011         this.navgroup.onRender(this.el, null);
27012         // add the buttons to the navgroup
27013         
27014         if(this.displayInfo){
27015             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27016             this.displayEl = this.el.select('.x-paging-info', true).first();
27017 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27018 //            this.displayEl = navel.el.select('span',true).first();
27019         }
27020         
27021         var _this = this;
27022         
27023         if(this.buttons){
27024             Roo.each(_this.buttons, function(e){ // this might need to use render????
27025                Roo.factory(e).render(_this.el);
27026             });
27027         }
27028             
27029         Roo.each(_this.toolbarItems, function(e) {
27030             _this.navgroup.addItem(e);
27031         });
27032         
27033         
27034         this.first = this.navgroup.addItem({
27035             tooltip: this.firstText,
27036             cls: "prev btn-outline-secondary",
27037             html : ' <i class="fa fa-step-backward"></i>',
27038             disabled: true,
27039             preventDefault: true,
27040             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27041         });
27042         
27043         this.prev =  this.navgroup.addItem({
27044             tooltip: this.prevText,
27045             cls: "prev btn-outline-secondary",
27046             html : ' <i class="fa fa-backward"></i>',
27047             disabled: true,
27048             preventDefault: true,
27049             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27050         });
27051     //this.addSeparator();
27052         
27053         
27054         var field = this.navgroup.addItem( {
27055             tagtype : 'span',
27056             cls : 'x-paging-position  btn-outline-secondary',
27057              disabled: true,
27058             html : this.beforePageText  +
27059                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27060                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27061          } ); //?? escaped?
27062         
27063         this.field = field.el.select('input', true).first();
27064         this.field.on("keydown", this.onPagingKeydown, this);
27065         this.field.on("focus", function(){this.dom.select();});
27066     
27067     
27068         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27069         //this.field.setHeight(18);
27070         //this.addSeparator();
27071         this.next = this.navgroup.addItem({
27072             tooltip: this.nextText,
27073             cls: "next btn-outline-secondary",
27074             html : ' <i class="fa fa-forward"></i>',
27075             disabled: true,
27076             preventDefault: true,
27077             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27078         });
27079         this.last = this.navgroup.addItem({
27080             tooltip: this.lastText,
27081             html : ' <i class="fa fa-step-forward"></i>',
27082             cls: "next btn-outline-secondary",
27083             disabled: true,
27084             preventDefault: true,
27085             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27086         });
27087     //this.addSeparator();
27088         this.loading = this.navgroup.addItem({
27089             tooltip: this.refreshText,
27090             cls: "btn-outline-secondary",
27091             html : ' <i class="fa fa-refresh"></i>',
27092             preventDefault: true,
27093             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27094         });
27095         
27096     },
27097
27098     // private
27099     updateInfo : function(){
27100         if(this.displayEl){
27101             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27102             var msg = count == 0 ?
27103                 this.emptyMsg :
27104                 String.format(
27105                     this.displayMsg,
27106                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27107                 );
27108             this.displayEl.update(msg);
27109         }
27110     },
27111
27112     // private
27113     onLoad : function(ds, r, o)
27114     {
27115         this.cursor = o.params.start ? o.params.start : 0;
27116         
27117         var d = this.getPageData(),
27118             ap = d.activePage,
27119             ps = d.pages;
27120         
27121         
27122         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27123         this.field.dom.value = ap;
27124         this.first.setDisabled(ap == 1);
27125         this.prev.setDisabled(ap == 1);
27126         this.next.setDisabled(ap == ps);
27127         this.last.setDisabled(ap == ps);
27128         this.loading.enable();
27129         this.updateInfo();
27130     },
27131
27132     // private
27133     getPageData : function(){
27134         var total = this.ds.getTotalCount();
27135         return {
27136             total : total,
27137             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27138             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27139         };
27140     },
27141
27142     // private
27143     onLoadError : function(){
27144         this.loading.enable();
27145     },
27146
27147     // private
27148     onPagingKeydown : function(e){
27149         var k = e.getKey();
27150         var d = this.getPageData();
27151         if(k == e.RETURN){
27152             var v = this.field.dom.value, pageNum;
27153             if(!v || isNaN(pageNum = parseInt(v, 10))){
27154                 this.field.dom.value = d.activePage;
27155                 return;
27156             }
27157             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27158             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27159             e.stopEvent();
27160         }
27161         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))
27162         {
27163           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27164           this.field.dom.value = pageNum;
27165           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27166           e.stopEvent();
27167         }
27168         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27169         {
27170           var v = this.field.dom.value, pageNum; 
27171           var increment = (e.shiftKey) ? 10 : 1;
27172           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27173                 increment *= -1;
27174           }
27175           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27176             this.field.dom.value = d.activePage;
27177             return;
27178           }
27179           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27180           {
27181             this.field.dom.value = parseInt(v, 10) + increment;
27182             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27184           }
27185           e.stopEvent();
27186         }
27187     },
27188
27189     // private
27190     beforeLoad : function(){
27191         if(this.loading){
27192             this.loading.disable();
27193         }
27194     },
27195
27196     // private
27197     onClick : function(which){
27198         
27199         var ds = this.ds;
27200         if (!ds) {
27201             return;
27202         }
27203         
27204         switch(which){
27205             case "first":
27206                 ds.load({params:{start: 0, limit: this.pageSize}});
27207             break;
27208             case "prev":
27209                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27210             break;
27211             case "next":
27212                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27213             break;
27214             case "last":
27215                 var total = ds.getTotalCount();
27216                 var extra = total % this.pageSize;
27217                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27218                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27219             break;
27220             case "refresh":
27221                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27222             break;
27223         }
27224     },
27225
27226     /**
27227      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27228      * @param {Roo.data.Store} store The data store to unbind
27229      */
27230     unbind : function(ds){
27231         ds.un("beforeload", this.beforeLoad, this);
27232         ds.un("load", this.onLoad, this);
27233         ds.un("loadexception", this.onLoadError, this);
27234         ds.un("remove", this.updateInfo, this);
27235         ds.un("add", this.updateInfo, this);
27236         this.ds = undefined;
27237     },
27238
27239     /**
27240      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27241      * @param {Roo.data.Store} store The data store to bind
27242      */
27243     bind : function(ds){
27244         ds.on("beforeload", this.beforeLoad, this);
27245         ds.on("load", this.onLoad, this);
27246         ds.on("loadexception", this.onLoadError, this);
27247         ds.on("remove", this.updateInfo, this);
27248         ds.on("add", this.updateInfo, this);
27249         this.ds = ds;
27250     }
27251 });/*
27252  * - LGPL
27253  *
27254  * element
27255  * 
27256  */
27257
27258 /**
27259  * @class Roo.bootstrap.MessageBar
27260  * @extends Roo.bootstrap.Component
27261  * Bootstrap MessageBar class
27262  * @cfg {String} html contents of the MessageBar
27263  * @cfg {String} weight (info | success | warning | danger) default info
27264  * @cfg {String} beforeClass insert the bar before the given class
27265  * @cfg {Boolean} closable (true | false) default false
27266  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27267  * 
27268  * @constructor
27269  * Create a new Element
27270  * @param {Object} config The config object
27271  */
27272
27273 Roo.bootstrap.MessageBar = function(config){
27274     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27275 };
27276
27277 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27278     
27279     html: '',
27280     weight: 'info',
27281     closable: false,
27282     fixed: false,
27283     beforeClass: 'bootstrap-sticky-wrap',
27284     
27285     getAutoCreate : function(){
27286         
27287         var cfg = {
27288             tag: 'div',
27289             cls: 'alert alert-dismissable alert-' + this.weight,
27290             cn: [
27291                 {
27292                     tag: 'span',
27293                     cls: 'message',
27294                     html: this.html || ''
27295                 }
27296             ]
27297         };
27298         
27299         if(this.fixed){
27300             cfg.cls += ' alert-messages-fixed';
27301         }
27302         
27303         if(this.closable){
27304             cfg.cn.push({
27305                 tag: 'button',
27306                 cls: 'close',
27307                 html: 'x'
27308             });
27309         }
27310         
27311         return cfg;
27312     },
27313     
27314     onRender : function(ct, position)
27315     {
27316         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27317         
27318         if(!this.el){
27319             var cfg = Roo.apply({},  this.getAutoCreate());
27320             cfg.id = Roo.id();
27321             
27322             if (this.cls) {
27323                 cfg.cls += ' ' + this.cls;
27324             }
27325             if (this.style) {
27326                 cfg.style = this.style;
27327             }
27328             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27329             
27330             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27331         }
27332         
27333         this.el.select('>button.close').on('click', this.hide, this);
27334         
27335     },
27336     
27337     show : function()
27338     {
27339         if (!this.rendered) {
27340             this.render();
27341         }
27342         
27343         this.el.show();
27344         
27345         this.fireEvent('show', this);
27346         
27347     },
27348     
27349     hide : function()
27350     {
27351         if (!this.rendered) {
27352             this.render();
27353         }
27354         
27355         this.el.hide();
27356         
27357         this.fireEvent('hide', this);
27358     },
27359     
27360     update : function()
27361     {
27362 //        var e = this.el.dom.firstChild;
27363 //        
27364 //        if(this.closable){
27365 //            e = e.nextSibling;
27366 //        }
27367 //        
27368 //        e.data = this.html || '';
27369
27370         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27371     }
27372    
27373 });
27374
27375  
27376
27377      /*
27378  * - LGPL
27379  *
27380  * Graph
27381  * 
27382  */
27383
27384
27385 /**
27386  * @class Roo.bootstrap.Graph
27387  * @extends Roo.bootstrap.Component
27388  * Bootstrap Graph class
27389 > Prameters
27390  -sm {number} sm 4
27391  -md {number} md 5
27392  @cfg {String} graphtype  bar | vbar | pie
27393  @cfg {number} g_x coodinator | centre x (pie)
27394  @cfg {number} g_y coodinator | centre y (pie)
27395  @cfg {number} g_r radius (pie)
27396  @cfg {number} g_height height of the chart (respected by all elements in the set)
27397  @cfg {number} g_width width of the chart (respected by all elements in the set)
27398  @cfg {Object} title The title of the chart
27399     
27400  -{Array}  values
27401  -opts (object) options for the chart 
27402      o {
27403      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27404      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27405      o vgutter (number)
27406      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.
27407      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27408      o to
27409      o stretch (boolean)
27410      o }
27411  -opts (object) options for the pie
27412      o{
27413      o cut
27414      o startAngle (number)
27415      o endAngle (number)
27416      } 
27417  *
27418  * @constructor
27419  * Create a new Input
27420  * @param {Object} config The config object
27421  */
27422
27423 Roo.bootstrap.Graph = function(config){
27424     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27425     
27426     this.addEvents({
27427         // img events
27428         /**
27429          * @event click
27430          * The img click event for the img.
27431          * @param {Roo.EventObject} e
27432          */
27433         "click" : true
27434     });
27435 };
27436
27437 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27438     
27439     sm: 4,
27440     md: 5,
27441     graphtype: 'bar',
27442     g_height: 250,
27443     g_width: 400,
27444     g_x: 50,
27445     g_y: 50,
27446     g_r: 30,
27447     opts:{
27448         //g_colors: this.colors,
27449         g_type: 'soft',
27450         g_gutter: '20%'
27451
27452     },
27453     title : false,
27454
27455     getAutoCreate : function(){
27456         
27457         var cfg = {
27458             tag: 'div',
27459             html : null
27460         };
27461         
27462         
27463         return  cfg;
27464     },
27465
27466     onRender : function(ct,position){
27467         
27468         
27469         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27470         
27471         if (typeof(Raphael) == 'undefined') {
27472             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27473             return;
27474         }
27475         
27476         this.raphael = Raphael(this.el.dom);
27477         
27478                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27479                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27480                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27481                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27482                 /*
27483                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27484                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27485                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27486                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27487                 
27488                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27489                 r.barchart(330, 10, 300, 220, data1);
27490                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27491                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27492                 */
27493                 
27494                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27495                 // r.barchart(30, 30, 560, 250,  xdata, {
27496                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27497                 //     axis : "0 0 1 1",
27498                 //     axisxlabels :  xdata
27499                 //     //yvalues : cols,
27500                    
27501                 // });
27502 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27503 //        
27504 //        this.load(null,xdata,{
27505 //                axis : "0 0 1 1",
27506 //                axisxlabels :  xdata
27507 //                });
27508
27509     },
27510
27511     load : function(graphtype,xdata,opts)
27512     {
27513         this.raphael.clear();
27514         if(!graphtype) {
27515             graphtype = this.graphtype;
27516         }
27517         if(!opts){
27518             opts = this.opts;
27519         }
27520         var r = this.raphael,
27521             fin = function () {
27522                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27523             },
27524             fout = function () {
27525                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27526             },
27527             pfin = function() {
27528                 this.sector.stop();
27529                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27530
27531                 if (this.label) {
27532                     this.label[0].stop();
27533                     this.label[0].attr({ r: 7.5 });
27534                     this.label[1].attr({ "font-weight": 800 });
27535                 }
27536             },
27537             pfout = function() {
27538                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27539
27540                 if (this.label) {
27541                     this.label[0].animate({ r: 5 }, 500, "bounce");
27542                     this.label[1].attr({ "font-weight": 400 });
27543                 }
27544             };
27545
27546         switch(graphtype){
27547             case 'bar':
27548                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27549                 break;
27550             case 'hbar':
27551                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27552                 break;
27553             case 'pie':
27554 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27555 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27556 //            
27557                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27558                 
27559                 break;
27560
27561         }
27562         
27563         if(this.title){
27564             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27565         }
27566         
27567     },
27568     
27569     setTitle: function(o)
27570     {
27571         this.title = o;
27572     },
27573     
27574     initEvents: function() {
27575         
27576         if(!this.href){
27577             this.el.on('click', this.onClick, this);
27578         }
27579     },
27580     
27581     onClick : function(e)
27582     {
27583         Roo.log('img onclick');
27584         this.fireEvent('click', this, e);
27585     }
27586    
27587 });
27588
27589  
27590 /*
27591  * - LGPL
27592  *
27593  * numberBox
27594  * 
27595  */
27596 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27597
27598 /**
27599  * @class Roo.bootstrap.dash.NumberBox
27600  * @extends Roo.bootstrap.Component
27601  * Bootstrap NumberBox class
27602  * @cfg {String} headline Box headline
27603  * @cfg {String} content Box content
27604  * @cfg {String} icon Box icon
27605  * @cfg {String} footer Footer text
27606  * @cfg {String} fhref Footer href
27607  * 
27608  * @constructor
27609  * Create a new NumberBox
27610  * @param {Object} config The config object
27611  */
27612
27613
27614 Roo.bootstrap.dash.NumberBox = function(config){
27615     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27616     
27617 };
27618
27619 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27620     
27621     headline : '',
27622     content : '',
27623     icon : '',
27624     footer : '',
27625     fhref : '',
27626     ficon : '',
27627     
27628     getAutoCreate : function(){
27629         
27630         var cfg = {
27631             tag : 'div',
27632             cls : 'small-box ',
27633             cn : [
27634                 {
27635                     tag : 'div',
27636                     cls : 'inner',
27637                     cn :[
27638                         {
27639                             tag : 'h3',
27640                             cls : 'roo-headline',
27641                             html : this.headline
27642                         },
27643                         {
27644                             tag : 'p',
27645                             cls : 'roo-content',
27646                             html : this.content
27647                         }
27648                     ]
27649                 }
27650             ]
27651         };
27652         
27653         if(this.icon){
27654             cfg.cn.push({
27655                 tag : 'div',
27656                 cls : 'icon',
27657                 cn :[
27658                     {
27659                         tag : 'i',
27660                         cls : 'ion ' + this.icon
27661                     }
27662                 ]
27663             });
27664         }
27665         
27666         if(this.footer){
27667             var footer = {
27668                 tag : 'a',
27669                 cls : 'small-box-footer',
27670                 href : this.fhref || '#',
27671                 html : this.footer
27672             };
27673             
27674             cfg.cn.push(footer);
27675             
27676         }
27677         
27678         return  cfg;
27679     },
27680
27681     onRender : function(ct,position){
27682         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27683
27684
27685        
27686                 
27687     },
27688
27689     setHeadline: function (value)
27690     {
27691         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27692     },
27693     
27694     setFooter: function (value, href)
27695     {
27696         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27697         
27698         if(href){
27699             this.el.select('a.small-box-footer',true).first().attr('href', href);
27700         }
27701         
27702     },
27703
27704     setContent: function (value)
27705     {
27706         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27707     },
27708
27709     initEvents: function() 
27710     {   
27711         
27712     }
27713     
27714 });
27715
27716  
27717 /*
27718  * - LGPL
27719  *
27720  * TabBox
27721  * 
27722  */
27723 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27724
27725 /**
27726  * @class Roo.bootstrap.dash.TabBox
27727  * @extends Roo.bootstrap.Component
27728  * Bootstrap TabBox class
27729  * @cfg {String} title Title of the TabBox
27730  * @cfg {String} icon Icon of the TabBox
27731  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27732  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27733  * 
27734  * @constructor
27735  * Create a new TabBox
27736  * @param {Object} config The config object
27737  */
27738
27739
27740 Roo.bootstrap.dash.TabBox = function(config){
27741     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27742     this.addEvents({
27743         // raw events
27744         /**
27745          * @event addpane
27746          * When a pane is added
27747          * @param {Roo.bootstrap.dash.TabPane} pane
27748          */
27749         "addpane" : true,
27750         /**
27751          * @event activatepane
27752          * When a pane is activated
27753          * @param {Roo.bootstrap.dash.TabPane} pane
27754          */
27755         "activatepane" : true
27756         
27757          
27758     });
27759     
27760     this.panes = [];
27761 };
27762
27763 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27764
27765     title : '',
27766     icon : false,
27767     showtabs : true,
27768     tabScrollable : false,
27769     
27770     getChildContainer : function()
27771     {
27772         return this.el.select('.tab-content', true).first();
27773     },
27774     
27775     getAutoCreate : function(){
27776         
27777         var header = {
27778             tag: 'li',
27779             cls: 'pull-left header',
27780             html: this.title,
27781             cn : []
27782         };
27783         
27784         if(this.icon){
27785             header.cn.push({
27786                 tag: 'i',
27787                 cls: 'fa ' + this.icon
27788             });
27789         }
27790         
27791         var h = {
27792             tag: 'ul',
27793             cls: 'nav nav-tabs pull-right',
27794             cn: [
27795                 header
27796             ]
27797         };
27798         
27799         if(this.tabScrollable){
27800             h = {
27801                 tag: 'div',
27802                 cls: 'tab-header',
27803                 cn: [
27804                     {
27805                         tag: 'ul',
27806                         cls: 'nav nav-tabs pull-right',
27807                         cn: [
27808                             header
27809                         ]
27810                     }
27811                 ]
27812             };
27813         }
27814         
27815         var cfg = {
27816             tag: 'div',
27817             cls: 'nav-tabs-custom',
27818             cn: [
27819                 h,
27820                 {
27821                     tag: 'div',
27822                     cls: 'tab-content no-padding',
27823                     cn: []
27824                 }
27825             ]
27826         };
27827
27828         return  cfg;
27829     },
27830     initEvents : function()
27831     {
27832         //Roo.log('add add pane handler');
27833         this.on('addpane', this.onAddPane, this);
27834     },
27835      /**
27836      * Updates the box title
27837      * @param {String} html to set the title to.
27838      */
27839     setTitle : function(value)
27840     {
27841         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27842     },
27843     onAddPane : function(pane)
27844     {
27845         this.panes.push(pane);
27846         //Roo.log('addpane');
27847         //Roo.log(pane);
27848         // tabs are rendere left to right..
27849         if(!this.showtabs){
27850             return;
27851         }
27852         
27853         var ctr = this.el.select('.nav-tabs', true).first();
27854          
27855          
27856         var existing = ctr.select('.nav-tab',true);
27857         var qty = existing.getCount();;
27858         
27859         
27860         var tab = ctr.createChild({
27861             tag : 'li',
27862             cls : 'nav-tab' + (qty ? '' : ' active'),
27863             cn : [
27864                 {
27865                     tag : 'a',
27866                     href:'#',
27867                     html : pane.title
27868                 }
27869             ]
27870         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27871         pane.tab = tab;
27872         
27873         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27874         if (!qty) {
27875             pane.el.addClass('active');
27876         }
27877         
27878                 
27879     },
27880     onTabClick : function(ev,un,ob,pane)
27881     {
27882         //Roo.log('tab - prev default');
27883         ev.preventDefault();
27884         
27885         
27886         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27887         pane.tab.addClass('active');
27888         //Roo.log(pane.title);
27889         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27890         // technically we should have a deactivate event.. but maybe add later.
27891         // and it should not de-activate the selected tab...
27892         this.fireEvent('activatepane', pane);
27893         pane.el.addClass('active');
27894         pane.fireEvent('activate');
27895         
27896         
27897     },
27898     
27899     getActivePane : function()
27900     {
27901         var r = false;
27902         Roo.each(this.panes, function(p) {
27903             if(p.el.hasClass('active')){
27904                 r = p;
27905                 return false;
27906             }
27907             
27908             return;
27909         });
27910         
27911         return r;
27912     }
27913     
27914     
27915 });
27916
27917  
27918 /*
27919  * - LGPL
27920  *
27921  * Tab pane
27922  * 
27923  */
27924 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27925 /**
27926  * @class Roo.bootstrap.TabPane
27927  * @extends Roo.bootstrap.Component
27928  * Bootstrap TabPane class
27929  * @cfg {Boolean} active (false | true) Default false
27930  * @cfg {String} title title of panel
27931
27932  * 
27933  * @constructor
27934  * Create a new TabPane
27935  * @param {Object} config The config object
27936  */
27937
27938 Roo.bootstrap.dash.TabPane = function(config){
27939     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27940     
27941     this.addEvents({
27942         // raw events
27943         /**
27944          * @event activate
27945          * When a pane is activated
27946          * @param {Roo.bootstrap.dash.TabPane} pane
27947          */
27948         "activate" : true
27949          
27950     });
27951 };
27952
27953 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27954     
27955     active : false,
27956     title : '',
27957     
27958     // the tabBox that this is attached to.
27959     tab : false,
27960      
27961     getAutoCreate : function() 
27962     {
27963         var cfg = {
27964             tag: 'div',
27965             cls: 'tab-pane'
27966         };
27967         
27968         if(this.active){
27969             cfg.cls += ' active';
27970         }
27971         
27972         return cfg;
27973     },
27974     initEvents  : function()
27975     {
27976         //Roo.log('trigger add pane handler');
27977         this.parent().fireEvent('addpane', this)
27978     },
27979     
27980      /**
27981      * Updates the tab title 
27982      * @param {String} html to set the title to.
27983      */
27984     setTitle: function(str)
27985     {
27986         if (!this.tab) {
27987             return;
27988         }
27989         this.title = str;
27990         this.tab.select('a', true).first().dom.innerHTML = str;
27991         
27992     }
27993     
27994     
27995     
27996 });
27997
27998  
27999
28000
28001  /*
28002  * - LGPL
28003  *
28004  * menu
28005  * 
28006  */
28007 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28008
28009 /**
28010  * @class Roo.bootstrap.menu.Menu
28011  * @extends Roo.bootstrap.Component
28012  * Bootstrap Menu class - container for Menu
28013  * @cfg {String} html Text of the menu
28014  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28015  * @cfg {String} icon Font awesome icon
28016  * @cfg {String} pos Menu align to (top | bottom) default bottom
28017  * 
28018  * 
28019  * @constructor
28020  * Create a new Menu
28021  * @param {Object} config The config object
28022  */
28023
28024
28025 Roo.bootstrap.menu.Menu = function(config){
28026     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28027     
28028     this.addEvents({
28029         /**
28030          * @event beforeshow
28031          * Fires before this menu is displayed
28032          * @param {Roo.bootstrap.menu.Menu} this
28033          */
28034         beforeshow : true,
28035         /**
28036          * @event beforehide
28037          * Fires before this menu is hidden
28038          * @param {Roo.bootstrap.menu.Menu} this
28039          */
28040         beforehide : true,
28041         /**
28042          * @event show
28043          * Fires after this menu is displayed
28044          * @param {Roo.bootstrap.menu.Menu} this
28045          */
28046         show : true,
28047         /**
28048          * @event hide
28049          * Fires after this menu is hidden
28050          * @param {Roo.bootstrap.menu.Menu} this
28051          */
28052         hide : true,
28053         /**
28054          * @event click
28055          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28056          * @param {Roo.bootstrap.menu.Menu} this
28057          * @param {Roo.EventObject} e
28058          */
28059         click : true
28060     });
28061     
28062 };
28063
28064 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28065     
28066     submenu : false,
28067     html : '',
28068     weight : 'default',
28069     icon : false,
28070     pos : 'bottom',
28071     
28072     
28073     getChildContainer : function() {
28074         if(this.isSubMenu){
28075             return this.el;
28076         }
28077         
28078         return this.el.select('ul.dropdown-menu', true).first();  
28079     },
28080     
28081     getAutoCreate : function()
28082     {
28083         var text = [
28084             {
28085                 tag : 'span',
28086                 cls : 'roo-menu-text',
28087                 html : this.html
28088             }
28089         ];
28090         
28091         if(this.icon){
28092             text.unshift({
28093                 tag : 'i',
28094                 cls : 'fa ' + this.icon
28095             })
28096         }
28097         
28098         
28099         var cfg = {
28100             tag : 'div',
28101             cls : 'btn-group',
28102             cn : [
28103                 {
28104                     tag : 'button',
28105                     cls : 'dropdown-button btn btn-' + this.weight,
28106                     cn : text
28107                 },
28108                 {
28109                     tag : 'button',
28110                     cls : 'dropdown-toggle btn btn-' + this.weight,
28111                     cn : [
28112                         {
28113                             tag : 'span',
28114                             cls : 'caret'
28115                         }
28116                     ]
28117                 },
28118                 {
28119                     tag : 'ul',
28120                     cls : 'dropdown-menu'
28121                 }
28122             ]
28123             
28124         };
28125         
28126         if(this.pos == 'top'){
28127             cfg.cls += ' dropup';
28128         }
28129         
28130         if(this.isSubMenu){
28131             cfg = {
28132                 tag : 'ul',
28133                 cls : 'dropdown-menu'
28134             }
28135         }
28136         
28137         return cfg;
28138     },
28139     
28140     onRender : function(ct, position)
28141     {
28142         this.isSubMenu = ct.hasClass('dropdown-submenu');
28143         
28144         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28145     },
28146     
28147     initEvents : function() 
28148     {
28149         if(this.isSubMenu){
28150             return;
28151         }
28152         
28153         this.hidden = true;
28154         
28155         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28156         this.triggerEl.on('click', this.onTriggerPress, this);
28157         
28158         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28159         this.buttonEl.on('click', this.onClick, this);
28160         
28161     },
28162     
28163     list : function()
28164     {
28165         if(this.isSubMenu){
28166             return this.el;
28167         }
28168         
28169         return this.el.select('ul.dropdown-menu', true).first();
28170     },
28171     
28172     onClick : function(e)
28173     {
28174         this.fireEvent("click", this, e);
28175     },
28176     
28177     onTriggerPress  : function(e)
28178     {   
28179         if (this.isVisible()) {
28180             this.hide();
28181         } else {
28182             this.show();
28183         }
28184     },
28185     
28186     isVisible : function(){
28187         return !this.hidden;
28188     },
28189     
28190     show : function()
28191     {
28192         this.fireEvent("beforeshow", this);
28193         
28194         this.hidden = false;
28195         this.el.addClass('open');
28196         
28197         Roo.get(document).on("mouseup", this.onMouseUp, this);
28198         
28199         this.fireEvent("show", this);
28200         
28201         
28202     },
28203     
28204     hide : function()
28205     {
28206         this.fireEvent("beforehide", this);
28207         
28208         this.hidden = true;
28209         this.el.removeClass('open');
28210         
28211         Roo.get(document).un("mouseup", this.onMouseUp);
28212         
28213         this.fireEvent("hide", this);
28214     },
28215     
28216     onMouseUp : function()
28217     {
28218         this.hide();
28219     }
28220     
28221 });
28222
28223  
28224  /*
28225  * - LGPL
28226  *
28227  * menu item
28228  * 
28229  */
28230 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28231
28232 /**
28233  * @class Roo.bootstrap.menu.Item
28234  * @extends Roo.bootstrap.Component
28235  * Bootstrap MenuItem class
28236  * @cfg {Boolean} submenu (true | false) default false
28237  * @cfg {String} html text of the item
28238  * @cfg {String} href the link
28239  * @cfg {Boolean} disable (true | false) default false
28240  * @cfg {Boolean} preventDefault (true | false) default true
28241  * @cfg {String} icon Font awesome icon
28242  * @cfg {String} pos Submenu align to (left | right) default right 
28243  * 
28244  * 
28245  * @constructor
28246  * Create a new Item
28247  * @param {Object} config The config object
28248  */
28249
28250
28251 Roo.bootstrap.menu.Item = function(config){
28252     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28253     this.addEvents({
28254         /**
28255          * @event mouseover
28256          * Fires when the mouse is hovering over this menu
28257          * @param {Roo.bootstrap.menu.Item} this
28258          * @param {Roo.EventObject} e
28259          */
28260         mouseover : true,
28261         /**
28262          * @event mouseout
28263          * Fires when the mouse exits this menu
28264          * @param {Roo.bootstrap.menu.Item} this
28265          * @param {Roo.EventObject} e
28266          */
28267         mouseout : true,
28268         // raw events
28269         /**
28270          * @event click
28271          * The raw click event for the entire grid.
28272          * @param {Roo.EventObject} e
28273          */
28274         click : true
28275     });
28276 };
28277
28278 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28279     
28280     submenu : false,
28281     href : '',
28282     html : '',
28283     preventDefault: true,
28284     disable : false,
28285     icon : false,
28286     pos : 'right',
28287     
28288     getAutoCreate : function()
28289     {
28290         var text = [
28291             {
28292                 tag : 'span',
28293                 cls : 'roo-menu-item-text',
28294                 html : this.html
28295             }
28296         ];
28297         
28298         if(this.icon){
28299             text.unshift({
28300                 tag : 'i',
28301                 cls : 'fa ' + this.icon
28302             })
28303         }
28304         
28305         var cfg = {
28306             tag : 'li',
28307             cn : [
28308                 {
28309                     tag : 'a',
28310                     href : this.href || '#',
28311                     cn : text
28312                 }
28313             ]
28314         };
28315         
28316         if(this.disable){
28317             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28318         }
28319         
28320         if(this.submenu){
28321             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28322             
28323             if(this.pos == 'left'){
28324                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28325             }
28326         }
28327         
28328         return cfg;
28329     },
28330     
28331     initEvents : function() 
28332     {
28333         this.el.on('mouseover', this.onMouseOver, this);
28334         this.el.on('mouseout', this.onMouseOut, this);
28335         
28336         this.el.select('a', true).first().on('click', this.onClick, this);
28337         
28338     },
28339     
28340     onClick : function(e)
28341     {
28342         if(this.preventDefault){
28343             e.preventDefault();
28344         }
28345         
28346         this.fireEvent("click", this, e);
28347     },
28348     
28349     onMouseOver : function(e)
28350     {
28351         if(this.submenu && this.pos == 'left'){
28352             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28353         }
28354         
28355         this.fireEvent("mouseover", this, e);
28356     },
28357     
28358     onMouseOut : function(e)
28359     {
28360         this.fireEvent("mouseout", this, e);
28361     }
28362 });
28363
28364  
28365
28366  /*
28367  * - LGPL
28368  *
28369  * menu separator
28370  * 
28371  */
28372 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28373
28374 /**
28375  * @class Roo.bootstrap.menu.Separator
28376  * @extends Roo.bootstrap.Component
28377  * Bootstrap Separator class
28378  * 
28379  * @constructor
28380  * Create a new Separator
28381  * @param {Object} config The config object
28382  */
28383
28384
28385 Roo.bootstrap.menu.Separator = function(config){
28386     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28387 };
28388
28389 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28390     
28391     getAutoCreate : function(){
28392         var cfg = {
28393             tag : 'li',
28394             cls: 'divider'
28395         };
28396         
28397         return cfg;
28398     }
28399    
28400 });
28401
28402  
28403
28404  /*
28405  * - LGPL
28406  *
28407  * Tooltip
28408  * 
28409  */
28410
28411 /**
28412  * @class Roo.bootstrap.Tooltip
28413  * Bootstrap Tooltip class
28414  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28415  * to determine which dom element triggers the tooltip.
28416  * 
28417  * It needs to add support for additional attributes like tooltip-position
28418  * 
28419  * @constructor
28420  * Create a new Toolti
28421  * @param {Object} config The config object
28422  */
28423
28424 Roo.bootstrap.Tooltip = function(config){
28425     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28426     
28427     this.alignment = Roo.bootstrap.Tooltip.alignment;
28428     
28429     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28430         this.alignment = config.alignment;
28431     }
28432     
28433 };
28434
28435 Roo.apply(Roo.bootstrap.Tooltip, {
28436     /**
28437      * @function init initialize tooltip monitoring.
28438      * @static
28439      */
28440     currentEl : false,
28441     currentTip : false,
28442     currentRegion : false,
28443     
28444     //  init : delay?
28445     
28446     init : function()
28447     {
28448         Roo.get(document).on('mouseover', this.enter ,this);
28449         Roo.get(document).on('mouseout', this.leave, this);
28450          
28451         
28452         this.currentTip = new Roo.bootstrap.Tooltip();
28453     },
28454     
28455     enter : function(ev)
28456     {
28457         var dom = ev.getTarget();
28458         
28459         //Roo.log(['enter',dom]);
28460         var el = Roo.fly(dom);
28461         if (this.currentEl) {
28462             //Roo.log(dom);
28463             //Roo.log(this.currentEl);
28464             //Roo.log(this.currentEl.contains(dom));
28465             if (this.currentEl == el) {
28466                 return;
28467             }
28468             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28469                 return;
28470             }
28471
28472         }
28473         
28474         if (this.currentTip.el) {
28475             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28476         }    
28477         //Roo.log(ev);
28478         
28479         if(!el || el.dom == document){
28480             return;
28481         }
28482         
28483         var bindEl = el;
28484         
28485         // you can not look for children, as if el is the body.. then everythign is the child..
28486         if (!el.attr('tooltip')) { //
28487             if (!el.select("[tooltip]").elements.length) {
28488                 return;
28489             }
28490             // is the mouse over this child...?
28491             bindEl = el.select("[tooltip]").first();
28492             var xy = ev.getXY();
28493             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28494                 //Roo.log("not in region.");
28495                 return;
28496             }
28497             //Roo.log("child element over..");
28498             
28499         }
28500         this.currentEl = bindEl;
28501         this.currentTip.bind(bindEl);
28502         this.currentRegion = Roo.lib.Region.getRegion(dom);
28503         this.currentTip.enter();
28504         
28505     },
28506     leave : function(ev)
28507     {
28508         var dom = ev.getTarget();
28509         //Roo.log(['leave',dom]);
28510         if (!this.currentEl) {
28511             return;
28512         }
28513         
28514         
28515         if (dom != this.currentEl.dom) {
28516             return;
28517         }
28518         var xy = ev.getXY();
28519         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28520             return;
28521         }
28522         // only activate leave if mouse cursor is outside... bounding box..
28523         
28524         
28525         
28526         
28527         if (this.currentTip) {
28528             this.currentTip.leave();
28529         }
28530         //Roo.log('clear currentEl');
28531         this.currentEl = false;
28532         
28533         
28534     },
28535     alignment : {
28536         'left' : ['r-l', [-2,0], 'right'],
28537         'right' : ['l-r', [2,0], 'left'],
28538         'bottom' : ['t-b', [0,2], 'top'],
28539         'top' : [ 'b-t', [0,-2], 'bottom']
28540     }
28541     
28542 });
28543
28544
28545 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28546     
28547     
28548     bindEl : false,
28549     
28550     delay : null, // can be { show : 300 , hide: 500}
28551     
28552     timeout : null,
28553     
28554     hoverState : null, //???
28555     
28556     placement : 'bottom', 
28557     
28558     alignment : false,
28559     
28560     getAutoCreate : function(){
28561     
28562         var cfg = {
28563            cls : 'tooltip',   
28564            role : 'tooltip',
28565            cn : [
28566                 {
28567                     cls : 'tooltip-arrow arrow'
28568                 },
28569                 {
28570                     cls : 'tooltip-inner'
28571                 }
28572            ]
28573         };
28574         
28575         return cfg;
28576     },
28577     bind : function(el)
28578     {
28579         this.bindEl = el;
28580     },
28581     
28582     initEvents : function()
28583     {
28584         this.arrowEl = this.el.select('.arrow', true).first();
28585         this.innerEl = this.el.select('.tooltip-inner', true).first();
28586     },
28587     
28588     enter : function () {
28589        
28590         if (this.timeout != null) {
28591             clearTimeout(this.timeout);
28592         }
28593         
28594         this.hoverState = 'in';
28595          //Roo.log("enter - show");
28596         if (!this.delay || !this.delay.show) {
28597             this.show();
28598             return;
28599         }
28600         var _t = this;
28601         this.timeout = setTimeout(function () {
28602             if (_t.hoverState == 'in') {
28603                 _t.show();
28604             }
28605         }, this.delay.show);
28606     },
28607     leave : function()
28608     {
28609         clearTimeout(this.timeout);
28610     
28611         this.hoverState = 'out';
28612          if (!this.delay || !this.delay.hide) {
28613             this.hide();
28614             return;
28615         }
28616        
28617         var _t = this;
28618         this.timeout = setTimeout(function () {
28619             //Roo.log("leave - timeout");
28620             
28621             if (_t.hoverState == 'out') {
28622                 _t.hide();
28623                 Roo.bootstrap.Tooltip.currentEl = false;
28624             }
28625         }, delay);
28626     },
28627     
28628     show : function (msg)
28629     {
28630         if (!this.el) {
28631             this.render(document.body);
28632         }
28633         // set content.
28634         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28635         
28636         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28637         
28638         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28639         
28640         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28641                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28642         
28643         var placement = typeof this.placement == 'function' ?
28644             this.placement.call(this, this.el, on_el) :
28645             this.placement;
28646             
28647         var autoToken = /\s?auto?\s?/i;
28648         var autoPlace = autoToken.test(placement);
28649         if (autoPlace) {
28650             placement = placement.replace(autoToken, '') || 'top';
28651         }
28652         
28653         //this.el.detach()
28654         //this.el.setXY([0,0]);
28655         this.el.show();
28656         //this.el.dom.style.display='block';
28657         
28658         //this.el.appendTo(on_el);
28659         
28660         var p = this.getPosition();
28661         var box = this.el.getBox();
28662         
28663         if (autoPlace) {
28664             // fixme..
28665         }
28666         
28667         var align = this.alignment[placement];
28668         
28669         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28670         
28671         if(placement == 'top' || placement == 'bottom'){
28672             if(xy[0] < 0){
28673                 placement = 'right';
28674             }
28675             
28676             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28677                 placement = 'left';
28678             }
28679             
28680             var scroll = Roo.select('body', true).first().getScroll();
28681             
28682             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28683                 placement = 'top';
28684             }
28685             
28686             align = this.alignment[placement];
28687             
28688             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28689             
28690         }
28691         
28692         this.el.alignTo(this.bindEl, align[0],align[1]);
28693         //var arrow = this.el.select('.arrow',true).first();
28694         //arrow.set(align[2], 
28695         
28696         this.el.addClass(placement);
28697         this.el.addClass("bs-tooltip-"+ placement);
28698         
28699         this.el.addClass('in fade show');
28700         
28701         this.hoverState = null;
28702         
28703         if (this.el.hasClass('fade')) {
28704             // fade it?
28705         }
28706         
28707         
28708         
28709         
28710         
28711     },
28712     hide : function()
28713     {
28714          
28715         if (!this.el) {
28716             return;
28717         }
28718         //this.el.setXY([0,0]);
28719         this.el.removeClass(['show', 'in']);
28720         //this.el.hide();
28721         
28722     }
28723     
28724 });
28725  
28726
28727  /*
28728  * - LGPL
28729  *
28730  * Location Picker
28731  * 
28732  */
28733
28734 /**
28735  * @class Roo.bootstrap.LocationPicker
28736  * @extends Roo.bootstrap.Component
28737  * Bootstrap LocationPicker class
28738  * @cfg {Number} latitude Position when init default 0
28739  * @cfg {Number} longitude Position when init default 0
28740  * @cfg {Number} zoom default 15
28741  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28742  * @cfg {Boolean} mapTypeControl default false
28743  * @cfg {Boolean} disableDoubleClickZoom default false
28744  * @cfg {Boolean} scrollwheel default true
28745  * @cfg {Boolean} streetViewControl default false
28746  * @cfg {Number} radius default 0
28747  * @cfg {String} locationName
28748  * @cfg {Boolean} draggable default true
28749  * @cfg {Boolean} enableAutocomplete default false
28750  * @cfg {Boolean} enableReverseGeocode default true
28751  * @cfg {String} markerTitle
28752  * 
28753  * @constructor
28754  * Create a new LocationPicker
28755  * @param {Object} config The config object
28756  */
28757
28758
28759 Roo.bootstrap.LocationPicker = function(config){
28760     
28761     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28762     
28763     this.addEvents({
28764         /**
28765          * @event initial
28766          * Fires when the picker initialized.
28767          * @param {Roo.bootstrap.LocationPicker} this
28768          * @param {Google Location} location
28769          */
28770         initial : true,
28771         /**
28772          * @event positionchanged
28773          * Fires when the picker position changed.
28774          * @param {Roo.bootstrap.LocationPicker} this
28775          * @param {Google Location} location
28776          */
28777         positionchanged : true,
28778         /**
28779          * @event resize
28780          * Fires when the map resize.
28781          * @param {Roo.bootstrap.LocationPicker} this
28782          */
28783         resize : true,
28784         /**
28785          * @event show
28786          * Fires when the map show.
28787          * @param {Roo.bootstrap.LocationPicker} this
28788          */
28789         show : true,
28790         /**
28791          * @event hide
28792          * Fires when the map hide.
28793          * @param {Roo.bootstrap.LocationPicker} this
28794          */
28795         hide : true,
28796         /**
28797          * @event mapClick
28798          * Fires when click the map.
28799          * @param {Roo.bootstrap.LocationPicker} this
28800          * @param {Map event} e
28801          */
28802         mapClick : true,
28803         /**
28804          * @event mapRightClick
28805          * Fires when right click the map.
28806          * @param {Roo.bootstrap.LocationPicker} this
28807          * @param {Map event} e
28808          */
28809         mapRightClick : true,
28810         /**
28811          * @event markerClick
28812          * Fires when click the marker.
28813          * @param {Roo.bootstrap.LocationPicker} this
28814          * @param {Map event} e
28815          */
28816         markerClick : true,
28817         /**
28818          * @event markerRightClick
28819          * Fires when right click the marker.
28820          * @param {Roo.bootstrap.LocationPicker} this
28821          * @param {Map event} e
28822          */
28823         markerRightClick : true,
28824         /**
28825          * @event OverlayViewDraw
28826          * Fires when OverlayView Draw
28827          * @param {Roo.bootstrap.LocationPicker} this
28828          */
28829         OverlayViewDraw : true,
28830         /**
28831          * @event OverlayViewOnAdd
28832          * Fires when OverlayView Draw
28833          * @param {Roo.bootstrap.LocationPicker} this
28834          */
28835         OverlayViewOnAdd : true,
28836         /**
28837          * @event OverlayViewOnRemove
28838          * Fires when OverlayView Draw
28839          * @param {Roo.bootstrap.LocationPicker} this
28840          */
28841         OverlayViewOnRemove : true,
28842         /**
28843          * @event OverlayViewShow
28844          * Fires when OverlayView Draw
28845          * @param {Roo.bootstrap.LocationPicker} this
28846          * @param {Pixel} cpx
28847          */
28848         OverlayViewShow : true,
28849         /**
28850          * @event OverlayViewHide
28851          * Fires when OverlayView Draw
28852          * @param {Roo.bootstrap.LocationPicker} this
28853          */
28854         OverlayViewHide : true,
28855         /**
28856          * @event loadexception
28857          * Fires when load google lib failed.
28858          * @param {Roo.bootstrap.LocationPicker} this
28859          */
28860         loadexception : true
28861     });
28862         
28863 };
28864
28865 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28866     
28867     gMapContext: false,
28868     
28869     latitude: 0,
28870     longitude: 0,
28871     zoom: 15,
28872     mapTypeId: false,
28873     mapTypeControl: false,
28874     disableDoubleClickZoom: false,
28875     scrollwheel: true,
28876     streetViewControl: false,
28877     radius: 0,
28878     locationName: '',
28879     draggable: true,
28880     enableAutocomplete: false,
28881     enableReverseGeocode: true,
28882     markerTitle: '',
28883     
28884     getAutoCreate: function()
28885     {
28886
28887         var cfg = {
28888             tag: 'div',
28889             cls: 'roo-location-picker'
28890         };
28891         
28892         return cfg
28893     },
28894     
28895     initEvents: function(ct, position)
28896     {       
28897         if(!this.el.getWidth() || this.isApplied()){
28898             return;
28899         }
28900         
28901         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28902         
28903         this.initial();
28904     },
28905     
28906     initial: function()
28907     {
28908         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28909             this.fireEvent('loadexception', this);
28910             return;
28911         }
28912         
28913         if(!this.mapTypeId){
28914             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28915         }
28916         
28917         this.gMapContext = this.GMapContext();
28918         
28919         this.initOverlayView();
28920         
28921         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28922         
28923         var _this = this;
28924                 
28925         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28926             _this.setPosition(_this.gMapContext.marker.position);
28927         });
28928         
28929         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28930             _this.fireEvent('mapClick', this, event);
28931             
28932         });
28933
28934         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28935             _this.fireEvent('mapRightClick', this, event);
28936             
28937         });
28938         
28939         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28940             _this.fireEvent('markerClick', this, event);
28941             
28942         });
28943
28944         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28945             _this.fireEvent('markerRightClick', this, event);
28946             
28947         });
28948         
28949         this.setPosition(this.gMapContext.location);
28950         
28951         this.fireEvent('initial', this, this.gMapContext.location);
28952     },
28953     
28954     initOverlayView: function()
28955     {
28956         var _this = this;
28957         
28958         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28959             
28960             draw: function()
28961             {
28962                 _this.fireEvent('OverlayViewDraw', _this);
28963             },
28964             
28965             onAdd: function()
28966             {
28967                 _this.fireEvent('OverlayViewOnAdd', _this);
28968             },
28969             
28970             onRemove: function()
28971             {
28972                 _this.fireEvent('OverlayViewOnRemove', _this);
28973             },
28974             
28975             show: function(cpx)
28976             {
28977                 _this.fireEvent('OverlayViewShow', _this, cpx);
28978             },
28979             
28980             hide: function()
28981             {
28982                 _this.fireEvent('OverlayViewHide', _this);
28983             }
28984             
28985         });
28986     },
28987     
28988     fromLatLngToContainerPixel: function(event)
28989     {
28990         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28991     },
28992     
28993     isApplied: function() 
28994     {
28995         return this.getGmapContext() == false ? false : true;
28996     },
28997     
28998     getGmapContext: function() 
28999     {
29000         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29001     },
29002     
29003     GMapContext: function() 
29004     {
29005         var position = new google.maps.LatLng(this.latitude, this.longitude);
29006         
29007         var _map = new google.maps.Map(this.el.dom, {
29008             center: position,
29009             zoom: this.zoom,
29010             mapTypeId: this.mapTypeId,
29011             mapTypeControl: this.mapTypeControl,
29012             disableDoubleClickZoom: this.disableDoubleClickZoom,
29013             scrollwheel: this.scrollwheel,
29014             streetViewControl: this.streetViewControl,
29015             locationName: this.locationName,
29016             draggable: this.draggable,
29017             enableAutocomplete: this.enableAutocomplete,
29018             enableReverseGeocode: this.enableReverseGeocode
29019         });
29020         
29021         var _marker = new google.maps.Marker({
29022             position: position,
29023             map: _map,
29024             title: this.markerTitle,
29025             draggable: this.draggable
29026         });
29027         
29028         return {
29029             map: _map,
29030             marker: _marker,
29031             circle: null,
29032             location: position,
29033             radius: this.radius,
29034             locationName: this.locationName,
29035             addressComponents: {
29036                 formatted_address: null,
29037                 addressLine1: null,
29038                 addressLine2: null,
29039                 streetName: null,
29040                 streetNumber: null,
29041                 city: null,
29042                 district: null,
29043                 state: null,
29044                 stateOrProvince: null
29045             },
29046             settings: this,
29047             domContainer: this.el.dom,
29048             geodecoder: new google.maps.Geocoder()
29049         };
29050     },
29051     
29052     drawCircle: function(center, radius, options) 
29053     {
29054         if (this.gMapContext.circle != null) {
29055             this.gMapContext.circle.setMap(null);
29056         }
29057         if (radius > 0) {
29058             radius *= 1;
29059             options = Roo.apply({}, options, {
29060                 strokeColor: "#0000FF",
29061                 strokeOpacity: .35,
29062                 strokeWeight: 2,
29063                 fillColor: "#0000FF",
29064                 fillOpacity: .2
29065             });
29066             
29067             options.map = this.gMapContext.map;
29068             options.radius = radius;
29069             options.center = center;
29070             this.gMapContext.circle = new google.maps.Circle(options);
29071             return this.gMapContext.circle;
29072         }
29073         
29074         return null;
29075     },
29076     
29077     setPosition: function(location) 
29078     {
29079         this.gMapContext.location = location;
29080         this.gMapContext.marker.setPosition(location);
29081         this.gMapContext.map.panTo(location);
29082         this.drawCircle(location, this.gMapContext.radius, {});
29083         
29084         var _this = this;
29085         
29086         if (this.gMapContext.settings.enableReverseGeocode) {
29087             this.gMapContext.geodecoder.geocode({
29088                 latLng: this.gMapContext.location
29089             }, function(results, status) {
29090                 
29091                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29092                     _this.gMapContext.locationName = results[0].formatted_address;
29093                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29094                     
29095                     _this.fireEvent('positionchanged', this, location);
29096                 }
29097             });
29098             
29099             return;
29100         }
29101         
29102         this.fireEvent('positionchanged', this, location);
29103     },
29104     
29105     resize: function()
29106     {
29107         google.maps.event.trigger(this.gMapContext.map, "resize");
29108         
29109         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29110         
29111         this.fireEvent('resize', this);
29112     },
29113     
29114     setPositionByLatLng: function(latitude, longitude)
29115     {
29116         this.setPosition(new google.maps.LatLng(latitude, longitude));
29117     },
29118     
29119     getCurrentPosition: function() 
29120     {
29121         return {
29122             latitude: this.gMapContext.location.lat(),
29123             longitude: this.gMapContext.location.lng()
29124         };
29125     },
29126     
29127     getAddressName: function() 
29128     {
29129         return this.gMapContext.locationName;
29130     },
29131     
29132     getAddressComponents: function() 
29133     {
29134         return this.gMapContext.addressComponents;
29135     },
29136     
29137     address_component_from_google_geocode: function(address_components) 
29138     {
29139         var result = {};
29140         
29141         for (var i = 0; i < address_components.length; i++) {
29142             var component = address_components[i];
29143             if (component.types.indexOf("postal_code") >= 0) {
29144                 result.postalCode = component.short_name;
29145             } else if (component.types.indexOf("street_number") >= 0) {
29146                 result.streetNumber = component.short_name;
29147             } else if (component.types.indexOf("route") >= 0) {
29148                 result.streetName = component.short_name;
29149             } else if (component.types.indexOf("neighborhood") >= 0) {
29150                 result.city = component.short_name;
29151             } else if (component.types.indexOf("locality") >= 0) {
29152                 result.city = component.short_name;
29153             } else if (component.types.indexOf("sublocality") >= 0) {
29154                 result.district = component.short_name;
29155             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29156                 result.stateOrProvince = component.short_name;
29157             } else if (component.types.indexOf("country") >= 0) {
29158                 result.country = component.short_name;
29159             }
29160         }
29161         
29162         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29163         result.addressLine2 = "";
29164         return result;
29165     },
29166     
29167     setZoomLevel: function(zoom)
29168     {
29169         this.gMapContext.map.setZoom(zoom);
29170     },
29171     
29172     show: function()
29173     {
29174         if(!this.el){
29175             return;
29176         }
29177         
29178         this.el.show();
29179         
29180         this.resize();
29181         
29182         this.fireEvent('show', this);
29183     },
29184     
29185     hide: function()
29186     {
29187         if(!this.el){
29188             return;
29189         }
29190         
29191         this.el.hide();
29192         
29193         this.fireEvent('hide', this);
29194     }
29195     
29196 });
29197
29198 Roo.apply(Roo.bootstrap.LocationPicker, {
29199     
29200     OverlayView : function(map, options)
29201     {
29202         options = options || {};
29203         
29204         this.setMap(map);
29205     }
29206     
29207     
29208 });/**
29209  * @class Roo.bootstrap.Alert
29210  * @extends Roo.bootstrap.Component
29211  * Bootstrap Alert class - shows an alert area box
29212  * eg
29213  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29214   Enter a valid email address
29215 </div>
29216  * @licence LGPL
29217  * @cfg {String} title The title of alert
29218  * @cfg {String} html The content of alert
29219  * @cfg {String} weight (  success | info | warning | danger )
29220  * @cfg {String} faicon font-awesomeicon
29221  * 
29222  * @constructor
29223  * Create a new alert
29224  * @param {Object} config The config object
29225  */
29226
29227
29228 Roo.bootstrap.Alert = function(config){
29229     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29230     
29231 };
29232
29233 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29234     
29235     title: '',
29236     html: '',
29237     weight: false,
29238     faicon: false,
29239     
29240     getAutoCreate : function()
29241     {
29242         
29243         var cfg = {
29244             tag : 'div',
29245             cls : 'alert',
29246             cn : [
29247                 {
29248                     tag : 'i',
29249                     cls : 'roo-alert-icon'
29250                     
29251                 },
29252                 {
29253                     tag : 'b',
29254                     cls : 'roo-alert-title',
29255                     html : this.title
29256                 },
29257                 {
29258                     tag : 'span',
29259                     cls : 'roo-alert-text',
29260                     html : this.html
29261                 }
29262             ]
29263         };
29264         
29265         if(this.faicon){
29266             cfg.cn[0].cls += ' fa ' + this.faicon;
29267         }
29268         
29269         if(this.weight){
29270             cfg.cls += ' alert-' + this.weight;
29271         }
29272         
29273         return cfg;
29274     },
29275     
29276     initEvents: function() 
29277     {
29278         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29279     },
29280     
29281     setTitle : function(str)
29282     {
29283         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29284     },
29285     
29286     setText : function(str)
29287     {
29288         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29289     },
29290     
29291     setWeight : function(weight)
29292     {
29293         if(this.weight){
29294             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29295         }
29296         
29297         this.weight = weight;
29298         
29299         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29300     },
29301     
29302     setIcon : function(icon)
29303     {
29304         if(this.faicon){
29305             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29306         }
29307         
29308         this.faicon = icon;
29309         
29310         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29311     },
29312     
29313     hide: function() 
29314     {
29315         this.el.hide();   
29316     },
29317     
29318     show: function() 
29319     {  
29320         this.el.show();   
29321     }
29322     
29323 });
29324
29325  
29326 /*
29327 * Licence: LGPL
29328 */
29329
29330 /**
29331  * @class Roo.bootstrap.UploadCropbox
29332  * @extends Roo.bootstrap.Component
29333  * Bootstrap UploadCropbox class
29334  * @cfg {String} emptyText show when image has been loaded
29335  * @cfg {String} rotateNotify show when image too small to rotate
29336  * @cfg {Number} errorTimeout default 3000
29337  * @cfg {Number} minWidth default 300
29338  * @cfg {Number} minHeight default 300
29339  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29340  * @cfg {Boolean} isDocument (true|false) default false
29341  * @cfg {String} url action url
29342  * @cfg {String} paramName default 'imageUpload'
29343  * @cfg {String} method default POST
29344  * @cfg {Boolean} loadMask (true|false) default true
29345  * @cfg {Boolean} loadingText default 'Loading...'
29346  * 
29347  * @constructor
29348  * Create a new UploadCropbox
29349  * @param {Object} config The config object
29350  */
29351
29352 Roo.bootstrap.UploadCropbox = function(config){
29353     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29354     
29355     this.addEvents({
29356         /**
29357          * @event beforeselectfile
29358          * Fire before select file
29359          * @param {Roo.bootstrap.UploadCropbox} this
29360          */
29361         "beforeselectfile" : true,
29362         /**
29363          * @event initial
29364          * Fire after initEvent
29365          * @param {Roo.bootstrap.UploadCropbox} this
29366          */
29367         "initial" : true,
29368         /**
29369          * @event crop
29370          * Fire after initEvent
29371          * @param {Roo.bootstrap.UploadCropbox} this
29372          * @param {String} data
29373          */
29374         "crop" : true,
29375         /**
29376          * @event prepare
29377          * Fire when preparing the file data
29378          * @param {Roo.bootstrap.UploadCropbox} this
29379          * @param {Object} file
29380          */
29381         "prepare" : true,
29382         /**
29383          * @event exception
29384          * Fire when get exception
29385          * @param {Roo.bootstrap.UploadCropbox} this
29386          * @param {XMLHttpRequest} xhr
29387          */
29388         "exception" : true,
29389         /**
29390          * @event beforeloadcanvas
29391          * Fire before load the canvas
29392          * @param {Roo.bootstrap.UploadCropbox} this
29393          * @param {String} src
29394          */
29395         "beforeloadcanvas" : true,
29396         /**
29397          * @event trash
29398          * Fire when trash image
29399          * @param {Roo.bootstrap.UploadCropbox} this
29400          */
29401         "trash" : true,
29402         /**
29403          * @event download
29404          * Fire when download the image
29405          * @param {Roo.bootstrap.UploadCropbox} this
29406          */
29407         "download" : true,
29408         /**
29409          * @event footerbuttonclick
29410          * Fire when footerbuttonclick
29411          * @param {Roo.bootstrap.UploadCropbox} this
29412          * @param {String} type
29413          */
29414         "footerbuttonclick" : true,
29415         /**
29416          * @event resize
29417          * Fire when resize
29418          * @param {Roo.bootstrap.UploadCropbox} this
29419          */
29420         "resize" : true,
29421         /**
29422          * @event rotate
29423          * Fire when rotate the image
29424          * @param {Roo.bootstrap.UploadCropbox} this
29425          * @param {String} pos
29426          */
29427         "rotate" : true,
29428         /**
29429          * @event inspect
29430          * Fire when inspect the file
29431          * @param {Roo.bootstrap.UploadCropbox} this
29432          * @param {Object} file
29433          */
29434         "inspect" : true,
29435         /**
29436          * @event upload
29437          * Fire when xhr upload the file
29438          * @param {Roo.bootstrap.UploadCropbox} this
29439          * @param {Object} data
29440          */
29441         "upload" : true,
29442         /**
29443          * @event arrange
29444          * Fire when arrange the file data
29445          * @param {Roo.bootstrap.UploadCropbox} this
29446          * @param {Object} formData
29447          */
29448         "arrange" : true
29449     });
29450     
29451     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29452 };
29453
29454 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29455     
29456     emptyText : 'Click to upload image',
29457     rotateNotify : 'Image is too small to rotate',
29458     errorTimeout : 3000,
29459     scale : 0,
29460     baseScale : 1,
29461     rotate : 0,
29462     dragable : false,
29463     pinching : false,
29464     mouseX : 0,
29465     mouseY : 0,
29466     cropData : false,
29467     minWidth : 300,
29468     minHeight : 300,
29469     file : false,
29470     exif : {},
29471     baseRotate : 1,
29472     cropType : 'image/jpeg',
29473     buttons : false,
29474     canvasLoaded : false,
29475     isDocument : false,
29476     method : 'POST',
29477     paramName : 'imageUpload',
29478     loadMask : true,
29479     loadingText : 'Loading...',
29480     maskEl : false,
29481     
29482     getAutoCreate : function()
29483     {
29484         var cfg = {
29485             tag : 'div',
29486             cls : 'roo-upload-cropbox',
29487             cn : [
29488                 {
29489                     tag : 'input',
29490                     cls : 'roo-upload-cropbox-selector',
29491                     type : 'file'
29492                 },
29493                 {
29494                     tag : 'div',
29495                     cls : 'roo-upload-cropbox-body',
29496                     style : 'cursor:pointer',
29497                     cn : [
29498                         {
29499                             tag : 'div',
29500                             cls : 'roo-upload-cropbox-preview'
29501                         },
29502                         {
29503                             tag : 'div',
29504                             cls : 'roo-upload-cropbox-thumb'
29505                         },
29506                         {
29507                             tag : 'div',
29508                             cls : 'roo-upload-cropbox-empty-notify',
29509                             html : this.emptyText
29510                         },
29511                         {
29512                             tag : 'div',
29513                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29514                             html : this.rotateNotify
29515                         }
29516                     ]
29517                 },
29518                 {
29519                     tag : 'div',
29520                     cls : 'roo-upload-cropbox-footer',
29521                     cn : {
29522                         tag : 'div',
29523                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29524                         cn : []
29525                     }
29526                 }
29527             ]
29528         };
29529         
29530         return cfg;
29531     },
29532     
29533     onRender : function(ct, position)
29534     {
29535         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29536         
29537         if (this.buttons.length) {
29538             
29539             Roo.each(this.buttons, function(bb) {
29540                 
29541                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29542                 
29543                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29544                 
29545             }, this);
29546         }
29547         
29548         if(this.loadMask){
29549             this.maskEl = this.el;
29550         }
29551     },
29552     
29553     initEvents : function()
29554     {
29555         this.urlAPI = (window.createObjectURL && window) || 
29556                                 (window.URL && URL.revokeObjectURL && URL) || 
29557                                 (window.webkitURL && webkitURL);
29558                         
29559         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29560         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29561         
29562         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29563         this.selectorEl.hide();
29564         
29565         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29566         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29567         
29568         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29569         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29570         this.thumbEl.hide();
29571         
29572         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29573         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29574         
29575         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29576         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29577         this.errorEl.hide();
29578         
29579         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29580         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29581         this.footerEl.hide();
29582         
29583         this.setThumbBoxSize();
29584         
29585         this.bind();
29586         
29587         this.resize();
29588         
29589         this.fireEvent('initial', this);
29590     },
29591
29592     bind : function()
29593     {
29594         var _this = this;
29595         
29596         window.addEventListener("resize", function() { _this.resize(); } );
29597         
29598         this.bodyEl.on('click', this.beforeSelectFile, this);
29599         
29600         if(Roo.isTouch){
29601             this.bodyEl.on('touchstart', this.onTouchStart, this);
29602             this.bodyEl.on('touchmove', this.onTouchMove, this);
29603             this.bodyEl.on('touchend', this.onTouchEnd, this);
29604         }
29605         
29606         if(!Roo.isTouch){
29607             this.bodyEl.on('mousedown', this.onMouseDown, this);
29608             this.bodyEl.on('mousemove', this.onMouseMove, this);
29609             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29610             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29611             Roo.get(document).on('mouseup', this.onMouseUp, this);
29612         }
29613         
29614         this.selectorEl.on('change', this.onFileSelected, this);
29615     },
29616     
29617     reset : function()
29618     {    
29619         this.scale = 0;
29620         this.baseScale = 1;
29621         this.rotate = 0;
29622         this.baseRotate = 1;
29623         this.dragable = false;
29624         this.pinching = false;
29625         this.mouseX = 0;
29626         this.mouseY = 0;
29627         this.cropData = false;
29628         this.notifyEl.dom.innerHTML = this.emptyText;
29629         
29630         this.selectorEl.dom.value = '';
29631         
29632     },
29633     
29634     resize : function()
29635     {
29636         if(this.fireEvent('resize', this) != false){
29637             this.setThumbBoxPosition();
29638             this.setCanvasPosition();
29639         }
29640     },
29641     
29642     onFooterButtonClick : function(e, el, o, type)
29643     {
29644         switch (type) {
29645             case 'rotate-left' :
29646                 this.onRotateLeft(e);
29647                 break;
29648             case 'rotate-right' :
29649                 this.onRotateRight(e);
29650                 break;
29651             case 'picture' :
29652                 this.beforeSelectFile(e);
29653                 break;
29654             case 'trash' :
29655                 this.trash(e);
29656                 break;
29657             case 'crop' :
29658                 this.crop(e);
29659                 break;
29660             case 'download' :
29661                 this.download(e);
29662                 break;
29663             default :
29664                 break;
29665         }
29666         
29667         this.fireEvent('footerbuttonclick', this, type);
29668     },
29669     
29670     beforeSelectFile : function(e)
29671     {
29672         e.preventDefault();
29673         
29674         if(this.fireEvent('beforeselectfile', this) != false){
29675             this.selectorEl.dom.click();
29676         }
29677     },
29678     
29679     onFileSelected : function(e)
29680     {
29681         e.preventDefault();
29682         
29683         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29684             return;
29685         }
29686         
29687         var file = this.selectorEl.dom.files[0];
29688         
29689         if(this.fireEvent('inspect', this, file) != false){
29690             this.prepare(file);
29691         }
29692         
29693     },
29694     
29695     trash : function(e)
29696     {
29697         this.fireEvent('trash', this);
29698     },
29699     
29700     download : function(e)
29701     {
29702         this.fireEvent('download', this);
29703     },
29704     
29705     loadCanvas : function(src)
29706     {   
29707         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29708             
29709             this.reset();
29710             
29711             this.imageEl = document.createElement('img');
29712             
29713             var _this = this;
29714             
29715             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29716             
29717             this.imageEl.src = src;
29718         }
29719     },
29720     
29721     onLoadCanvas : function()
29722     {   
29723         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29724         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29725         
29726         this.bodyEl.un('click', this.beforeSelectFile, this);
29727         
29728         this.notifyEl.hide();
29729         this.thumbEl.show();
29730         this.footerEl.show();
29731         
29732         this.baseRotateLevel();
29733         
29734         if(this.isDocument){
29735             this.setThumbBoxSize();
29736         }
29737         
29738         this.setThumbBoxPosition();
29739         
29740         this.baseScaleLevel();
29741         
29742         this.draw();
29743         
29744         this.resize();
29745         
29746         this.canvasLoaded = true;
29747         
29748         if(this.loadMask){
29749             this.maskEl.unmask();
29750         }
29751         
29752     },
29753     
29754     setCanvasPosition : function()
29755     {   
29756         if(!this.canvasEl){
29757             return;
29758         }
29759         
29760         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29761         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29762         
29763         this.previewEl.setLeft(pw);
29764         this.previewEl.setTop(ph);
29765         
29766     },
29767     
29768     onMouseDown : function(e)
29769     {   
29770         e.stopEvent();
29771         
29772         this.dragable = true;
29773         this.pinching = false;
29774         
29775         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29776             this.dragable = false;
29777             return;
29778         }
29779         
29780         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29781         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29782         
29783     },
29784     
29785     onMouseMove : function(e)
29786     {   
29787         e.stopEvent();
29788         
29789         if(!this.canvasLoaded){
29790             return;
29791         }
29792         
29793         if (!this.dragable){
29794             return;
29795         }
29796         
29797         var minX = Math.ceil(this.thumbEl.getLeft(true));
29798         var minY = Math.ceil(this.thumbEl.getTop(true));
29799         
29800         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29801         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29802         
29803         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29804         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29805         
29806         x = x - this.mouseX;
29807         y = y - this.mouseY;
29808         
29809         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29810         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29811         
29812         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29813         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29814         
29815         this.previewEl.setLeft(bgX);
29816         this.previewEl.setTop(bgY);
29817         
29818         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29819         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29820     },
29821     
29822     onMouseUp : function(e)
29823     {   
29824         e.stopEvent();
29825         
29826         this.dragable = false;
29827     },
29828     
29829     onMouseWheel : function(e)
29830     {   
29831         e.stopEvent();
29832         
29833         this.startScale = this.scale;
29834         
29835         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29836         
29837         if(!this.zoomable()){
29838             this.scale = this.startScale;
29839             return;
29840         }
29841         
29842         this.draw();
29843         
29844         return;
29845     },
29846     
29847     zoomable : function()
29848     {
29849         var minScale = this.thumbEl.getWidth() / this.minWidth;
29850         
29851         if(this.minWidth < this.minHeight){
29852             minScale = this.thumbEl.getHeight() / this.minHeight;
29853         }
29854         
29855         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29856         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29857         
29858         if(
29859                 this.isDocument &&
29860                 (this.rotate == 0 || this.rotate == 180) && 
29861                 (
29862                     width > this.imageEl.OriginWidth || 
29863                     height > this.imageEl.OriginHeight ||
29864                     (width < this.minWidth && height < this.minHeight)
29865                 )
29866         ){
29867             return false;
29868         }
29869         
29870         if(
29871                 this.isDocument &&
29872                 (this.rotate == 90 || this.rotate == 270) && 
29873                 (
29874                     width > this.imageEl.OriginWidth || 
29875                     height > this.imageEl.OriginHeight ||
29876                     (width < this.minHeight && height < this.minWidth)
29877                 )
29878         ){
29879             return false;
29880         }
29881         
29882         if(
29883                 !this.isDocument &&
29884                 (this.rotate == 0 || this.rotate == 180) && 
29885                 (
29886                     width < this.minWidth || 
29887                     width > this.imageEl.OriginWidth || 
29888                     height < this.minHeight || 
29889                     height > this.imageEl.OriginHeight
29890                 )
29891         ){
29892             return false;
29893         }
29894         
29895         if(
29896                 !this.isDocument &&
29897                 (this.rotate == 90 || this.rotate == 270) && 
29898                 (
29899                     width < this.minHeight || 
29900                     width > this.imageEl.OriginWidth || 
29901                     height < this.minWidth || 
29902                     height > this.imageEl.OriginHeight
29903                 )
29904         ){
29905             return false;
29906         }
29907         
29908         return true;
29909         
29910     },
29911     
29912     onRotateLeft : function(e)
29913     {   
29914         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29915             
29916             var minScale = this.thumbEl.getWidth() / this.minWidth;
29917             
29918             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29919             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29920             
29921             this.startScale = this.scale;
29922             
29923             while (this.getScaleLevel() < minScale){
29924             
29925                 this.scale = this.scale + 1;
29926                 
29927                 if(!this.zoomable()){
29928                     break;
29929                 }
29930                 
29931                 if(
29932                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29933                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29934                 ){
29935                     continue;
29936                 }
29937                 
29938                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29939
29940                 this.draw();
29941                 
29942                 return;
29943             }
29944             
29945             this.scale = this.startScale;
29946             
29947             this.onRotateFail();
29948             
29949             return false;
29950         }
29951         
29952         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29953
29954         if(this.isDocument){
29955             this.setThumbBoxSize();
29956             this.setThumbBoxPosition();
29957             this.setCanvasPosition();
29958         }
29959         
29960         this.draw();
29961         
29962         this.fireEvent('rotate', this, 'left');
29963         
29964     },
29965     
29966     onRotateRight : function(e)
29967     {
29968         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29969             
29970             var minScale = this.thumbEl.getWidth() / this.minWidth;
29971         
29972             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29973             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29974             
29975             this.startScale = this.scale;
29976             
29977             while (this.getScaleLevel() < minScale){
29978             
29979                 this.scale = this.scale + 1;
29980                 
29981                 if(!this.zoomable()){
29982                     break;
29983                 }
29984                 
29985                 if(
29986                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29987                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29988                 ){
29989                     continue;
29990                 }
29991                 
29992                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29993
29994                 this.draw();
29995                 
29996                 return;
29997             }
29998             
29999             this.scale = this.startScale;
30000             
30001             this.onRotateFail();
30002             
30003             return false;
30004         }
30005         
30006         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30007
30008         if(this.isDocument){
30009             this.setThumbBoxSize();
30010             this.setThumbBoxPosition();
30011             this.setCanvasPosition();
30012         }
30013         
30014         this.draw();
30015         
30016         this.fireEvent('rotate', this, 'right');
30017     },
30018     
30019     onRotateFail : function()
30020     {
30021         this.errorEl.show(true);
30022         
30023         var _this = this;
30024         
30025         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30026     },
30027     
30028     draw : function()
30029     {
30030         this.previewEl.dom.innerHTML = '';
30031         
30032         var canvasEl = document.createElement("canvas");
30033         
30034         var contextEl = canvasEl.getContext("2d");
30035         
30036         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30037         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30038         var center = this.imageEl.OriginWidth / 2;
30039         
30040         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30041             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30042             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30043             center = this.imageEl.OriginHeight / 2;
30044         }
30045         
30046         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30047         
30048         contextEl.translate(center, center);
30049         contextEl.rotate(this.rotate * Math.PI / 180);
30050
30051         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30052         
30053         this.canvasEl = document.createElement("canvas");
30054         
30055         this.contextEl = this.canvasEl.getContext("2d");
30056         
30057         switch (this.rotate) {
30058             case 0 :
30059                 
30060                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30061                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30062                 
30063                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30064                 
30065                 break;
30066             case 90 : 
30067                 
30068                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30069                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30070                 
30071                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30072                     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);
30073                     break;
30074                 }
30075                 
30076                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30077                 
30078                 break;
30079             case 180 :
30080                 
30081                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30082                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30083                 
30084                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30085                     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);
30086                     break;
30087                 }
30088                 
30089                 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);
30090                 
30091                 break;
30092             case 270 :
30093                 
30094                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30095                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30096         
30097                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30098                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30099                     break;
30100                 }
30101                 
30102                 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);
30103                 
30104                 break;
30105             default : 
30106                 break;
30107         }
30108         
30109         this.previewEl.appendChild(this.canvasEl);
30110         
30111         this.setCanvasPosition();
30112     },
30113     
30114     crop : function()
30115     {
30116         if(!this.canvasLoaded){
30117             return;
30118         }
30119         
30120         var imageCanvas = document.createElement("canvas");
30121         
30122         var imageContext = imageCanvas.getContext("2d");
30123         
30124         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30125         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30126         
30127         var center = imageCanvas.width / 2;
30128         
30129         imageContext.translate(center, center);
30130         
30131         imageContext.rotate(this.rotate * Math.PI / 180);
30132         
30133         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30134         
30135         var canvas = document.createElement("canvas");
30136         
30137         var context = canvas.getContext("2d");
30138                 
30139         canvas.width = this.minWidth;
30140         canvas.height = this.minHeight;
30141
30142         switch (this.rotate) {
30143             case 0 :
30144                 
30145                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30146                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30147                 
30148                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30149                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30150                 
30151                 var targetWidth = this.minWidth - 2 * x;
30152                 var targetHeight = this.minHeight - 2 * y;
30153                 
30154                 var scale = 1;
30155                 
30156                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30157                     scale = targetWidth / width;
30158                 }
30159                 
30160                 if(x > 0 && y == 0){
30161                     scale = targetHeight / height;
30162                 }
30163                 
30164                 if(x > 0 && y > 0){
30165                     scale = targetWidth / width;
30166                     
30167                     if(width < height){
30168                         scale = targetHeight / height;
30169                     }
30170                 }
30171                 
30172                 context.scale(scale, scale);
30173                 
30174                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30175                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30176
30177                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30178                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30179
30180                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30181                 
30182                 break;
30183             case 90 : 
30184                 
30185                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30186                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30187                 
30188                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30189                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30190                 
30191                 var targetWidth = this.minWidth - 2 * x;
30192                 var targetHeight = this.minHeight - 2 * y;
30193                 
30194                 var scale = 1;
30195                 
30196                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30197                     scale = targetWidth / width;
30198                 }
30199                 
30200                 if(x > 0 && y == 0){
30201                     scale = targetHeight / height;
30202                 }
30203                 
30204                 if(x > 0 && y > 0){
30205                     scale = targetWidth / width;
30206                     
30207                     if(width < height){
30208                         scale = targetHeight / height;
30209                     }
30210                 }
30211                 
30212                 context.scale(scale, scale);
30213                 
30214                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30215                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30216
30217                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30218                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30219                 
30220                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30221                 
30222                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30223                 
30224                 break;
30225             case 180 :
30226                 
30227                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30228                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30229                 
30230                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30231                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30232                 
30233                 var targetWidth = this.minWidth - 2 * x;
30234                 var targetHeight = this.minHeight - 2 * y;
30235                 
30236                 var scale = 1;
30237                 
30238                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30239                     scale = targetWidth / width;
30240                 }
30241                 
30242                 if(x > 0 && y == 0){
30243                     scale = targetHeight / height;
30244                 }
30245                 
30246                 if(x > 0 && y > 0){
30247                     scale = targetWidth / width;
30248                     
30249                     if(width < height){
30250                         scale = targetHeight / height;
30251                     }
30252                 }
30253                 
30254                 context.scale(scale, scale);
30255                 
30256                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30257                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30258
30259                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30260                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30261
30262                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30263                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30264                 
30265                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30266                 
30267                 break;
30268             case 270 :
30269                 
30270                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30271                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30272                 
30273                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30274                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30275                 
30276                 var targetWidth = this.minWidth - 2 * x;
30277                 var targetHeight = this.minHeight - 2 * y;
30278                 
30279                 var scale = 1;
30280                 
30281                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30282                     scale = targetWidth / width;
30283                 }
30284                 
30285                 if(x > 0 && y == 0){
30286                     scale = targetHeight / height;
30287                 }
30288                 
30289                 if(x > 0 && y > 0){
30290                     scale = targetWidth / width;
30291                     
30292                     if(width < height){
30293                         scale = targetHeight / height;
30294                     }
30295                 }
30296                 
30297                 context.scale(scale, scale);
30298                 
30299                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30300                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30301
30302                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30303                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30304                 
30305                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30306                 
30307                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30308                 
30309                 break;
30310             default : 
30311                 break;
30312         }
30313         
30314         this.cropData = canvas.toDataURL(this.cropType);
30315         
30316         if(this.fireEvent('crop', this, this.cropData) !== false){
30317             this.process(this.file, this.cropData);
30318         }
30319         
30320         return;
30321         
30322     },
30323     
30324     setThumbBoxSize : function()
30325     {
30326         var width, height;
30327         
30328         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30329             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30330             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30331             
30332             this.minWidth = width;
30333             this.minHeight = height;
30334             
30335             if(this.rotate == 90 || this.rotate == 270){
30336                 this.minWidth = height;
30337                 this.minHeight = width;
30338             }
30339         }
30340         
30341         height = 300;
30342         width = Math.ceil(this.minWidth * height / this.minHeight);
30343         
30344         if(this.minWidth > this.minHeight){
30345             width = 300;
30346             height = Math.ceil(this.minHeight * width / this.minWidth);
30347         }
30348         
30349         this.thumbEl.setStyle({
30350             width : width + 'px',
30351             height : height + 'px'
30352         });
30353
30354         return;
30355             
30356     },
30357     
30358     setThumbBoxPosition : function()
30359     {
30360         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30361         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30362         
30363         this.thumbEl.setLeft(x);
30364         this.thumbEl.setTop(y);
30365         
30366     },
30367     
30368     baseRotateLevel : function()
30369     {
30370         this.baseRotate = 1;
30371         
30372         if(
30373                 typeof(this.exif) != 'undefined' &&
30374                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30375                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30376         ){
30377             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30378         }
30379         
30380         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30381         
30382     },
30383     
30384     baseScaleLevel : function()
30385     {
30386         var width, height;
30387         
30388         if(this.isDocument){
30389             
30390             if(this.baseRotate == 6 || this.baseRotate == 8){
30391             
30392                 height = this.thumbEl.getHeight();
30393                 this.baseScale = height / this.imageEl.OriginWidth;
30394
30395                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30396                     width = this.thumbEl.getWidth();
30397                     this.baseScale = width / this.imageEl.OriginHeight;
30398                 }
30399
30400                 return;
30401             }
30402
30403             height = this.thumbEl.getHeight();
30404             this.baseScale = height / this.imageEl.OriginHeight;
30405
30406             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30407                 width = this.thumbEl.getWidth();
30408                 this.baseScale = width / this.imageEl.OriginWidth;
30409             }
30410
30411             return;
30412         }
30413         
30414         if(this.baseRotate == 6 || this.baseRotate == 8){
30415             
30416             width = this.thumbEl.getHeight();
30417             this.baseScale = width / this.imageEl.OriginHeight;
30418             
30419             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30420                 height = this.thumbEl.getWidth();
30421                 this.baseScale = height / this.imageEl.OriginHeight;
30422             }
30423             
30424             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30425                 height = this.thumbEl.getWidth();
30426                 this.baseScale = height / this.imageEl.OriginHeight;
30427                 
30428                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30429                     width = this.thumbEl.getHeight();
30430                     this.baseScale = width / this.imageEl.OriginWidth;
30431                 }
30432             }
30433             
30434             return;
30435         }
30436         
30437         width = this.thumbEl.getWidth();
30438         this.baseScale = width / this.imageEl.OriginWidth;
30439         
30440         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30441             height = this.thumbEl.getHeight();
30442             this.baseScale = height / this.imageEl.OriginHeight;
30443         }
30444         
30445         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30446             
30447             height = this.thumbEl.getHeight();
30448             this.baseScale = height / this.imageEl.OriginHeight;
30449             
30450             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30451                 width = this.thumbEl.getWidth();
30452                 this.baseScale = width / this.imageEl.OriginWidth;
30453             }
30454             
30455         }
30456         
30457         return;
30458     },
30459     
30460     getScaleLevel : function()
30461     {
30462         return this.baseScale * Math.pow(1.1, this.scale);
30463     },
30464     
30465     onTouchStart : function(e)
30466     {
30467         if(!this.canvasLoaded){
30468             this.beforeSelectFile(e);
30469             return;
30470         }
30471         
30472         var touches = e.browserEvent.touches;
30473         
30474         if(!touches){
30475             return;
30476         }
30477         
30478         if(touches.length == 1){
30479             this.onMouseDown(e);
30480             return;
30481         }
30482         
30483         if(touches.length != 2){
30484             return;
30485         }
30486         
30487         var coords = [];
30488         
30489         for(var i = 0, finger; finger = touches[i]; i++){
30490             coords.push(finger.pageX, finger.pageY);
30491         }
30492         
30493         var x = Math.pow(coords[0] - coords[2], 2);
30494         var y = Math.pow(coords[1] - coords[3], 2);
30495         
30496         this.startDistance = Math.sqrt(x + y);
30497         
30498         this.startScale = this.scale;
30499         
30500         this.pinching = true;
30501         this.dragable = false;
30502         
30503     },
30504     
30505     onTouchMove : function(e)
30506     {
30507         if(!this.pinching && !this.dragable){
30508             return;
30509         }
30510         
30511         var touches = e.browserEvent.touches;
30512         
30513         if(!touches){
30514             return;
30515         }
30516         
30517         if(this.dragable){
30518             this.onMouseMove(e);
30519             return;
30520         }
30521         
30522         var coords = [];
30523         
30524         for(var i = 0, finger; finger = touches[i]; i++){
30525             coords.push(finger.pageX, finger.pageY);
30526         }
30527         
30528         var x = Math.pow(coords[0] - coords[2], 2);
30529         var y = Math.pow(coords[1] - coords[3], 2);
30530         
30531         this.endDistance = Math.sqrt(x + y);
30532         
30533         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30534         
30535         if(!this.zoomable()){
30536             this.scale = this.startScale;
30537             return;
30538         }
30539         
30540         this.draw();
30541         
30542     },
30543     
30544     onTouchEnd : function(e)
30545     {
30546         this.pinching = false;
30547         this.dragable = false;
30548         
30549     },
30550     
30551     process : function(file, crop)
30552     {
30553         if(this.loadMask){
30554             this.maskEl.mask(this.loadingText);
30555         }
30556         
30557         this.xhr = new XMLHttpRequest();
30558         
30559         file.xhr = this.xhr;
30560
30561         this.xhr.open(this.method, this.url, true);
30562         
30563         var headers = {
30564             "Accept": "application/json",
30565             "Cache-Control": "no-cache",
30566             "X-Requested-With": "XMLHttpRequest"
30567         };
30568         
30569         for (var headerName in headers) {
30570             var headerValue = headers[headerName];
30571             if (headerValue) {
30572                 this.xhr.setRequestHeader(headerName, headerValue);
30573             }
30574         }
30575         
30576         var _this = this;
30577         
30578         this.xhr.onload = function()
30579         {
30580             _this.xhrOnLoad(_this.xhr);
30581         }
30582         
30583         this.xhr.onerror = function()
30584         {
30585             _this.xhrOnError(_this.xhr);
30586         }
30587         
30588         var formData = new FormData();
30589
30590         formData.append('returnHTML', 'NO');
30591         
30592         if(crop){
30593             formData.append('crop', crop);
30594         }
30595         
30596         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30597             formData.append(this.paramName, file, file.name);
30598         }
30599         
30600         if(typeof(file.filename) != 'undefined'){
30601             formData.append('filename', file.filename);
30602         }
30603         
30604         if(typeof(file.mimetype) != 'undefined'){
30605             formData.append('mimetype', file.mimetype);
30606         }
30607         
30608         if(this.fireEvent('arrange', this, formData) != false){
30609             this.xhr.send(formData);
30610         };
30611     },
30612     
30613     xhrOnLoad : function(xhr)
30614     {
30615         if(this.loadMask){
30616             this.maskEl.unmask();
30617         }
30618         
30619         if (xhr.readyState !== 4) {
30620             this.fireEvent('exception', this, xhr);
30621             return;
30622         }
30623
30624         var response = Roo.decode(xhr.responseText);
30625         
30626         if(!response.success){
30627             this.fireEvent('exception', this, xhr);
30628             return;
30629         }
30630         
30631         var response = Roo.decode(xhr.responseText);
30632         
30633         this.fireEvent('upload', this, response);
30634         
30635     },
30636     
30637     xhrOnError : function()
30638     {
30639         if(this.loadMask){
30640             this.maskEl.unmask();
30641         }
30642         
30643         Roo.log('xhr on error');
30644         
30645         var response = Roo.decode(xhr.responseText);
30646           
30647         Roo.log(response);
30648         
30649     },
30650     
30651     prepare : function(file)
30652     {   
30653         if(this.loadMask){
30654             this.maskEl.mask(this.loadingText);
30655         }
30656         
30657         this.file = false;
30658         this.exif = {};
30659         
30660         if(typeof(file) === 'string'){
30661             this.loadCanvas(file);
30662             return;
30663         }
30664         
30665         if(!file || !this.urlAPI){
30666             return;
30667         }
30668         
30669         this.file = file;
30670         this.cropType = file.type;
30671         
30672         var _this = this;
30673         
30674         if(this.fireEvent('prepare', this, this.file) != false){
30675             
30676             var reader = new FileReader();
30677             
30678             reader.onload = function (e) {
30679                 if (e.target.error) {
30680                     Roo.log(e.target.error);
30681                     return;
30682                 }
30683                 
30684                 var buffer = e.target.result,
30685                     dataView = new DataView(buffer),
30686                     offset = 2,
30687                     maxOffset = dataView.byteLength - 4,
30688                     markerBytes,
30689                     markerLength;
30690                 
30691                 if (dataView.getUint16(0) === 0xffd8) {
30692                     while (offset < maxOffset) {
30693                         markerBytes = dataView.getUint16(offset);
30694                         
30695                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30696                             markerLength = dataView.getUint16(offset + 2) + 2;
30697                             if (offset + markerLength > dataView.byteLength) {
30698                                 Roo.log('Invalid meta data: Invalid segment size.');
30699                                 break;
30700                             }
30701                             
30702                             if(markerBytes == 0xffe1){
30703                                 _this.parseExifData(
30704                                     dataView,
30705                                     offset,
30706                                     markerLength
30707                                 );
30708                             }
30709                             
30710                             offset += markerLength;
30711                             
30712                             continue;
30713                         }
30714                         
30715                         break;
30716                     }
30717                     
30718                 }
30719                 
30720                 var url = _this.urlAPI.createObjectURL(_this.file);
30721                 
30722                 _this.loadCanvas(url);
30723                 
30724                 return;
30725             }
30726             
30727             reader.readAsArrayBuffer(this.file);
30728             
30729         }
30730         
30731     },
30732     
30733     parseExifData : function(dataView, offset, length)
30734     {
30735         var tiffOffset = offset + 10,
30736             littleEndian,
30737             dirOffset;
30738     
30739         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30740             // No Exif data, might be XMP data instead
30741             return;
30742         }
30743         
30744         // Check for the ASCII code for "Exif" (0x45786966):
30745         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30746             // No Exif data, might be XMP data instead
30747             return;
30748         }
30749         if (tiffOffset + 8 > dataView.byteLength) {
30750             Roo.log('Invalid Exif data: Invalid segment size.');
30751             return;
30752         }
30753         // Check for the two null bytes:
30754         if (dataView.getUint16(offset + 8) !== 0x0000) {
30755             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30756             return;
30757         }
30758         // Check the byte alignment:
30759         switch (dataView.getUint16(tiffOffset)) {
30760         case 0x4949:
30761             littleEndian = true;
30762             break;
30763         case 0x4D4D:
30764             littleEndian = false;
30765             break;
30766         default:
30767             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30768             return;
30769         }
30770         // Check for the TIFF tag marker (0x002A):
30771         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30772             Roo.log('Invalid Exif data: Missing TIFF marker.');
30773             return;
30774         }
30775         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30776         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30777         
30778         this.parseExifTags(
30779             dataView,
30780             tiffOffset,
30781             tiffOffset + dirOffset,
30782             littleEndian
30783         );
30784     },
30785     
30786     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30787     {
30788         var tagsNumber,
30789             dirEndOffset,
30790             i;
30791         if (dirOffset + 6 > dataView.byteLength) {
30792             Roo.log('Invalid Exif data: Invalid directory offset.');
30793             return;
30794         }
30795         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30796         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30797         if (dirEndOffset + 4 > dataView.byteLength) {
30798             Roo.log('Invalid Exif data: Invalid directory size.');
30799             return;
30800         }
30801         for (i = 0; i < tagsNumber; i += 1) {
30802             this.parseExifTag(
30803                 dataView,
30804                 tiffOffset,
30805                 dirOffset + 2 + 12 * i, // tag offset
30806                 littleEndian
30807             );
30808         }
30809         // Return the offset to the next directory:
30810         return dataView.getUint32(dirEndOffset, littleEndian);
30811     },
30812     
30813     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30814     {
30815         var tag = dataView.getUint16(offset, littleEndian);
30816         
30817         this.exif[tag] = this.getExifValue(
30818             dataView,
30819             tiffOffset,
30820             offset,
30821             dataView.getUint16(offset + 2, littleEndian), // tag type
30822             dataView.getUint32(offset + 4, littleEndian), // tag length
30823             littleEndian
30824         );
30825     },
30826     
30827     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30828     {
30829         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30830             tagSize,
30831             dataOffset,
30832             values,
30833             i,
30834             str,
30835             c;
30836     
30837         if (!tagType) {
30838             Roo.log('Invalid Exif data: Invalid tag type.');
30839             return;
30840         }
30841         
30842         tagSize = tagType.size * length;
30843         // Determine if the value is contained in the dataOffset bytes,
30844         // or if the value at the dataOffset is a pointer to the actual data:
30845         dataOffset = tagSize > 4 ?
30846                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30847         if (dataOffset + tagSize > dataView.byteLength) {
30848             Roo.log('Invalid Exif data: Invalid data offset.');
30849             return;
30850         }
30851         if (length === 1) {
30852             return tagType.getValue(dataView, dataOffset, littleEndian);
30853         }
30854         values = [];
30855         for (i = 0; i < length; i += 1) {
30856             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30857         }
30858         
30859         if (tagType.ascii) {
30860             str = '';
30861             // Concatenate the chars:
30862             for (i = 0; i < values.length; i += 1) {
30863                 c = values[i];
30864                 // Ignore the terminating NULL byte(s):
30865                 if (c === '\u0000') {
30866                     break;
30867                 }
30868                 str += c;
30869             }
30870             return str;
30871         }
30872         return values;
30873     }
30874     
30875 });
30876
30877 Roo.apply(Roo.bootstrap.UploadCropbox, {
30878     tags : {
30879         'Orientation': 0x0112
30880     },
30881     
30882     Orientation: {
30883             1: 0, //'top-left',
30884 //            2: 'top-right',
30885             3: 180, //'bottom-right',
30886 //            4: 'bottom-left',
30887 //            5: 'left-top',
30888             6: 90, //'right-top',
30889 //            7: 'right-bottom',
30890             8: 270 //'left-bottom'
30891     },
30892     
30893     exifTagTypes : {
30894         // byte, 8-bit unsigned int:
30895         1: {
30896             getValue: function (dataView, dataOffset) {
30897                 return dataView.getUint8(dataOffset);
30898             },
30899             size: 1
30900         },
30901         // ascii, 8-bit byte:
30902         2: {
30903             getValue: function (dataView, dataOffset) {
30904                 return String.fromCharCode(dataView.getUint8(dataOffset));
30905             },
30906             size: 1,
30907             ascii: true
30908         },
30909         // short, 16 bit int:
30910         3: {
30911             getValue: function (dataView, dataOffset, littleEndian) {
30912                 return dataView.getUint16(dataOffset, littleEndian);
30913             },
30914             size: 2
30915         },
30916         // long, 32 bit int:
30917         4: {
30918             getValue: function (dataView, dataOffset, littleEndian) {
30919                 return dataView.getUint32(dataOffset, littleEndian);
30920             },
30921             size: 4
30922         },
30923         // rational = two long values, first is numerator, second is denominator:
30924         5: {
30925             getValue: function (dataView, dataOffset, littleEndian) {
30926                 return dataView.getUint32(dataOffset, littleEndian) /
30927                     dataView.getUint32(dataOffset + 4, littleEndian);
30928             },
30929             size: 8
30930         },
30931         // slong, 32 bit signed int:
30932         9: {
30933             getValue: function (dataView, dataOffset, littleEndian) {
30934                 return dataView.getInt32(dataOffset, littleEndian);
30935             },
30936             size: 4
30937         },
30938         // srational, two slongs, first is numerator, second is denominator:
30939         10: {
30940             getValue: function (dataView, dataOffset, littleEndian) {
30941                 return dataView.getInt32(dataOffset, littleEndian) /
30942                     dataView.getInt32(dataOffset + 4, littleEndian);
30943             },
30944             size: 8
30945         }
30946     },
30947     
30948     footer : {
30949         STANDARD : [
30950             {
30951                 tag : 'div',
30952                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30953                 action : 'rotate-left',
30954                 cn : [
30955                     {
30956                         tag : 'button',
30957                         cls : 'btn btn-default',
30958                         html : '<i class="fa fa-undo"></i>'
30959                     }
30960                 ]
30961             },
30962             {
30963                 tag : 'div',
30964                 cls : 'btn-group roo-upload-cropbox-picture',
30965                 action : 'picture',
30966                 cn : [
30967                     {
30968                         tag : 'button',
30969                         cls : 'btn btn-default',
30970                         html : '<i class="fa fa-picture-o"></i>'
30971                     }
30972                 ]
30973             },
30974             {
30975                 tag : 'div',
30976                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30977                 action : 'rotate-right',
30978                 cn : [
30979                     {
30980                         tag : 'button',
30981                         cls : 'btn btn-default',
30982                         html : '<i class="fa fa-repeat"></i>'
30983                     }
30984                 ]
30985             }
30986         ],
30987         DOCUMENT : [
30988             {
30989                 tag : 'div',
30990                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30991                 action : 'rotate-left',
30992                 cn : [
30993                     {
30994                         tag : 'button',
30995                         cls : 'btn btn-default',
30996                         html : '<i class="fa fa-undo"></i>'
30997                     }
30998                 ]
30999             },
31000             {
31001                 tag : 'div',
31002                 cls : 'btn-group roo-upload-cropbox-download',
31003                 action : 'download',
31004                 cn : [
31005                     {
31006                         tag : 'button',
31007                         cls : 'btn btn-default',
31008                         html : '<i class="fa fa-download"></i>'
31009                     }
31010                 ]
31011             },
31012             {
31013                 tag : 'div',
31014                 cls : 'btn-group roo-upload-cropbox-crop',
31015                 action : 'crop',
31016                 cn : [
31017                     {
31018                         tag : 'button',
31019                         cls : 'btn btn-default',
31020                         html : '<i class="fa fa-crop"></i>'
31021                     }
31022                 ]
31023             },
31024             {
31025                 tag : 'div',
31026                 cls : 'btn-group roo-upload-cropbox-trash',
31027                 action : 'trash',
31028                 cn : [
31029                     {
31030                         tag : 'button',
31031                         cls : 'btn btn-default',
31032                         html : '<i class="fa fa-trash"></i>'
31033                     }
31034                 ]
31035             },
31036             {
31037                 tag : 'div',
31038                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31039                 action : 'rotate-right',
31040                 cn : [
31041                     {
31042                         tag : 'button',
31043                         cls : 'btn btn-default',
31044                         html : '<i class="fa fa-repeat"></i>'
31045                     }
31046                 ]
31047             }
31048         ],
31049         ROTATOR : [
31050             {
31051                 tag : 'div',
31052                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31053                 action : 'rotate-left',
31054                 cn : [
31055                     {
31056                         tag : 'button',
31057                         cls : 'btn btn-default',
31058                         html : '<i class="fa fa-undo"></i>'
31059                     }
31060                 ]
31061             },
31062             {
31063                 tag : 'div',
31064                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31065                 action : 'rotate-right',
31066                 cn : [
31067                     {
31068                         tag : 'button',
31069                         cls : 'btn btn-default',
31070                         html : '<i class="fa fa-repeat"></i>'
31071                     }
31072                 ]
31073             }
31074         ]
31075     }
31076 });
31077
31078 /*
31079 * Licence: LGPL
31080 */
31081
31082 /**
31083  * @class Roo.bootstrap.DocumentManager
31084  * @extends Roo.bootstrap.Component
31085  * Bootstrap DocumentManager class
31086  * @cfg {String} paramName default 'imageUpload'
31087  * @cfg {String} toolTipName default 'filename'
31088  * @cfg {String} method default POST
31089  * @cfg {String} url action url
31090  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31091  * @cfg {Boolean} multiple multiple upload default true
31092  * @cfg {Number} thumbSize default 300
31093  * @cfg {String} fieldLabel
31094  * @cfg {Number} labelWidth default 4
31095  * @cfg {String} labelAlign (left|top) default left
31096  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31097 * @cfg {Number} labellg set the width of label (1-12)
31098  * @cfg {Number} labelmd set the width of label (1-12)
31099  * @cfg {Number} labelsm set the width of label (1-12)
31100  * @cfg {Number} labelxs set the width of label (1-12)
31101  * 
31102  * @constructor
31103  * Create a new DocumentManager
31104  * @param {Object} config The config object
31105  */
31106
31107 Roo.bootstrap.DocumentManager = function(config){
31108     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31109     
31110     this.files = [];
31111     this.delegates = [];
31112     
31113     this.addEvents({
31114         /**
31115          * @event initial
31116          * Fire when initial the DocumentManager
31117          * @param {Roo.bootstrap.DocumentManager} this
31118          */
31119         "initial" : true,
31120         /**
31121          * @event inspect
31122          * inspect selected file
31123          * @param {Roo.bootstrap.DocumentManager} this
31124          * @param {File} file
31125          */
31126         "inspect" : true,
31127         /**
31128          * @event exception
31129          * Fire when xhr load exception
31130          * @param {Roo.bootstrap.DocumentManager} this
31131          * @param {XMLHttpRequest} xhr
31132          */
31133         "exception" : true,
31134         /**
31135          * @event afterupload
31136          * Fire when xhr load exception
31137          * @param {Roo.bootstrap.DocumentManager} this
31138          * @param {XMLHttpRequest} xhr
31139          */
31140         "afterupload" : true,
31141         /**
31142          * @event prepare
31143          * prepare the form data
31144          * @param {Roo.bootstrap.DocumentManager} this
31145          * @param {Object} formData
31146          */
31147         "prepare" : true,
31148         /**
31149          * @event remove
31150          * Fire when remove the file
31151          * @param {Roo.bootstrap.DocumentManager} this
31152          * @param {Object} file
31153          */
31154         "remove" : true,
31155         /**
31156          * @event refresh
31157          * Fire after refresh the file
31158          * @param {Roo.bootstrap.DocumentManager} this
31159          */
31160         "refresh" : true,
31161         /**
31162          * @event click
31163          * Fire after click the image
31164          * @param {Roo.bootstrap.DocumentManager} this
31165          * @param {Object} file
31166          */
31167         "click" : true,
31168         /**
31169          * @event edit
31170          * Fire when upload a image and editable set to true
31171          * @param {Roo.bootstrap.DocumentManager} this
31172          * @param {Object} file
31173          */
31174         "edit" : true,
31175         /**
31176          * @event beforeselectfile
31177          * Fire before select file
31178          * @param {Roo.bootstrap.DocumentManager} this
31179          */
31180         "beforeselectfile" : true,
31181         /**
31182          * @event process
31183          * Fire before process file
31184          * @param {Roo.bootstrap.DocumentManager} this
31185          * @param {Object} file
31186          */
31187         "process" : true,
31188         /**
31189          * @event previewrendered
31190          * Fire when preview rendered
31191          * @param {Roo.bootstrap.DocumentManager} this
31192          * @param {Object} file
31193          */
31194         "previewrendered" : true,
31195         /**
31196          */
31197         "previewResize" : true
31198         
31199     });
31200 };
31201
31202 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31203     
31204     boxes : 0,
31205     inputName : '',
31206     thumbSize : 300,
31207     multiple : true,
31208     files : false,
31209     method : 'POST',
31210     url : '',
31211     paramName : 'imageUpload',
31212     toolTipName : 'filename',
31213     fieldLabel : '',
31214     labelWidth : 4,
31215     labelAlign : 'left',
31216     editable : true,
31217     delegates : false,
31218     xhr : false, 
31219     
31220     labellg : 0,
31221     labelmd : 0,
31222     labelsm : 0,
31223     labelxs : 0,
31224     
31225     getAutoCreate : function()
31226     {   
31227         var managerWidget = {
31228             tag : 'div',
31229             cls : 'roo-document-manager',
31230             cn : [
31231                 {
31232                     tag : 'input',
31233                     cls : 'roo-document-manager-selector',
31234                     type : 'file'
31235                 },
31236                 {
31237                     tag : 'div',
31238                     cls : 'roo-document-manager-uploader',
31239                     cn : [
31240                         {
31241                             tag : 'div',
31242                             cls : 'roo-document-manager-upload-btn',
31243                             html : '<i class="fa fa-plus"></i>'
31244                         }
31245                     ]
31246                     
31247                 }
31248             ]
31249         };
31250         
31251         var content = [
31252             {
31253                 tag : 'div',
31254                 cls : 'column col-md-12',
31255                 cn : managerWidget
31256             }
31257         ];
31258         
31259         if(this.fieldLabel.length){
31260             
31261             content = [
31262                 {
31263                     tag : 'div',
31264                     cls : 'column col-md-12',
31265                     html : this.fieldLabel
31266                 },
31267                 {
31268                     tag : 'div',
31269                     cls : 'column col-md-12',
31270                     cn : managerWidget
31271                 }
31272             ];
31273
31274             if(this.labelAlign == 'left'){
31275                 content = [
31276                     {
31277                         tag : 'div',
31278                         cls : 'column',
31279                         html : this.fieldLabel
31280                     },
31281                     {
31282                         tag : 'div',
31283                         cls : 'column',
31284                         cn : managerWidget
31285                     }
31286                 ];
31287                 
31288                 if(this.labelWidth > 12){
31289                     content[0].style = "width: " + this.labelWidth + 'px';
31290                 }
31291
31292                 if(this.labelWidth < 13 && this.labelmd == 0){
31293                     this.labelmd = this.labelWidth;
31294                 }
31295
31296                 if(this.labellg > 0){
31297                     content[0].cls += ' col-lg-' + this.labellg;
31298                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31299                 }
31300
31301                 if(this.labelmd > 0){
31302                     content[0].cls += ' col-md-' + this.labelmd;
31303                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31304                 }
31305
31306                 if(this.labelsm > 0){
31307                     content[0].cls += ' col-sm-' + this.labelsm;
31308                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31309                 }
31310
31311                 if(this.labelxs > 0){
31312                     content[0].cls += ' col-xs-' + this.labelxs;
31313                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31314                 }
31315                 
31316             }
31317         }
31318         
31319         var cfg = {
31320             tag : 'div',
31321             cls : 'row clearfix',
31322             cn : content
31323         };
31324         
31325         return cfg;
31326         
31327     },
31328     
31329     initEvents : function()
31330     {
31331         this.managerEl = this.el.select('.roo-document-manager', true).first();
31332         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31333         
31334         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31335         this.selectorEl.hide();
31336         
31337         if(this.multiple){
31338             this.selectorEl.attr('multiple', 'multiple');
31339         }
31340         
31341         this.selectorEl.on('change', this.onFileSelected, this);
31342         
31343         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31344         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31345         
31346         this.uploader.on('click', this.onUploaderClick, this);
31347         
31348         this.renderProgressDialog();
31349         
31350         var _this = this;
31351         
31352         window.addEventListener("resize", function() { _this.refresh(); } );
31353         
31354         this.fireEvent('initial', this);
31355     },
31356     
31357     renderProgressDialog : function()
31358     {
31359         var _this = this;
31360         
31361         this.progressDialog = new Roo.bootstrap.Modal({
31362             cls : 'roo-document-manager-progress-dialog',
31363             allow_close : false,
31364             animate : false,
31365             title : '',
31366             buttons : [
31367                 {
31368                     name  :'cancel',
31369                     weight : 'danger',
31370                     html : 'Cancel'
31371                 }
31372             ], 
31373             listeners : { 
31374                 btnclick : function() {
31375                     _this.uploadCancel();
31376                     this.hide();
31377                 }
31378             }
31379         });
31380          
31381         this.progressDialog.render(Roo.get(document.body));
31382          
31383         this.progress = new Roo.bootstrap.Progress({
31384             cls : 'roo-document-manager-progress',
31385             active : true,
31386             striped : true
31387         });
31388         
31389         this.progress.render(this.progressDialog.getChildContainer());
31390         
31391         this.progressBar = new Roo.bootstrap.ProgressBar({
31392             cls : 'roo-document-manager-progress-bar',
31393             aria_valuenow : 0,
31394             aria_valuemin : 0,
31395             aria_valuemax : 12,
31396             panel : 'success'
31397         });
31398         
31399         this.progressBar.render(this.progress.getChildContainer());
31400     },
31401     
31402     onUploaderClick : function(e)
31403     {
31404         e.preventDefault();
31405      
31406         if(this.fireEvent('beforeselectfile', this) != false){
31407             this.selectorEl.dom.click();
31408         }
31409         
31410     },
31411     
31412     onFileSelected : function(e)
31413     {
31414         e.preventDefault();
31415         
31416         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31417             return;
31418         }
31419         
31420         Roo.each(this.selectorEl.dom.files, function(file){
31421             if(this.fireEvent('inspect', this, file) != false){
31422                 this.files.push(file);
31423             }
31424         }, this);
31425         
31426         this.queue();
31427         
31428     },
31429     
31430     queue : function()
31431     {
31432         this.selectorEl.dom.value = '';
31433         
31434         if(!this.files || !this.files.length){
31435             return;
31436         }
31437         
31438         if(this.boxes > 0 && this.files.length > this.boxes){
31439             this.files = this.files.slice(0, this.boxes);
31440         }
31441         
31442         this.uploader.show();
31443         
31444         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31445             this.uploader.hide();
31446         }
31447         
31448         var _this = this;
31449         
31450         var files = [];
31451         
31452         var docs = [];
31453         
31454         Roo.each(this.files, function(file){
31455             
31456             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31457                 var f = this.renderPreview(file);
31458                 files.push(f);
31459                 return;
31460             }
31461             
31462             if(file.type.indexOf('image') != -1){
31463                 this.delegates.push(
31464                     (function(){
31465                         _this.process(file);
31466                     }).createDelegate(this)
31467                 );
31468         
31469                 return;
31470             }
31471             
31472             docs.push(
31473                 (function(){
31474                     _this.process(file);
31475                 }).createDelegate(this)
31476             );
31477             
31478         }, this);
31479         
31480         this.files = files;
31481         
31482         this.delegates = this.delegates.concat(docs);
31483         
31484         if(!this.delegates.length){
31485             this.refresh();
31486             return;
31487         }
31488         
31489         this.progressBar.aria_valuemax = this.delegates.length;
31490         
31491         this.arrange();
31492         
31493         return;
31494     },
31495     
31496     arrange : function()
31497     {
31498         if(!this.delegates.length){
31499             this.progressDialog.hide();
31500             this.refresh();
31501             return;
31502         }
31503         
31504         var delegate = this.delegates.shift();
31505         
31506         this.progressDialog.show();
31507         
31508         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31509         
31510         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31511         
31512         delegate();
31513     },
31514     
31515     refresh : function()
31516     {
31517         this.uploader.show();
31518         
31519         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31520             this.uploader.hide();
31521         }
31522         
31523         Roo.isTouch ? this.closable(false) : this.closable(true);
31524         
31525         this.fireEvent('refresh', this);
31526     },
31527     
31528     onRemove : function(e, el, o)
31529     {
31530         e.preventDefault();
31531         
31532         this.fireEvent('remove', this, o);
31533         
31534     },
31535     
31536     remove : function(o)
31537     {
31538         var files = [];
31539         
31540         Roo.each(this.files, function(file){
31541             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31542                 files.push(file);
31543                 return;
31544             }
31545
31546             o.target.remove();
31547
31548         }, this);
31549         
31550         this.files = files;
31551         
31552         this.refresh();
31553     },
31554     
31555     clear : function()
31556     {
31557         Roo.each(this.files, function(file){
31558             if(!file.target){
31559                 return;
31560             }
31561             
31562             file.target.remove();
31563
31564         }, this);
31565         
31566         this.files = [];
31567         
31568         this.refresh();
31569     },
31570     
31571     onClick : function(e, el, o)
31572     {
31573         e.preventDefault();
31574         
31575         this.fireEvent('click', this, o);
31576         
31577     },
31578     
31579     closable : function(closable)
31580     {
31581         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31582             
31583             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31584             
31585             if(closable){
31586                 el.show();
31587                 return;
31588             }
31589             
31590             el.hide();
31591             
31592         }, this);
31593     },
31594     
31595     xhrOnLoad : function(xhr)
31596     {
31597         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31598             el.remove();
31599         }, this);
31600         
31601         if (xhr.readyState !== 4) {
31602             this.arrange();
31603             this.fireEvent('exception', this, xhr);
31604             return;
31605         }
31606
31607         var response = Roo.decode(xhr.responseText);
31608         
31609         if(!response.success){
31610             this.arrange();
31611             this.fireEvent('exception', this, xhr);
31612             return;
31613         }
31614         
31615         var file = this.renderPreview(response.data);
31616         
31617         this.files.push(file);
31618         
31619         this.arrange();
31620         
31621         this.fireEvent('afterupload', this, xhr);
31622         
31623     },
31624     
31625     xhrOnError : function(xhr)
31626     {
31627         Roo.log('xhr on error');
31628         
31629         var response = Roo.decode(xhr.responseText);
31630           
31631         Roo.log(response);
31632         
31633         this.arrange();
31634     },
31635     
31636     process : function(file)
31637     {
31638         if(this.fireEvent('process', this, file) !== false){
31639             if(this.editable && file.type.indexOf('image') != -1){
31640                 this.fireEvent('edit', this, file);
31641                 return;
31642             }
31643
31644             this.uploadStart(file, false);
31645
31646             return;
31647         }
31648         
31649     },
31650     
31651     uploadStart : function(file, crop)
31652     {
31653         this.xhr = new XMLHttpRequest();
31654         
31655         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31656             this.arrange();
31657             return;
31658         }
31659         
31660         file.xhr = this.xhr;
31661             
31662         this.managerEl.createChild({
31663             tag : 'div',
31664             cls : 'roo-document-manager-loading',
31665             cn : [
31666                 {
31667                     tag : 'div',
31668                     tooltip : file.name,
31669                     cls : 'roo-document-manager-thumb',
31670                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31671                 }
31672             ]
31673
31674         });
31675
31676         this.xhr.open(this.method, this.url, true);
31677         
31678         var headers = {
31679             "Accept": "application/json",
31680             "Cache-Control": "no-cache",
31681             "X-Requested-With": "XMLHttpRequest"
31682         };
31683         
31684         for (var headerName in headers) {
31685             var headerValue = headers[headerName];
31686             if (headerValue) {
31687                 this.xhr.setRequestHeader(headerName, headerValue);
31688             }
31689         }
31690         
31691         var _this = this;
31692         
31693         this.xhr.onload = function()
31694         {
31695             _this.xhrOnLoad(_this.xhr);
31696         }
31697         
31698         this.xhr.onerror = function()
31699         {
31700             _this.xhrOnError(_this.xhr);
31701         }
31702         
31703         var formData = new FormData();
31704
31705         formData.append('returnHTML', 'NO');
31706         
31707         if(crop){
31708             formData.append('crop', crop);
31709         }
31710         
31711         formData.append(this.paramName, file, file.name);
31712         
31713         var options = {
31714             file : file, 
31715             manually : false
31716         };
31717         
31718         if(this.fireEvent('prepare', this, formData, options) != false){
31719             
31720             if(options.manually){
31721                 return;
31722             }
31723             
31724             this.xhr.send(formData);
31725             return;
31726         };
31727         
31728         this.uploadCancel();
31729     },
31730     
31731     uploadCancel : function()
31732     {
31733         if (this.xhr) {
31734             this.xhr.abort();
31735         }
31736         
31737         this.delegates = [];
31738         
31739         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31740             el.remove();
31741         }, this);
31742         
31743         this.arrange();
31744     },
31745     
31746     renderPreview : function(file)
31747     {
31748         if(typeof(file.target) != 'undefined' && file.target){
31749             return file;
31750         }
31751         
31752         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31753         
31754         var previewEl = this.managerEl.createChild({
31755             tag : 'div',
31756             cls : 'roo-document-manager-preview',
31757             cn : [
31758                 {
31759                     tag : 'div',
31760                     tooltip : file[this.toolTipName],
31761                     cls : 'roo-document-manager-thumb',
31762                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31763                 },
31764                 {
31765                     tag : 'button',
31766                     cls : 'close',
31767                     html : '<i class="fa fa-times-circle"></i>'
31768                 }
31769             ]
31770         });
31771
31772         var close = previewEl.select('button.close', true).first();
31773
31774         close.on('click', this.onRemove, this, file);
31775
31776         file.target = previewEl;
31777
31778         var image = previewEl.select('img', true).first();
31779         
31780         var _this = this;
31781         
31782         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31783         
31784         image.on('click', this.onClick, this, file);
31785         
31786         this.fireEvent('previewrendered', this, file);
31787         
31788         return file;
31789         
31790     },
31791     
31792     onPreviewLoad : function(file, image)
31793     {
31794         if(typeof(file.target) == 'undefined' || !file.target){
31795             return;
31796         }
31797         
31798         var width = image.dom.naturalWidth || image.dom.width;
31799         var height = image.dom.naturalHeight || image.dom.height;
31800         
31801         if(!this.previewResize) {
31802             return;
31803         }
31804         
31805         if(width > height){
31806             file.target.addClass('wide');
31807             return;
31808         }
31809         
31810         file.target.addClass('tall');
31811         return;
31812         
31813     },
31814     
31815     uploadFromSource : function(file, crop)
31816     {
31817         this.xhr = new XMLHttpRequest();
31818         
31819         this.managerEl.createChild({
31820             tag : 'div',
31821             cls : 'roo-document-manager-loading',
31822             cn : [
31823                 {
31824                     tag : 'div',
31825                     tooltip : file.name,
31826                     cls : 'roo-document-manager-thumb',
31827                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31828                 }
31829             ]
31830
31831         });
31832
31833         this.xhr.open(this.method, this.url, true);
31834         
31835         var headers = {
31836             "Accept": "application/json",
31837             "Cache-Control": "no-cache",
31838             "X-Requested-With": "XMLHttpRequest"
31839         };
31840         
31841         for (var headerName in headers) {
31842             var headerValue = headers[headerName];
31843             if (headerValue) {
31844                 this.xhr.setRequestHeader(headerName, headerValue);
31845             }
31846         }
31847         
31848         var _this = this;
31849         
31850         this.xhr.onload = function()
31851         {
31852             _this.xhrOnLoad(_this.xhr);
31853         }
31854         
31855         this.xhr.onerror = function()
31856         {
31857             _this.xhrOnError(_this.xhr);
31858         }
31859         
31860         var formData = new FormData();
31861
31862         formData.append('returnHTML', 'NO');
31863         
31864         formData.append('crop', crop);
31865         
31866         if(typeof(file.filename) != 'undefined'){
31867             formData.append('filename', file.filename);
31868         }
31869         
31870         if(typeof(file.mimetype) != 'undefined'){
31871             formData.append('mimetype', file.mimetype);
31872         }
31873         
31874         Roo.log(formData);
31875         
31876         if(this.fireEvent('prepare', this, formData) != false){
31877             this.xhr.send(formData);
31878         };
31879     }
31880 });
31881
31882 /*
31883 * Licence: LGPL
31884 */
31885
31886 /**
31887  * @class Roo.bootstrap.DocumentViewer
31888  * @extends Roo.bootstrap.Component
31889  * Bootstrap DocumentViewer class
31890  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31891  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31892  * 
31893  * @constructor
31894  * Create a new DocumentViewer
31895  * @param {Object} config The config object
31896  */
31897
31898 Roo.bootstrap.DocumentViewer = function(config){
31899     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31900     
31901     this.addEvents({
31902         /**
31903          * @event initial
31904          * Fire after initEvent
31905          * @param {Roo.bootstrap.DocumentViewer} this
31906          */
31907         "initial" : true,
31908         /**
31909          * @event click
31910          * Fire after click
31911          * @param {Roo.bootstrap.DocumentViewer} this
31912          */
31913         "click" : true,
31914         /**
31915          * @event download
31916          * Fire after download button
31917          * @param {Roo.bootstrap.DocumentViewer} this
31918          */
31919         "download" : true,
31920         /**
31921          * @event trash
31922          * Fire after trash button
31923          * @param {Roo.bootstrap.DocumentViewer} this
31924          */
31925         "trash" : true
31926         
31927     });
31928 };
31929
31930 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31931     
31932     showDownload : true,
31933     
31934     showTrash : true,
31935     
31936     getAutoCreate : function()
31937     {
31938         var cfg = {
31939             tag : 'div',
31940             cls : 'roo-document-viewer',
31941             cn : [
31942                 {
31943                     tag : 'div',
31944                     cls : 'roo-document-viewer-body',
31945                     cn : [
31946                         {
31947                             tag : 'div',
31948                             cls : 'roo-document-viewer-thumb',
31949                             cn : [
31950                                 {
31951                                     tag : 'img',
31952                                     cls : 'roo-document-viewer-image'
31953                                 }
31954                             ]
31955                         }
31956                     ]
31957                 },
31958                 {
31959                     tag : 'div',
31960                     cls : 'roo-document-viewer-footer',
31961                     cn : {
31962                         tag : 'div',
31963                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31964                         cn : [
31965                             {
31966                                 tag : 'div',
31967                                 cls : 'btn-group roo-document-viewer-download',
31968                                 cn : [
31969                                     {
31970                                         tag : 'button',
31971                                         cls : 'btn btn-default',
31972                                         html : '<i class="fa fa-download"></i>'
31973                                     }
31974                                 ]
31975                             },
31976                             {
31977                                 tag : 'div',
31978                                 cls : 'btn-group roo-document-viewer-trash',
31979                                 cn : [
31980                                     {
31981                                         tag : 'button',
31982                                         cls : 'btn btn-default',
31983                                         html : '<i class="fa fa-trash"></i>'
31984                                     }
31985                                 ]
31986                             }
31987                         ]
31988                     }
31989                 }
31990             ]
31991         };
31992         
31993         return cfg;
31994     },
31995     
31996     initEvents : function()
31997     {
31998         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31999         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32000         
32001         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32002         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32003         
32004         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32005         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32006         
32007         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32008         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32009         
32010         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32011         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32012         
32013         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32014         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32015         
32016         this.bodyEl.on('click', this.onClick, this);
32017         this.downloadBtn.on('click', this.onDownload, this);
32018         this.trashBtn.on('click', this.onTrash, this);
32019         
32020         this.downloadBtn.hide();
32021         this.trashBtn.hide();
32022         
32023         if(this.showDownload){
32024             this.downloadBtn.show();
32025         }
32026         
32027         if(this.showTrash){
32028             this.trashBtn.show();
32029         }
32030         
32031         if(!this.showDownload && !this.showTrash) {
32032             this.footerEl.hide();
32033         }
32034         
32035     },
32036     
32037     initial : function()
32038     {
32039         this.fireEvent('initial', this);
32040         
32041     },
32042     
32043     onClick : function(e)
32044     {
32045         e.preventDefault();
32046         
32047         this.fireEvent('click', this);
32048     },
32049     
32050     onDownload : function(e)
32051     {
32052         e.preventDefault();
32053         
32054         this.fireEvent('download', this);
32055     },
32056     
32057     onTrash : function(e)
32058     {
32059         e.preventDefault();
32060         
32061         this.fireEvent('trash', this);
32062     }
32063     
32064 });
32065 /*
32066  * - LGPL
32067  *
32068  * nav progress bar
32069  * 
32070  */
32071
32072 /**
32073  * @class Roo.bootstrap.NavProgressBar
32074  * @extends Roo.bootstrap.Component
32075  * Bootstrap NavProgressBar class
32076  * 
32077  * @constructor
32078  * Create a new nav progress bar
32079  * @param {Object} config The config object
32080  */
32081
32082 Roo.bootstrap.NavProgressBar = function(config){
32083     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32084
32085     this.bullets = this.bullets || [];
32086    
32087 //    Roo.bootstrap.NavProgressBar.register(this);
32088      this.addEvents({
32089         /**
32090              * @event changed
32091              * Fires when the active item changes
32092              * @param {Roo.bootstrap.NavProgressBar} this
32093              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32094              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32095          */
32096         'changed': true
32097      });
32098     
32099 };
32100
32101 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32102     
32103     bullets : [],
32104     barItems : [],
32105     
32106     getAutoCreate : function()
32107     {
32108         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32109         
32110         cfg = {
32111             tag : 'div',
32112             cls : 'roo-navigation-bar-group',
32113             cn : [
32114                 {
32115                     tag : 'div',
32116                     cls : 'roo-navigation-top-bar'
32117                 },
32118                 {
32119                     tag : 'div',
32120                     cls : 'roo-navigation-bullets-bar',
32121                     cn : [
32122                         {
32123                             tag : 'ul',
32124                             cls : 'roo-navigation-bar'
32125                         }
32126                     ]
32127                 },
32128                 
32129                 {
32130                     tag : 'div',
32131                     cls : 'roo-navigation-bottom-bar'
32132                 }
32133             ]
32134             
32135         };
32136         
32137         return cfg;
32138         
32139     },
32140     
32141     initEvents: function() 
32142     {
32143         
32144     },
32145     
32146     onRender : function(ct, position) 
32147     {
32148         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32149         
32150         if(this.bullets.length){
32151             Roo.each(this.bullets, function(b){
32152                this.addItem(b);
32153             }, this);
32154         }
32155         
32156         this.format();
32157         
32158     },
32159     
32160     addItem : function(cfg)
32161     {
32162         var item = new Roo.bootstrap.NavProgressItem(cfg);
32163         
32164         item.parentId = this.id;
32165         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32166         
32167         if(cfg.html){
32168             var top = new Roo.bootstrap.Element({
32169                 tag : 'div',
32170                 cls : 'roo-navigation-bar-text'
32171             });
32172             
32173             var bottom = new Roo.bootstrap.Element({
32174                 tag : 'div',
32175                 cls : 'roo-navigation-bar-text'
32176             });
32177             
32178             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32179             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32180             
32181             var topText = new Roo.bootstrap.Element({
32182                 tag : 'span',
32183                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32184             });
32185             
32186             var bottomText = new Roo.bootstrap.Element({
32187                 tag : 'span',
32188                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32189             });
32190             
32191             topText.onRender(top.el, null);
32192             bottomText.onRender(bottom.el, null);
32193             
32194             item.topEl = top;
32195             item.bottomEl = bottom;
32196         }
32197         
32198         this.barItems.push(item);
32199         
32200         return item;
32201     },
32202     
32203     getActive : function()
32204     {
32205         var active = false;
32206         
32207         Roo.each(this.barItems, function(v){
32208             
32209             if (!v.isActive()) {
32210                 return;
32211             }
32212             
32213             active = v;
32214             return false;
32215             
32216         });
32217         
32218         return active;
32219     },
32220     
32221     setActiveItem : function(item)
32222     {
32223         var prev = false;
32224         
32225         Roo.each(this.barItems, function(v){
32226             if (v.rid == item.rid) {
32227                 return ;
32228             }
32229             
32230             if (v.isActive()) {
32231                 v.setActive(false);
32232                 prev = v;
32233             }
32234         });
32235
32236         item.setActive(true);
32237         
32238         this.fireEvent('changed', this, item, prev);
32239     },
32240     
32241     getBarItem: function(rid)
32242     {
32243         var ret = false;
32244         
32245         Roo.each(this.barItems, function(e) {
32246             if (e.rid != rid) {
32247                 return;
32248             }
32249             
32250             ret =  e;
32251             return false;
32252         });
32253         
32254         return ret;
32255     },
32256     
32257     indexOfItem : function(item)
32258     {
32259         var index = false;
32260         
32261         Roo.each(this.barItems, function(v, i){
32262             
32263             if (v.rid != item.rid) {
32264                 return;
32265             }
32266             
32267             index = i;
32268             return false
32269         });
32270         
32271         return index;
32272     },
32273     
32274     setActiveNext : function()
32275     {
32276         var i = this.indexOfItem(this.getActive());
32277         
32278         if (i > this.barItems.length) {
32279             return;
32280         }
32281         
32282         this.setActiveItem(this.barItems[i+1]);
32283     },
32284     
32285     setActivePrev : function()
32286     {
32287         var i = this.indexOfItem(this.getActive());
32288         
32289         if (i  < 1) {
32290             return;
32291         }
32292         
32293         this.setActiveItem(this.barItems[i-1]);
32294     },
32295     
32296     format : function()
32297     {
32298         if(!this.barItems.length){
32299             return;
32300         }
32301      
32302         var width = 100 / this.barItems.length;
32303         
32304         Roo.each(this.barItems, function(i){
32305             i.el.setStyle('width', width + '%');
32306             i.topEl.el.setStyle('width', width + '%');
32307             i.bottomEl.el.setStyle('width', width + '%');
32308         }, this);
32309         
32310     }
32311     
32312 });
32313 /*
32314  * - LGPL
32315  *
32316  * Nav Progress Item
32317  * 
32318  */
32319
32320 /**
32321  * @class Roo.bootstrap.NavProgressItem
32322  * @extends Roo.bootstrap.Component
32323  * Bootstrap NavProgressItem class
32324  * @cfg {String} rid the reference id
32325  * @cfg {Boolean} active (true|false) Is item active default false
32326  * @cfg {Boolean} disabled (true|false) Is item active default false
32327  * @cfg {String} html
32328  * @cfg {String} position (top|bottom) text position default bottom
32329  * @cfg {String} icon show icon instead of number
32330  * 
32331  * @constructor
32332  * Create a new NavProgressItem
32333  * @param {Object} config The config object
32334  */
32335 Roo.bootstrap.NavProgressItem = function(config){
32336     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32337     this.addEvents({
32338         // raw events
32339         /**
32340          * @event click
32341          * The raw click event for the entire grid.
32342          * @param {Roo.bootstrap.NavProgressItem} this
32343          * @param {Roo.EventObject} e
32344          */
32345         "click" : true
32346     });
32347    
32348 };
32349
32350 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32351     
32352     rid : '',
32353     active : false,
32354     disabled : false,
32355     html : '',
32356     position : 'bottom',
32357     icon : false,
32358     
32359     getAutoCreate : function()
32360     {
32361         var iconCls = 'roo-navigation-bar-item-icon';
32362         
32363         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32364         
32365         var cfg = {
32366             tag: 'li',
32367             cls: 'roo-navigation-bar-item',
32368             cn : [
32369                 {
32370                     tag : 'i',
32371                     cls : iconCls
32372                 }
32373             ]
32374         };
32375         
32376         if(this.active){
32377             cfg.cls += ' active';
32378         }
32379         if(this.disabled){
32380             cfg.cls += ' disabled';
32381         }
32382         
32383         return cfg;
32384     },
32385     
32386     disable : function()
32387     {
32388         this.setDisabled(true);
32389     },
32390     
32391     enable : function()
32392     {
32393         this.setDisabled(false);
32394     },
32395     
32396     initEvents: function() 
32397     {
32398         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32399         
32400         this.iconEl.on('click', this.onClick, this);
32401     },
32402     
32403     onClick : function(e)
32404     {
32405         e.preventDefault();
32406         
32407         if(this.disabled){
32408             return;
32409         }
32410         
32411         if(this.fireEvent('click', this, e) === false){
32412             return;
32413         };
32414         
32415         this.parent().setActiveItem(this);
32416     },
32417     
32418     isActive: function () 
32419     {
32420         return this.active;
32421     },
32422     
32423     setActive : function(state)
32424     {
32425         if(this.active == state){
32426             return;
32427         }
32428         
32429         this.active = state;
32430         
32431         if (state) {
32432             this.el.addClass('active');
32433             return;
32434         }
32435         
32436         this.el.removeClass('active');
32437         
32438         return;
32439     },
32440     
32441     setDisabled : function(state)
32442     {
32443         if(this.disabled == state){
32444             return;
32445         }
32446         
32447         this.disabled = state;
32448         
32449         if (state) {
32450             this.el.addClass('disabled');
32451             return;
32452         }
32453         
32454         this.el.removeClass('disabled');
32455     },
32456     
32457     tooltipEl : function()
32458     {
32459         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32460     }
32461 });
32462  
32463
32464  /*
32465  * - LGPL
32466  *
32467  * FieldLabel
32468  * 
32469  */
32470
32471 /**
32472  * @class Roo.bootstrap.FieldLabel
32473  * @extends Roo.bootstrap.Component
32474  * Bootstrap FieldLabel class
32475  * @cfg {String} html contents of the element
32476  * @cfg {String} tag tag of the element default label
32477  * @cfg {String} cls class of the element
32478  * @cfg {String} target label target 
32479  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32480  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32481  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32482  * @cfg {String} iconTooltip default "This field is required"
32483  * @cfg {String} indicatorpos (left|right) default left
32484  * 
32485  * @constructor
32486  * Create a new FieldLabel
32487  * @param {Object} config The config object
32488  */
32489
32490 Roo.bootstrap.FieldLabel = function(config){
32491     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32492     
32493     this.addEvents({
32494             /**
32495              * @event invalid
32496              * Fires after the field has been marked as invalid.
32497              * @param {Roo.form.FieldLabel} this
32498              * @param {String} msg The validation message
32499              */
32500             invalid : true,
32501             /**
32502              * @event valid
32503              * Fires after the field has been validated with no errors.
32504              * @param {Roo.form.FieldLabel} this
32505              */
32506             valid : true
32507         });
32508 };
32509
32510 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32511     
32512     tag: 'label',
32513     cls: '',
32514     html: '',
32515     target: '',
32516     allowBlank : true,
32517     invalidClass : 'has-warning',
32518     validClass : 'has-success',
32519     iconTooltip : 'This field is required',
32520     indicatorpos : 'left',
32521     
32522     getAutoCreate : function(){
32523         
32524         var cls = "";
32525         if (!this.allowBlank) {
32526             cls  = "visible";
32527         }
32528         
32529         var cfg = {
32530             tag : this.tag,
32531             cls : 'roo-bootstrap-field-label ' + this.cls,
32532             for : this.target,
32533             cn : [
32534                 {
32535                     tag : 'i',
32536                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32537                     tooltip : this.iconTooltip
32538                 },
32539                 {
32540                     tag : 'span',
32541                     html : this.html
32542                 }
32543             ] 
32544         };
32545         
32546         if(this.indicatorpos == 'right'){
32547             var cfg = {
32548                 tag : this.tag,
32549                 cls : 'roo-bootstrap-field-label ' + this.cls,
32550                 for : this.target,
32551                 cn : [
32552                     {
32553                         tag : 'span',
32554                         html : this.html
32555                     },
32556                     {
32557                         tag : 'i',
32558                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32559                         tooltip : this.iconTooltip
32560                     }
32561                 ] 
32562             };
32563         }
32564         
32565         return cfg;
32566     },
32567     
32568     initEvents: function() 
32569     {
32570         Roo.bootstrap.Element.superclass.initEvents.call(this);
32571         
32572         this.indicator = this.indicatorEl();
32573         
32574         if(this.indicator){
32575             this.indicator.removeClass('visible');
32576             this.indicator.addClass('invisible');
32577         }
32578         
32579         Roo.bootstrap.FieldLabel.register(this);
32580     },
32581     
32582     indicatorEl : function()
32583     {
32584         var indicator = this.el.select('i.roo-required-indicator',true).first();
32585         
32586         if(!indicator){
32587             return false;
32588         }
32589         
32590         return indicator;
32591         
32592     },
32593     
32594     /**
32595      * Mark this field as valid
32596      */
32597     markValid : function()
32598     {
32599         if(this.indicator){
32600             this.indicator.removeClass('visible');
32601             this.indicator.addClass('invisible');
32602         }
32603         if (Roo.bootstrap.version == 3) {
32604             this.el.removeClass(this.invalidClass);
32605             this.el.addClass(this.validClass);
32606         } else {
32607             this.el.removeClass('is-invalid');
32608             this.el.addClass('is-valid');
32609         }
32610         
32611         
32612         this.fireEvent('valid', this);
32613     },
32614     
32615     /**
32616      * Mark this field as invalid
32617      * @param {String} msg The validation message
32618      */
32619     markInvalid : function(msg)
32620     {
32621         if(this.indicator){
32622             this.indicator.removeClass('invisible');
32623             this.indicator.addClass('visible');
32624         }
32625           if (Roo.bootstrap.version == 3) {
32626             this.el.removeClass(this.validClass);
32627             this.el.addClass(this.invalidClass);
32628         } else {
32629             this.el.removeClass('is-valid');
32630             this.el.addClass('is-invalid');
32631         }
32632         
32633         
32634         this.fireEvent('invalid', this, msg);
32635     }
32636     
32637    
32638 });
32639
32640 Roo.apply(Roo.bootstrap.FieldLabel, {
32641     
32642     groups: {},
32643     
32644      /**
32645     * register a FieldLabel Group
32646     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32647     */
32648     register : function(label)
32649     {
32650         if(this.groups.hasOwnProperty(label.target)){
32651             return;
32652         }
32653      
32654         this.groups[label.target] = label;
32655         
32656     },
32657     /**
32658     * fetch a FieldLabel Group based on the target
32659     * @param {string} target
32660     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32661     */
32662     get: function(target) {
32663         if (typeof(this.groups[target]) == 'undefined') {
32664             return false;
32665         }
32666         
32667         return this.groups[target] ;
32668     }
32669 });
32670
32671  
32672
32673  /*
32674  * - LGPL
32675  *
32676  * page DateSplitField.
32677  * 
32678  */
32679
32680
32681 /**
32682  * @class Roo.bootstrap.DateSplitField
32683  * @extends Roo.bootstrap.Component
32684  * Bootstrap DateSplitField class
32685  * @cfg {string} fieldLabel - the label associated
32686  * @cfg {Number} labelWidth set the width of label (0-12)
32687  * @cfg {String} labelAlign (top|left)
32688  * @cfg {Boolean} dayAllowBlank (true|false) default false
32689  * @cfg {Boolean} monthAllowBlank (true|false) default false
32690  * @cfg {Boolean} yearAllowBlank (true|false) default false
32691  * @cfg {string} dayPlaceholder 
32692  * @cfg {string} monthPlaceholder
32693  * @cfg {string} yearPlaceholder
32694  * @cfg {string} dayFormat default 'd'
32695  * @cfg {string} monthFormat default 'm'
32696  * @cfg {string} yearFormat default 'Y'
32697  * @cfg {Number} labellg set the width of label (1-12)
32698  * @cfg {Number} labelmd set the width of label (1-12)
32699  * @cfg {Number} labelsm set the width of label (1-12)
32700  * @cfg {Number} labelxs set the width of label (1-12)
32701
32702  *     
32703  * @constructor
32704  * Create a new DateSplitField
32705  * @param {Object} config The config object
32706  */
32707
32708 Roo.bootstrap.DateSplitField = function(config){
32709     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32710     
32711     this.addEvents({
32712         // raw events
32713          /**
32714          * @event years
32715          * getting the data of years
32716          * @param {Roo.bootstrap.DateSplitField} this
32717          * @param {Object} years
32718          */
32719         "years" : true,
32720         /**
32721          * @event days
32722          * getting the data of days
32723          * @param {Roo.bootstrap.DateSplitField} this
32724          * @param {Object} days
32725          */
32726         "days" : true,
32727         /**
32728          * @event invalid
32729          * Fires after the field has been marked as invalid.
32730          * @param {Roo.form.Field} this
32731          * @param {String} msg The validation message
32732          */
32733         invalid : true,
32734        /**
32735          * @event valid
32736          * Fires after the field has been validated with no errors.
32737          * @param {Roo.form.Field} this
32738          */
32739         valid : true
32740     });
32741 };
32742
32743 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32744     
32745     fieldLabel : '',
32746     labelAlign : 'top',
32747     labelWidth : 3,
32748     dayAllowBlank : false,
32749     monthAllowBlank : false,
32750     yearAllowBlank : false,
32751     dayPlaceholder : '',
32752     monthPlaceholder : '',
32753     yearPlaceholder : '',
32754     dayFormat : 'd',
32755     monthFormat : 'm',
32756     yearFormat : 'Y',
32757     isFormField : true,
32758     labellg : 0,
32759     labelmd : 0,
32760     labelsm : 0,
32761     labelxs : 0,
32762     
32763     getAutoCreate : function()
32764     {
32765         var cfg = {
32766             tag : 'div',
32767             cls : 'row roo-date-split-field-group',
32768             cn : [
32769                 {
32770                     tag : 'input',
32771                     type : 'hidden',
32772                     cls : 'form-hidden-field roo-date-split-field-group-value',
32773                     name : this.name
32774                 }
32775             ]
32776         };
32777         
32778         var labelCls = 'col-md-12';
32779         var contentCls = 'col-md-4';
32780         
32781         if(this.fieldLabel){
32782             
32783             var label = {
32784                 tag : 'div',
32785                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32786                 cn : [
32787                     {
32788                         tag : 'label',
32789                         html : this.fieldLabel
32790                     }
32791                 ]
32792             };
32793             
32794             if(this.labelAlign == 'left'){
32795             
32796                 if(this.labelWidth > 12){
32797                     label.style = "width: " + this.labelWidth + 'px';
32798                 }
32799
32800                 if(this.labelWidth < 13 && this.labelmd == 0){
32801                     this.labelmd = this.labelWidth;
32802                 }
32803
32804                 if(this.labellg > 0){
32805                     labelCls = ' col-lg-' + this.labellg;
32806                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32807                 }
32808
32809                 if(this.labelmd > 0){
32810                     labelCls = ' col-md-' + this.labelmd;
32811                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32812                 }
32813
32814                 if(this.labelsm > 0){
32815                     labelCls = ' col-sm-' + this.labelsm;
32816                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32817                 }
32818
32819                 if(this.labelxs > 0){
32820                     labelCls = ' col-xs-' + this.labelxs;
32821                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32822                 }
32823             }
32824             
32825             label.cls += ' ' + labelCls;
32826             
32827             cfg.cn.push(label);
32828         }
32829         
32830         Roo.each(['day', 'month', 'year'], function(t){
32831             cfg.cn.push({
32832                 tag : 'div',
32833                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32834             });
32835         }, this);
32836         
32837         return cfg;
32838     },
32839     
32840     inputEl: function ()
32841     {
32842         return this.el.select('.roo-date-split-field-group-value', true).first();
32843     },
32844     
32845     onRender : function(ct, position) 
32846     {
32847         var _this = this;
32848         
32849         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32850         
32851         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32852         
32853         this.dayField = new Roo.bootstrap.ComboBox({
32854             allowBlank : this.dayAllowBlank,
32855             alwaysQuery : true,
32856             displayField : 'value',
32857             editable : false,
32858             fieldLabel : '',
32859             forceSelection : true,
32860             mode : 'local',
32861             placeholder : this.dayPlaceholder,
32862             selectOnFocus : true,
32863             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32864             triggerAction : 'all',
32865             typeAhead : true,
32866             valueField : 'value',
32867             store : new Roo.data.SimpleStore({
32868                 data : (function() {    
32869                     var days = [];
32870                     _this.fireEvent('days', _this, days);
32871                     return days;
32872                 })(),
32873                 fields : [ 'value' ]
32874             }),
32875             listeners : {
32876                 select : function (_self, record, index)
32877                 {
32878                     _this.setValue(_this.getValue());
32879                 }
32880             }
32881         });
32882
32883         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32884         
32885         this.monthField = new Roo.bootstrap.MonthField({
32886             after : '<i class=\"fa fa-calendar\"></i>',
32887             allowBlank : this.monthAllowBlank,
32888             placeholder : this.monthPlaceholder,
32889             readOnly : true,
32890             listeners : {
32891                 render : function (_self)
32892                 {
32893                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32894                         e.preventDefault();
32895                         _self.focus();
32896                     });
32897                 },
32898                 select : function (_self, oldvalue, newvalue)
32899                 {
32900                     _this.setValue(_this.getValue());
32901                 }
32902             }
32903         });
32904         
32905         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32906         
32907         this.yearField = new Roo.bootstrap.ComboBox({
32908             allowBlank : this.yearAllowBlank,
32909             alwaysQuery : true,
32910             displayField : 'value',
32911             editable : false,
32912             fieldLabel : '',
32913             forceSelection : true,
32914             mode : 'local',
32915             placeholder : this.yearPlaceholder,
32916             selectOnFocus : true,
32917             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32918             triggerAction : 'all',
32919             typeAhead : true,
32920             valueField : 'value',
32921             store : new Roo.data.SimpleStore({
32922                 data : (function() {
32923                     var years = [];
32924                     _this.fireEvent('years', _this, years);
32925                     return years;
32926                 })(),
32927                 fields : [ 'value' ]
32928             }),
32929             listeners : {
32930                 select : function (_self, record, index)
32931                 {
32932                     _this.setValue(_this.getValue());
32933                 }
32934             }
32935         });
32936
32937         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32938     },
32939     
32940     setValue : function(v, format)
32941     {
32942         this.inputEl.dom.value = v;
32943         
32944         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32945         
32946         var d = Date.parseDate(v, f);
32947         
32948         if(!d){
32949             this.validate();
32950             return;
32951         }
32952         
32953         this.setDay(d.format(this.dayFormat));
32954         this.setMonth(d.format(this.monthFormat));
32955         this.setYear(d.format(this.yearFormat));
32956         
32957         this.validate();
32958         
32959         return;
32960     },
32961     
32962     setDay : function(v)
32963     {
32964         this.dayField.setValue(v);
32965         this.inputEl.dom.value = this.getValue();
32966         this.validate();
32967         return;
32968     },
32969     
32970     setMonth : function(v)
32971     {
32972         this.monthField.setValue(v, true);
32973         this.inputEl.dom.value = this.getValue();
32974         this.validate();
32975         return;
32976     },
32977     
32978     setYear : function(v)
32979     {
32980         this.yearField.setValue(v);
32981         this.inputEl.dom.value = this.getValue();
32982         this.validate();
32983         return;
32984     },
32985     
32986     getDay : function()
32987     {
32988         return this.dayField.getValue();
32989     },
32990     
32991     getMonth : function()
32992     {
32993         return this.monthField.getValue();
32994     },
32995     
32996     getYear : function()
32997     {
32998         return this.yearField.getValue();
32999     },
33000     
33001     getValue : function()
33002     {
33003         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33004         
33005         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33006         
33007         return date;
33008     },
33009     
33010     reset : function()
33011     {
33012         this.setDay('');
33013         this.setMonth('');
33014         this.setYear('');
33015         this.inputEl.dom.value = '';
33016         this.validate();
33017         return;
33018     },
33019     
33020     validate : function()
33021     {
33022         var d = this.dayField.validate();
33023         var m = this.monthField.validate();
33024         var y = this.yearField.validate();
33025         
33026         var valid = true;
33027         
33028         if(
33029                 (!this.dayAllowBlank && !d) ||
33030                 (!this.monthAllowBlank && !m) ||
33031                 (!this.yearAllowBlank && !y)
33032         ){
33033             valid = false;
33034         }
33035         
33036         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33037             return valid;
33038         }
33039         
33040         if(valid){
33041             this.markValid();
33042             return valid;
33043         }
33044         
33045         this.markInvalid();
33046         
33047         return valid;
33048     },
33049     
33050     markValid : function()
33051     {
33052         
33053         var label = this.el.select('label', true).first();
33054         var icon = this.el.select('i.fa-star', true).first();
33055
33056         if(label && icon){
33057             icon.remove();
33058         }
33059         
33060         this.fireEvent('valid', this);
33061     },
33062     
33063      /**
33064      * Mark this field as invalid
33065      * @param {String} msg The validation message
33066      */
33067     markInvalid : function(msg)
33068     {
33069         
33070         var label = this.el.select('label', true).first();
33071         var icon = this.el.select('i.fa-star', true).first();
33072
33073         if(label && !icon){
33074             this.el.select('.roo-date-split-field-label', true).createChild({
33075                 tag : 'i',
33076                 cls : 'text-danger fa fa-lg fa-star',
33077                 tooltip : 'This field is required',
33078                 style : 'margin-right:5px;'
33079             }, label, true);
33080         }
33081         
33082         this.fireEvent('invalid', this, msg);
33083     },
33084     
33085     clearInvalid : function()
33086     {
33087         var label = this.el.select('label', true).first();
33088         var icon = this.el.select('i.fa-star', true).first();
33089
33090         if(label && icon){
33091             icon.remove();
33092         }
33093         
33094         this.fireEvent('valid', this);
33095     },
33096     
33097     getName: function()
33098     {
33099         return this.name;
33100     }
33101     
33102 });
33103
33104  /**
33105  *
33106  * This is based on 
33107  * http://masonry.desandro.com
33108  *
33109  * The idea is to render all the bricks based on vertical width...
33110  *
33111  * The original code extends 'outlayer' - we might need to use that....
33112  * 
33113  */
33114
33115
33116 /**
33117  * @class Roo.bootstrap.LayoutMasonry
33118  * @extends Roo.bootstrap.Component
33119  * Bootstrap Layout Masonry class
33120  * 
33121  * @constructor
33122  * Create a new Element
33123  * @param {Object} config The config object
33124  */
33125
33126 Roo.bootstrap.LayoutMasonry = function(config){
33127     
33128     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33129     
33130     this.bricks = [];
33131     
33132     Roo.bootstrap.LayoutMasonry.register(this);
33133     
33134     this.addEvents({
33135         // raw events
33136         /**
33137          * @event layout
33138          * Fire after layout the items
33139          * @param {Roo.bootstrap.LayoutMasonry} this
33140          * @param {Roo.EventObject} e
33141          */
33142         "layout" : true
33143     });
33144     
33145 };
33146
33147 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33148     
33149     /**
33150      * @cfg {Boolean} isLayoutInstant = no animation?
33151      */   
33152     isLayoutInstant : false, // needed?
33153    
33154     /**
33155      * @cfg {Number} boxWidth  width of the columns
33156      */   
33157     boxWidth : 450,
33158     
33159       /**
33160      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33161      */   
33162     boxHeight : 0,
33163     
33164     /**
33165      * @cfg {Number} padWidth padding below box..
33166      */   
33167     padWidth : 10, 
33168     
33169     /**
33170      * @cfg {Number} gutter gutter width..
33171      */   
33172     gutter : 10,
33173     
33174      /**
33175      * @cfg {Number} maxCols maximum number of columns
33176      */   
33177     
33178     maxCols: 0,
33179     
33180     /**
33181      * @cfg {Boolean} isAutoInitial defalut true
33182      */   
33183     isAutoInitial : true, 
33184     
33185     containerWidth: 0,
33186     
33187     /**
33188      * @cfg {Boolean} isHorizontal defalut false
33189      */   
33190     isHorizontal : false, 
33191
33192     currentSize : null,
33193     
33194     tag: 'div',
33195     
33196     cls: '',
33197     
33198     bricks: null, //CompositeElement
33199     
33200     cols : 1,
33201     
33202     _isLayoutInited : false,
33203     
33204 //    isAlternative : false, // only use for vertical layout...
33205     
33206     /**
33207      * @cfg {Number} alternativePadWidth padding below box..
33208      */   
33209     alternativePadWidth : 50,
33210     
33211     selectedBrick : [],
33212     
33213     getAutoCreate : function(){
33214         
33215         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33216         
33217         var cfg = {
33218             tag: this.tag,
33219             cls: 'blog-masonary-wrapper ' + this.cls,
33220             cn : {
33221                 cls : 'mas-boxes masonary'
33222             }
33223         };
33224         
33225         return cfg;
33226     },
33227     
33228     getChildContainer: function( )
33229     {
33230         if (this.boxesEl) {
33231             return this.boxesEl;
33232         }
33233         
33234         this.boxesEl = this.el.select('.mas-boxes').first();
33235         
33236         return this.boxesEl;
33237     },
33238     
33239     
33240     initEvents : function()
33241     {
33242         var _this = this;
33243         
33244         if(this.isAutoInitial){
33245             Roo.log('hook children rendered');
33246             this.on('childrenrendered', function() {
33247                 Roo.log('children rendered');
33248                 _this.initial();
33249             } ,this);
33250         }
33251     },
33252     
33253     initial : function()
33254     {
33255         this.selectedBrick = [];
33256         
33257         this.currentSize = this.el.getBox(true);
33258         
33259         Roo.EventManager.onWindowResize(this.resize, this); 
33260
33261         if(!this.isAutoInitial){
33262             this.layout();
33263             return;
33264         }
33265         
33266         this.layout();
33267         
33268         return;
33269         //this.layout.defer(500,this);
33270         
33271     },
33272     
33273     resize : function()
33274     {
33275         var cs = this.el.getBox(true);
33276         
33277         if (
33278                 this.currentSize.width == cs.width && 
33279                 this.currentSize.x == cs.x && 
33280                 this.currentSize.height == cs.height && 
33281                 this.currentSize.y == cs.y 
33282         ) {
33283             Roo.log("no change in with or X or Y");
33284             return;
33285         }
33286         
33287         this.currentSize = cs;
33288         
33289         this.layout();
33290         
33291     },
33292     
33293     layout : function()
33294     {   
33295         this._resetLayout();
33296         
33297         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33298         
33299         this.layoutItems( isInstant );
33300       
33301         this._isLayoutInited = true;
33302         
33303         this.fireEvent('layout', this);
33304         
33305     },
33306     
33307     _resetLayout : function()
33308     {
33309         if(this.isHorizontal){
33310             this.horizontalMeasureColumns();
33311             return;
33312         }
33313         
33314         this.verticalMeasureColumns();
33315         
33316     },
33317     
33318     verticalMeasureColumns : function()
33319     {
33320         this.getContainerWidth();
33321         
33322 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33323 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33324 //            return;
33325 //        }
33326         
33327         var boxWidth = this.boxWidth + this.padWidth;
33328         
33329         if(this.containerWidth < this.boxWidth){
33330             boxWidth = this.containerWidth
33331         }
33332         
33333         var containerWidth = this.containerWidth;
33334         
33335         var cols = Math.floor(containerWidth / boxWidth);
33336         
33337         this.cols = Math.max( cols, 1 );
33338         
33339         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33340         
33341         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33342         
33343         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33344         
33345         this.colWidth = boxWidth + avail - this.padWidth;
33346         
33347         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33348         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33349     },
33350     
33351     horizontalMeasureColumns : function()
33352     {
33353         this.getContainerWidth();
33354         
33355         var boxWidth = this.boxWidth;
33356         
33357         if(this.containerWidth < boxWidth){
33358             boxWidth = this.containerWidth;
33359         }
33360         
33361         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33362         
33363         this.el.setHeight(boxWidth);
33364         
33365     },
33366     
33367     getContainerWidth : function()
33368     {
33369         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33370     },
33371     
33372     layoutItems : function( isInstant )
33373     {
33374         Roo.log(this.bricks);
33375         
33376         var items = Roo.apply([], this.bricks);
33377         
33378         if(this.isHorizontal){
33379             this._horizontalLayoutItems( items , isInstant );
33380             return;
33381         }
33382         
33383 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33384 //            this._verticalAlternativeLayoutItems( items , isInstant );
33385 //            return;
33386 //        }
33387         
33388         this._verticalLayoutItems( items , isInstant );
33389         
33390     },
33391     
33392     _verticalLayoutItems : function ( items , isInstant)
33393     {
33394         if ( !items || !items.length ) {
33395             return;
33396         }
33397         
33398         var standard = [
33399             ['xs', 'xs', 'xs', 'tall'],
33400             ['xs', 'xs', 'tall'],
33401             ['xs', 'xs', 'sm'],
33402             ['xs', 'xs', 'xs'],
33403             ['xs', 'tall'],
33404             ['xs', 'sm'],
33405             ['xs', 'xs'],
33406             ['xs'],
33407             
33408             ['sm', 'xs', 'xs'],
33409             ['sm', 'xs'],
33410             ['sm'],
33411             
33412             ['tall', 'xs', 'xs', 'xs'],
33413             ['tall', 'xs', 'xs'],
33414             ['tall', 'xs'],
33415             ['tall']
33416             
33417         ];
33418         
33419         var queue = [];
33420         
33421         var boxes = [];
33422         
33423         var box = [];
33424         
33425         Roo.each(items, function(item, k){
33426             
33427             switch (item.size) {
33428                 // these layouts take up a full box,
33429                 case 'md' :
33430                 case 'md-left' :
33431                 case 'md-right' :
33432                 case 'wide' :
33433                     
33434                     if(box.length){
33435                         boxes.push(box);
33436                         box = [];
33437                     }
33438                     
33439                     boxes.push([item]);
33440                     
33441                     break;
33442                     
33443                 case 'xs' :
33444                 case 'sm' :
33445                 case 'tall' :
33446                     
33447                     box.push(item);
33448                     
33449                     break;
33450                 default :
33451                     break;
33452                     
33453             }
33454             
33455         }, this);
33456         
33457         if(box.length){
33458             boxes.push(box);
33459             box = [];
33460         }
33461         
33462         var filterPattern = function(box, length)
33463         {
33464             if(!box.length){
33465                 return;
33466             }
33467             
33468             var match = false;
33469             
33470             var pattern = box.slice(0, length);
33471             
33472             var format = [];
33473             
33474             Roo.each(pattern, function(i){
33475                 format.push(i.size);
33476             }, this);
33477             
33478             Roo.each(standard, function(s){
33479                 
33480                 if(String(s) != String(format)){
33481                     return;
33482                 }
33483                 
33484                 match = true;
33485                 return false;
33486                 
33487             }, this);
33488             
33489             if(!match && length == 1){
33490                 return;
33491             }
33492             
33493             if(!match){
33494                 filterPattern(box, length - 1);
33495                 return;
33496             }
33497                 
33498             queue.push(pattern);
33499
33500             box = box.slice(length, box.length);
33501
33502             filterPattern(box, 4);
33503
33504             return;
33505             
33506         }
33507         
33508         Roo.each(boxes, function(box, k){
33509             
33510             if(!box.length){
33511                 return;
33512             }
33513             
33514             if(box.length == 1){
33515                 queue.push(box);
33516                 return;
33517             }
33518             
33519             filterPattern(box, 4);
33520             
33521         }, this);
33522         
33523         this._processVerticalLayoutQueue( queue, isInstant );
33524         
33525     },
33526     
33527 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33528 //    {
33529 //        if ( !items || !items.length ) {
33530 //            return;
33531 //        }
33532 //
33533 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33534 //        
33535 //    },
33536     
33537     _horizontalLayoutItems : function ( items , isInstant)
33538     {
33539         if ( !items || !items.length || items.length < 3) {
33540             return;
33541         }
33542         
33543         items.reverse();
33544         
33545         var eItems = items.slice(0, 3);
33546         
33547         items = items.slice(3, items.length);
33548         
33549         var standard = [
33550             ['xs', 'xs', 'xs', 'wide'],
33551             ['xs', 'xs', 'wide'],
33552             ['xs', 'xs', 'sm'],
33553             ['xs', 'xs', 'xs'],
33554             ['xs', 'wide'],
33555             ['xs', 'sm'],
33556             ['xs', 'xs'],
33557             ['xs'],
33558             
33559             ['sm', 'xs', 'xs'],
33560             ['sm', 'xs'],
33561             ['sm'],
33562             
33563             ['wide', 'xs', 'xs', 'xs'],
33564             ['wide', 'xs', 'xs'],
33565             ['wide', 'xs'],
33566             ['wide'],
33567             
33568             ['wide-thin']
33569         ];
33570         
33571         var queue = [];
33572         
33573         var boxes = [];
33574         
33575         var box = [];
33576         
33577         Roo.each(items, function(item, k){
33578             
33579             switch (item.size) {
33580                 case 'md' :
33581                 case 'md-left' :
33582                 case 'md-right' :
33583                 case 'tall' :
33584                     
33585                     if(box.length){
33586                         boxes.push(box);
33587                         box = [];
33588                     }
33589                     
33590                     boxes.push([item]);
33591                     
33592                     break;
33593                     
33594                 case 'xs' :
33595                 case 'sm' :
33596                 case 'wide' :
33597                 case 'wide-thin' :
33598                     
33599                     box.push(item);
33600                     
33601                     break;
33602                 default :
33603                     break;
33604                     
33605             }
33606             
33607         }, this);
33608         
33609         if(box.length){
33610             boxes.push(box);
33611             box = [];
33612         }
33613         
33614         var filterPattern = function(box, length)
33615         {
33616             if(!box.length){
33617                 return;
33618             }
33619             
33620             var match = false;
33621             
33622             var pattern = box.slice(0, length);
33623             
33624             var format = [];
33625             
33626             Roo.each(pattern, function(i){
33627                 format.push(i.size);
33628             }, this);
33629             
33630             Roo.each(standard, function(s){
33631                 
33632                 if(String(s) != String(format)){
33633                     return;
33634                 }
33635                 
33636                 match = true;
33637                 return false;
33638                 
33639             }, this);
33640             
33641             if(!match && length == 1){
33642                 return;
33643             }
33644             
33645             if(!match){
33646                 filterPattern(box, length - 1);
33647                 return;
33648             }
33649                 
33650             queue.push(pattern);
33651
33652             box = box.slice(length, box.length);
33653
33654             filterPattern(box, 4);
33655
33656             return;
33657             
33658         }
33659         
33660         Roo.each(boxes, function(box, k){
33661             
33662             if(!box.length){
33663                 return;
33664             }
33665             
33666             if(box.length == 1){
33667                 queue.push(box);
33668                 return;
33669             }
33670             
33671             filterPattern(box, 4);
33672             
33673         }, this);
33674         
33675         
33676         var prune = [];
33677         
33678         var pos = this.el.getBox(true);
33679         
33680         var minX = pos.x;
33681         
33682         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33683         
33684         var hit_end = false;
33685         
33686         Roo.each(queue, function(box){
33687             
33688             if(hit_end){
33689                 
33690                 Roo.each(box, function(b){
33691                 
33692                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33693                     b.el.hide();
33694
33695                 }, this);
33696
33697                 return;
33698             }
33699             
33700             var mx = 0;
33701             
33702             Roo.each(box, function(b){
33703                 
33704                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33705                 b.el.show();
33706
33707                 mx = Math.max(mx, b.x);
33708                 
33709             }, this);
33710             
33711             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33712             
33713             if(maxX < minX){
33714                 
33715                 Roo.each(box, function(b){
33716                 
33717                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33718                     b.el.hide();
33719                     
33720                 }, this);
33721                 
33722                 hit_end = true;
33723                 
33724                 return;
33725             }
33726             
33727             prune.push(box);
33728             
33729         }, this);
33730         
33731         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33732     },
33733     
33734     /** Sets position of item in DOM
33735     * @param {Element} item
33736     * @param {Number} x - horizontal position
33737     * @param {Number} y - vertical position
33738     * @param {Boolean} isInstant - disables transitions
33739     */
33740     _processVerticalLayoutQueue : function( queue, isInstant )
33741     {
33742         var pos = this.el.getBox(true);
33743         var x = pos.x;
33744         var y = pos.y;
33745         var maxY = [];
33746         
33747         for (var i = 0; i < this.cols; i++){
33748             maxY[i] = pos.y;
33749         }
33750         
33751         Roo.each(queue, function(box, k){
33752             
33753             var col = k % this.cols;
33754             
33755             Roo.each(box, function(b,kk){
33756                 
33757                 b.el.position('absolute');
33758                 
33759                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33760                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33761                 
33762                 if(b.size == 'md-left' || b.size == 'md-right'){
33763                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33764                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33765                 }
33766                 
33767                 b.el.setWidth(width);
33768                 b.el.setHeight(height);
33769                 // iframe?
33770                 b.el.select('iframe',true).setSize(width,height);
33771                 
33772             }, this);
33773             
33774             for (var i = 0; i < this.cols; i++){
33775                 
33776                 if(maxY[i] < maxY[col]){
33777                     col = i;
33778                     continue;
33779                 }
33780                 
33781                 col = Math.min(col, i);
33782                 
33783             }
33784             
33785             x = pos.x + col * (this.colWidth + this.padWidth);
33786             
33787             y = maxY[col];
33788             
33789             var positions = [];
33790             
33791             switch (box.length){
33792                 case 1 :
33793                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33794                     break;
33795                 case 2 :
33796                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33797                     break;
33798                 case 3 :
33799                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33800                     break;
33801                 case 4 :
33802                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33803                     break;
33804                 default :
33805                     break;
33806             }
33807             
33808             Roo.each(box, function(b,kk){
33809                 
33810                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33811                 
33812                 var sz = b.el.getSize();
33813                 
33814                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33815                 
33816             }, this);
33817             
33818         }, this);
33819         
33820         var mY = 0;
33821         
33822         for (var i = 0; i < this.cols; i++){
33823             mY = Math.max(mY, maxY[i]);
33824         }
33825         
33826         this.el.setHeight(mY - pos.y);
33827         
33828     },
33829     
33830 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33831 //    {
33832 //        var pos = this.el.getBox(true);
33833 //        var x = pos.x;
33834 //        var y = pos.y;
33835 //        var maxX = pos.right;
33836 //        
33837 //        var maxHeight = 0;
33838 //        
33839 //        Roo.each(items, function(item, k){
33840 //            
33841 //            var c = k % 2;
33842 //            
33843 //            item.el.position('absolute');
33844 //                
33845 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33846 //
33847 //            item.el.setWidth(width);
33848 //
33849 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33850 //
33851 //            item.el.setHeight(height);
33852 //            
33853 //            if(c == 0){
33854 //                item.el.setXY([x, y], isInstant ? false : true);
33855 //            } else {
33856 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33857 //            }
33858 //            
33859 //            y = y + height + this.alternativePadWidth;
33860 //            
33861 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33862 //            
33863 //        }, this);
33864 //        
33865 //        this.el.setHeight(maxHeight);
33866 //        
33867 //    },
33868     
33869     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33870     {
33871         var pos = this.el.getBox(true);
33872         
33873         var minX = pos.x;
33874         var minY = pos.y;
33875         
33876         var maxX = pos.right;
33877         
33878         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33879         
33880         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33881         
33882         Roo.each(queue, function(box, k){
33883             
33884             Roo.each(box, function(b, kk){
33885                 
33886                 b.el.position('absolute');
33887                 
33888                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33889                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33890                 
33891                 if(b.size == 'md-left' || b.size == 'md-right'){
33892                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33893                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33894                 }
33895                 
33896                 b.el.setWidth(width);
33897                 b.el.setHeight(height);
33898                 
33899             }, this);
33900             
33901             if(!box.length){
33902                 return;
33903             }
33904             
33905             var positions = [];
33906             
33907             switch (box.length){
33908                 case 1 :
33909                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33910                     break;
33911                 case 2 :
33912                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33913                     break;
33914                 case 3 :
33915                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33916                     break;
33917                 case 4 :
33918                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33919                     break;
33920                 default :
33921                     break;
33922             }
33923             
33924             Roo.each(box, function(b,kk){
33925                 
33926                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33927                 
33928                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33929                 
33930             }, this);
33931             
33932         }, this);
33933         
33934     },
33935     
33936     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33937     {
33938         Roo.each(eItems, function(b,k){
33939             
33940             b.size = (k == 0) ? 'sm' : 'xs';
33941             b.x = (k == 0) ? 2 : 1;
33942             b.y = (k == 0) ? 2 : 1;
33943             
33944             b.el.position('absolute');
33945             
33946             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33947                 
33948             b.el.setWidth(width);
33949             
33950             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33951             
33952             b.el.setHeight(height);
33953             
33954         }, this);
33955
33956         var positions = [];
33957         
33958         positions.push({
33959             x : maxX - this.unitWidth * 2 - this.gutter,
33960             y : minY
33961         });
33962         
33963         positions.push({
33964             x : maxX - this.unitWidth,
33965             y : minY + (this.unitWidth + this.gutter) * 2
33966         });
33967         
33968         positions.push({
33969             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33970             y : minY
33971         });
33972         
33973         Roo.each(eItems, function(b,k){
33974             
33975             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33976
33977         }, this);
33978         
33979     },
33980     
33981     getVerticalOneBoxColPositions : function(x, y, box)
33982     {
33983         var pos = [];
33984         
33985         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33986         
33987         if(box[0].size == 'md-left'){
33988             rand = 0;
33989         }
33990         
33991         if(box[0].size == 'md-right'){
33992             rand = 1;
33993         }
33994         
33995         pos.push({
33996             x : x + (this.unitWidth + this.gutter) * rand,
33997             y : y
33998         });
33999         
34000         return pos;
34001     },
34002     
34003     getVerticalTwoBoxColPositions : function(x, y, box)
34004     {
34005         var pos = [];
34006         
34007         if(box[0].size == 'xs'){
34008             
34009             pos.push({
34010                 x : x,
34011                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34012             });
34013
34014             pos.push({
34015                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34016                 y : y
34017             });
34018             
34019             return pos;
34020             
34021         }
34022         
34023         pos.push({
34024             x : x,
34025             y : y
34026         });
34027
34028         pos.push({
34029             x : x + (this.unitWidth + this.gutter) * 2,
34030             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34031         });
34032         
34033         return pos;
34034         
34035     },
34036     
34037     getVerticalThreeBoxColPositions : function(x, y, box)
34038     {
34039         var pos = [];
34040         
34041         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34042             
34043             pos.push({
34044                 x : x,
34045                 y : y
34046             });
34047
34048             pos.push({
34049                 x : x + (this.unitWidth + this.gutter) * 1,
34050                 y : y
34051             });
34052             
34053             pos.push({
34054                 x : x + (this.unitWidth + this.gutter) * 2,
34055                 y : y
34056             });
34057             
34058             return pos;
34059             
34060         }
34061         
34062         if(box[0].size == 'xs' && box[1].size == 'xs'){
34063             
34064             pos.push({
34065                 x : x,
34066                 y : y
34067             });
34068
34069             pos.push({
34070                 x : x,
34071                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34072             });
34073             
34074             pos.push({
34075                 x : x + (this.unitWidth + this.gutter) * 1,
34076                 y : y
34077             });
34078             
34079             return pos;
34080             
34081         }
34082         
34083         pos.push({
34084             x : x,
34085             y : y
34086         });
34087
34088         pos.push({
34089             x : x + (this.unitWidth + this.gutter) * 2,
34090             y : y
34091         });
34092
34093         pos.push({
34094             x : x + (this.unitWidth + this.gutter) * 2,
34095             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34096         });
34097             
34098         return pos;
34099         
34100     },
34101     
34102     getVerticalFourBoxColPositions : function(x, y, box)
34103     {
34104         var pos = [];
34105         
34106         if(box[0].size == 'xs'){
34107             
34108             pos.push({
34109                 x : x,
34110                 y : y
34111             });
34112
34113             pos.push({
34114                 x : x,
34115                 y : y + (this.unitHeight + this.gutter) * 1
34116             });
34117             
34118             pos.push({
34119                 x : x,
34120                 y : y + (this.unitHeight + this.gutter) * 2
34121             });
34122             
34123             pos.push({
34124                 x : x + (this.unitWidth + this.gutter) * 1,
34125                 y : y
34126             });
34127             
34128             return pos;
34129             
34130         }
34131         
34132         pos.push({
34133             x : x,
34134             y : y
34135         });
34136
34137         pos.push({
34138             x : x + (this.unitWidth + this.gutter) * 2,
34139             y : y
34140         });
34141
34142         pos.push({
34143             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34144             y : y + (this.unitHeight + this.gutter) * 1
34145         });
34146
34147         pos.push({
34148             x : x + (this.unitWidth + this.gutter) * 2,
34149             y : y + (this.unitWidth + this.gutter) * 2
34150         });
34151
34152         return pos;
34153         
34154     },
34155     
34156     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34157     {
34158         var pos = [];
34159         
34160         if(box[0].size == 'md-left'){
34161             pos.push({
34162                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34163                 y : minY
34164             });
34165             
34166             return pos;
34167         }
34168         
34169         if(box[0].size == 'md-right'){
34170             pos.push({
34171                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34172                 y : minY + (this.unitWidth + this.gutter) * 1
34173             });
34174             
34175             return pos;
34176         }
34177         
34178         var rand = Math.floor(Math.random() * (4 - box[0].y));
34179         
34180         pos.push({
34181             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34182             y : minY + (this.unitWidth + this.gutter) * rand
34183         });
34184         
34185         return pos;
34186         
34187     },
34188     
34189     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34190     {
34191         var pos = [];
34192         
34193         if(box[0].size == 'xs'){
34194             
34195             pos.push({
34196                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34197                 y : minY
34198             });
34199
34200             pos.push({
34201                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34202                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34203             });
34204             
34205             return pos;
34206             
34207         }
34208         
34209         pos.push({
34210             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34211             y : minY
34212         });
34213
34214         pos.push({
34215             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34216             y : minY + (this.unitWidth + this.gutter) * 2
34217         });
34218         
34219         return pos;
34220         
34221     },
34222     
34223     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34224     {
34225         var pos = [];
34226         
34227         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34228             
34229             pos.push({
34230                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34231                 y : minY
34232             });
34233
34234             pos.push({
34235                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34236                 y : minY + (this.unitWidth + this.gutter) * 1
34237             });
34238             
34239             pos.push({
34240                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34241                 y : minY + (this.unitWidth + this.gutter) * 2
34242             });
34243             
34244             return pos;
34245             
34246         }
34247         
34248         if(box[0].size == 'xs' && box[1].size == 'xs'){
34249             
34250             pos.push({
34251                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34252                 y : minY
34253             });
34254
34255             pos.push({
34256                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34257                 y : minY
34258             });
34259             
34260             pos.push({
34261                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34262                 y : minY + (this.unitWidth + this.gutter) * 1
34263             });
34264             
34265             return pos;
34266             
34267         }
34268         
34269         pos.push({
34270             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34271             y : minY
34272         });
34273
34274         pos.push({
34275             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34276             y : minY + (this.unitWidth + this.gutter) * 2
34277         });
34278
34279         pos.push({
34280             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34281             y : minY + (this.unitWidth + this.gutter) * 2
34282         });
34283             
34284         return pos;
34285         
34286     },
34287     
34288     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34289     {
34290         var pos = [];
34291         
34292         if(box[0].size == 'xs'){
34293             
34294             pos.push({
34295                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34296                 y : minY
34297             });
34298
34299             pos.push({
34300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34301                 y : minY
34302             });
34303             
34304             pos.push({
34305                 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),
34306                 y : minY
34307             });
34308             
34309             pos.push({
34310                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34311                 y : minY + (this.unitWidth + this.gutter) * 1
34312             });
34313             
34314             return pos;
34315             
34316         }
34317         
34318         pos.push({
34319             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34320             y : minY
34321         });
34322         
34323         pos.push({
34324             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34325             y : minY + (this.unitWidth + this.gutter) * 2
34326         });
34327         
34328         pos.push({
34329             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34330             y : minY + (this.unitWidth + this.gutter) * 2
34331         });
34332         
34333         pos.push({
34334             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),
34335             y : minY + (this.unitWidth + this.gutter) * 2
34336         });
34337
34338         return pos;
34339         
34340     },
34341     
34342     /**
34343     * remove a Masonry Brick
34344     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34345     */
34346     removeBrick : function(brick_id)
34347     {
34348         if (!brick_id) {
34349             return;
34350         }
34351         
34352         for (var i = 0; i<this.bricks.length; i++) {
34353             if (this.bricks[i].id == brick_id) {
34354                 this.bricks.splice(i,1);
34355                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34356                 this.initial();
34357             }
34358         }
34359     },
34360     
34361     /**
34362     * adds a Masonry Brick
34363     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34364     */
34365     addBrick : function(cfg)
34366     {
34367         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34368         //this.register(cn);
34369         cn.parentId = this.id;
34370         cn.render(this.el);
34371         return cn;
34372     },
34373     
34374     /**
34375     * register a Masonry Brick
34376     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34377     */
34378     
34379     register : function(brick)
34380     {
34381         this.bricks.push(brick);
34382         brick.masonryId = this.id;
34383     },
34384     
34385     /**
34386     * clear all the Masonry Brick
34387     */
34388     clearAll : function()
34389     {
34390         this.bricks = [];
34391         //this.getChildContainer().dom.innerHTML = "";
34392         this.el.dom.innerHTML = '';
34393     },
34394     
34395     getSelected : function()
34396     {
34397         if (!this.selectedBrick) {
34398             return false;
34399         }
34400         
34401         return this.selectedBrick;
34402     }
34403 });
34404
34405 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34406     
34407     groups: {},
34408      /**
34409     * register a Masonry Layout
34410     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34411     */
34412     
34413     register : function(layout)
34414     {
34415         this.groups[layout.id] = layout;
34416     },
34417     /**
34418     * fetch a  Masonry Layout based on the masonry layout ID
34419     * @param {string} the masonry layout to add
34420     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34421     */
34422     
34423     get: function(layout_id) {
34424         if (typeof(this.groups[layout_id]) == 'undefined') {
34425             return false;
34426         }
34427         return this.groups[layout_id] ;
34428     }
34429     
34430     
34431     
34432 });
34433
34434  
34435
34436  /**
34437  *
34438  * This is based on 
34439  * http://masonry.desandro.com
34440  *
34441  * The idea is to render all the bricks based on vertical width...
34442  *
34443  * The original code extends 'outlayer' - we might need to use that....
34444  * 
34445  */
34446
34447
34448 /**
34449  * @class Roo.bootstrap.LayoutMasonryAuto
34450  * @extends Roo.bootstrap.Component
34451  * Bootstrap Layout Masonry class
34452  * 
34453  * @constructor
34454  * Create a new Element
34455  * @param {Object} config The config object
34456  */
34457
34458 Roo.bootstrap.LayoutMasonryAuto = function(config){
34459     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34460 };
34461
34462 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34463     
34464       /**
34465      * @cfg {Boolean} isFitWidth  - resize the width..
34466      */   
34467     isFitWidth : false,  // options..
34468     /**
34469      * @cfg {Boolean} isOriginLeft = left align?
34470      */   
34471     isOriginLeft : true,
34472     /**
34473      * @cfg {Boolean} isOriginTop = top align?
34474      */   
34475     isOriginTop : false,
34476     /**
34477      * @cfg {Boolean} isLayoutInstant = no animation?
34478      */   
34479     isLayoutInstant : false, // needed?
34480     /**
34481      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34482      */   
34483     isResizingContainer : true,
34484     /**
34485      * @cfg {Number} columnWidth  width of the columns 
34486      */   
34487     
34488     columnWidth : 0,
34489     
34490     /**
34491      * @cfg {Number} maxCols maximum number of columns
34492      */   
34493     
34494     maxCols: 0,
34495     /**
34496      * @cfg {Number} padHeight padding below box..
34497      */   
34498     
34499     padHeight : 10, 
34500     
34501     /**
34502      * @cfg {Boolean} isAutoInitial defalut true
34503      */   
34504     
34505     isAutoInitial : true, 
34506     
34507     // private?
34508     gutter : 0,
34509     
34510     containerWidth: 0,
34511     initialColumnWidth : 0,
34512     currentSize : null,
34513     
34514     colYs : null, // array.
34515     maxY : 0,
34516     padWidth: 10,
34517     
34518     
34519     tag: 'div',
34520     cls: '',
34521     bricks: null, //CompositeElement
34522     cols : 0, // array?
34523     // element : null, // wrapped now this.el
34524     _isLayoutInited : null, 
34525     
34526     
34527     getAutoCreate : function(){
34528         
34529         var cfg = {
34530             tag: this.tag,
34531             cls: 'blog-masonary-wrapper ' + this.cls,
34532             cn : {
34533                 cls : 'mas-boxes masonary'
34534             }
34535         };
34536         
34537         return cfg;
34538     },
34539     
34540     getChildContainer: function( )
34541     {
34542         if (this.boxesEl) {
34543             return this.boxesEl;
34544         }
34545         
34546         this.boxesEl = this.el.select('.mas-boxes').first();
34547         
34548         return this.boxesEl;
34549     },
34550     
34551     
34552     initEvents : function()
34553     {
34554         var _this = this;
34555         
34556         if(this.isAutoInitial){
34557             Roo.log('hook children rendered');
34558             this.on('childrenrendered', function() {
34559                 Roo.log('children rendered');
34560                 _this.initial();
34561             } ,this);
34562         }
34563         
34564     },
34565     
34566     initial : function()
34567     {
34568         this.reloadItems();
34569
34570         this.currentSize = this.el.getBox(true);
34571
34572         /// was window resize... - let's see if this works..
34573         Roo.EventManager.onWindowResize(this.resize, this); 
34574
34575         if(!this.isAutoInitial){
34576             this.layout();
34577             return;
34578         }
34579         
34580         this.layout.defer(500,this);
34581     },
34582     
34583     reloadItems: function()
34584     {
34585         this.bricks = this.el.select('.masonry-brick', true);
34586         
34587         this.bricks.each(function(b) {
34588             //Roo.log(b.getSize());
34589             if (!b.attr('originalwidth')) {
34590                 b.attr('originalwidth',  b.getSize().width);
34591             }
34592             
34593         });
34594         
34595         Roo.log(this.bricks.elements.length);
34596     },
34597     
34598     resize : function()
34599     {
34600         Roo.log('resize');
34601         var cs = this.el.getBox(true);
34602         
34603         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34604             Roo.log("no change in with or X");
34605             return;
34606         }
34607         this.currentSize = cs;
34608         this.layout();
34609     },
34610     
34611     layout : function()
34612     {
34613          Roo.log('layout');
34614         this._resetLayout();
34615         //this._manageStamps();
34616       
34617         // don't animate first layout
34618         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34619         this.layoutItems( isInstant );
34620       
34621         // flag for initalized
34622         this._isLayoutInited = true;
34623     },
34624     
34625     layoutItems : function( isInstant )
34626     {
34627         //var items = this._getItemsForLayout( this.items );
34628         // original code supports filtering layout items.. we just ignore it..
34629         
34630         this._layoutItems( this.bricks , isInstant );
34631       
34632         this._postLayout();
34633     },
34634     _layoutItems : function ( items , isInstant)
34635     {
34636        //this.fireEvent( 'layout', this, items );
34637     
34638
34639         if ( !items || !items.elements.length ) {
34640           // no items, emit event with empty array
34641             return;
34642         }
34643
34644         var queue = [];
34645         items.each(function(item) {
34646             Roo.log("layout item");
34647             Roo.log(item);
34648             // get x/y object from method
34649             var position = this._getItemLayoutPosition( item );
34650             // enqueue
34651             position.item = item;
34652             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34653             queue.push( position );
34654         }, this);
34655       
34656         this._processLayoutQueue( queue );
34657     },
34658     /** Sets position of item in DOM
34659     * @param {Element} item
34660     * @param {Number} x - horizontal position
34661     * @param {Number} y - vertical position
34662     * @param {Boolean} isInstant - disables transitions
34663     */
34664     _processLayoutQueue : function( queue )
34665     {
34666         for ( var i=0, len = queue.length; i < len; i++ ) {
34667             var obj = queue[i];
34668             obj.item.position('absolute');
34669             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34670         }
34671     },
34672       
34673     
34674     /**
34675     * Any logic you want to do after each layout,
34676     * i.e. size the container
34677     */
34678     _postLayout : function()
34679     {
34680         this.resizeContainer();
34681     },
34682     
34683     resizeContainer : function()
34684     {
34685         if ( !this.isResizingContainer ) {
34686             return;
34687         }
34688         var size = this._getContainerSize();
34689         if ( size ) {
34690             this.el.setSize(size.width,size.height);
34691             this.boxesEl.setSize(size.width,size.height);
34692         }
34693     },
34694     
34695     
34696     
34697     _resetLayout : function()
34698     {
34699         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34700         this.colWidth = this.el.getWidth();
34701         //this.gutter = this.el.getWidth(); 
34702         
34703         this.measureColumns();
34704
34705         // reset column Y
34706         var i = this.cols;
34707         this.colYs = [];
34708         while (i--) {
34709             this.colYs.push( 0 );
34710         }
34711     
34712         this.maxY = 0;
34713     },
34714
34715     measureColumns : function()
34716     {
34717         this.getContainerWidth();
34718       // if columnWidth is 0, default to outerWidth of first item
34719         if ( !this.columnWidth ) {
34720             var firstItem = this.bricks.first();
34721             Roo.log(firstItem);
34722             this.columnWidth  = this.containerWidth;
34723             if (firstItem && firstItem.attr('originalwidth') ) {
34724                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34725             }
34726             // columnWidth fall back to item of first element
34727             Roo.log("set column width?");
34728                         this.initialColumnWidth = this.columnWidth  ;
34729
34730             // if first elem has no width, default to size of container
34731             
34732         }
34733         
34734         
34735         if (this.initialColumnWidth) {
34736             this.columnWidth = this.initialColumnWidth;
34737         }
34738         
34739         
34740             
34741         // column width is fixed at the top - however if container width get's smaller we should
34742         // reduce it...
34743         
34744         // this bit calcs how man columns..
34745             
34746         var columnWidth = this.columnWidth += this.gutter;
34747       
34748         // calculate columns
34749         var containerWidth = this.containerWidth + this.gutter;
34750         
34751         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34752         // fix rounding errors, typically with gutters
34753         var excess = columnWidth - containerWidth % columnWidth;
34754         
34755         
34756         // if overshoot is less than a pixel, round up, otherwise floor it
34757         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34758         cols = Math[ mathMethod ]( cols );
34759         this.cols = Math.max( cols, 1 );
34760         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34761         
34762          // padding positioning..
34763         var totalColWidth = this.cols * this.columnWidth;
34764         var padavail = this.containerWidth - totalColWidth;
34765         // so for 2 columns - we need 3 'pads'
34766         
34767         var padNeeded = (1+this.cols) * this.padWidth;
34768         
34769         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34770         
34771         this.columnWidth += padExtra
34772         //this.padWidth = Math.floor(padavail /  ( this.cols));
34773         
34774         // adjust colum width so that padding is fixed??
34775         
34776         // we have 3 columns ... total = width * 3
34777         // we have X left over... that should be used by 
34778         
34779         //if (this.expandC) {
34780             
34781         //}
34782         
34783         
34784         
34785     },
34786     
34787     getContainerWidth : function()
34788     {
34789        /* // container is parent if fit width
34790         var container = this.isFitWidth ? this.element.parentNode : this.element;
34791         // check that this.size and size are there
34792         // IE8 triggers resize on body size change, so they might not be
34793         
34794         var size = getSize( container );  //FIXME
34795         this.containerWidth = size && size.innerWidth; //FIXME
34796         */
34797          
34798         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34799         
34800     },
34801     
34802     _getItemLayoutPosition : function( item )  // what is item?
34803     {
34804         // we resize the item to our columnWidth..
34805       
34806         item.setWidth(this.columnWidth);
34807         item.autoBoxAdjust  = false;
34808         
34809         var sz = item.getSize();
34810  
34811         // how many columns does this brick span
34812         var remainder = this.containerWidth % this.columnWidth;
34813         
34814         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34815         // round if off by 1 pixel, otherwise use ceil
34816         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34817         colSpan = Math.min( colSpan, this.cols );
34818         
34819         // normally this should be '1' as we dont' currently allow multi width columns..
34820         
34821         var colGroup = this._getColGroup( colSpan );
34822         // get the minimum Y value from the columns
34823         var minimumY = Math.min.apply( Math, colGroup );
34824         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34825         
34826         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34827          
34828         // position the brick
34829         var position = {
34830             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34831             y: this.currentSize.y + minimumY + this.padHeight
34832         };
34833         
34834         Roo.log(position);
34835         // apply setHeight to necessary columns
34836         var setHeight = minimumY + sz.height + this.padHeight;
34837         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34838         
34839         var setSpan = this.cols + 1 - colGroup.length;
34840         for ( var i = 0; i < setSpan; i++ ) {
34841           this.colYs[ shortColIndex + i ] = setHeight ;
34842         }
34843       
34844         return position;
34845     },
34846     
34847     /**
34848      * @param {Number} colSpan - number of columns the element spans
34849      * @returns {Array} colGroup
34850      */
34851     _getColGroup : function( colSpan )
34852     {
34853         if ( colSpan < 2 ) {
34854           // if brick spans only one column, use all the column Ys
34855           return this.colYs;
34856         }
34857       
34858         var colGroup = [];
34859         // how many different places could this brick fit horizontally
34860         var groupCount = this.cols + 1 - colSpan;
34861         // for each group potential horizontal position
34862         for ( var i = 0; i < groupCount; i++ ) {
34863           // make an array of colY values for that one group
34864           var groupColYs = this.colYs.slice( i, i + colSpan );
34865           // and get the max value of the array
34866           colGroup[i] = Math.max.apply( Math, groupColYs );
34867         }
34868         return colGroup;
34869     },
34870     /*
34871     _manageStamp : function( stamp )
34872     {
34873         var stampSize =  stamp.getSize();
34874         var offset = stamp.getBox();
34875         // get the columns that this stamp affects
34876         var firstX = this.isOriginLeft ? offset.x : offset.right;
34877         var lastX = firstX + stampSize.width;
34878         var firstCol = Math.floor( firstX / this.columnWidth );
34879         firstCol = Math.max( 0, firstCol );
34880         
34881         var lastCol = Math.floor( lastX / this.columnWidth );
34882         // lastCol should not go over if multiple of columnWidth #425
34883         lastCol -= lastX % this.columnWidth ? 0 : 1;
34884         lastCol = Math.min( this.cols - 1, lastCol );
34885         
34886         // set colYs to bottom of the stamp
34887         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34888             stampSize.height;
34889             
34890         for ( var i = firstCol; i <= lastCol; i++ ) {
34891           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34892         }
34893     },
34894     */
34895     
34896     _getContainerSize : function()
34897     {
34898         this.maxY = Math.max.apply( Math, this.colYs );
34899         var size = {
34900             height: this.maxY
34901         };
34902       
34903         if ( this.isFitWidth ) {
34904             size.width = this._getContainerFitWidth();
34905         }
34906       
34907         return size;
34908     },
34909     
34910     _getContainerFitWidth : function()
34911     {
34912         var unusedCols = 0;
34913         // count unused columns
34914         var i = this.cols;
34915         while ( --i ) {
34916           if ( this.colYs[i] !== 0 ) {
34917             break;
34918           }
34919           unusedCols++;
34920         }
34921         // fit container to columns that have been used
34922         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34923     },
34924     
34925     needsResizeLayout : function()
34926     {
34927         var previousWidth = this.containerWidth;
34928         this.getContainerWidth();
34929         return previousWidth !== this.containerWidth;
34930     }
34931  
34932 });
34933
34934  
34935
34936  /*
34937  * - LGPL
34938  *
34939  * element
34940  * 
34941  */
34942
34943 /**
34944  * @class Roo.bootstrap.MasonryBrick
34945  * @extends Roo.bootstrap.Component
34946  * Bootstrap MasonryBrick class
34947  * 
34948  * @constructor
34949  * Create a new MasonryBrick
34950  * @param {Object} config The config object
34951  */
34952
34953 Roo.bootstrap.MasonryBrick = function(config){
34954     
34955     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34956     
34957     Roo.bootstrap.MasonryBrick.register(this);
34958     
34959     this.addEvents({
34960         // raw events
34961         /**
34962          * @event click
34963          * When a MasonryBrick is clcik
34964          * @param {Roo.bootstrap.MasonryBrick} this
34965          * @param {Roo.EventObject} e
34966          */
34967         "click" : true
34968     });
34969 };
34970
34971 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34972     
34973     /**
34974      * @cfg {String} title
34975      */   
34976     title : '',
34977     /**
34978      * @cfg {String} html
34979      */   
34980     html : '',
34981     /**
34982      * @cfg {String} bgimage
34983      */   
34984     bgimage : '',
34985     /**
34986      * @cfg {String} videourl
34987      */   
34988     videourl : '',
34989     /**
34990      * @cfg {String} cls
34991      */   
34992     cls : '',
34993     /**
34994      * @cfg {String} href
34995      */   
34996     href : '',
34997     /**
34998      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34999      */   
35000     size : 'xs',
35001     
35002     /**
35003      * @cfg {String} placetitle (center|bottom)
35004      */   
35005     placetitle : '',
35006     
35007     /**
35008      * @cfg {Boolean} isFitContainer defalut true
35009      */   
35010     isFitContainer : true, 
35011     
35012     /**
35013      * @cfg {Boolean} preventDefault defalut false
35014      */   
35015     preventDefault : false, 
35016     
35017     /**
35018      * @cfg {Boolean} inverse defalut false
35019      */   
35020     maskInverse : false, 
35021     
35022     getAutoCreate : function()
35023     {
35024         if(!this.isFitContainer){
35025             return this.getSplitAutoCreate();
35026         }
35027         
35028         var cls = 'masonry-brick masonry-brick-full';
35029         
35030         if(this.href.length){
35031             cls += ' masonry-brick-link';
35032         }
35033         
35034         if(this.bgimage.length){
35035             cls += ' masonry-brick-image';
35036         }
35037         
35038         if(this.maskInverse){
35039             cls += ' mask-inverse';
35040         }
35041         
35042         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35043             cls += ' enable-mask';
35044         }
35045         
35046         if(this.size){
35047             cls += ' masonry-' + this.size + '-brick';
35048         }
35049         
35050         if(this.placetitle.length){
35051             
35052             switch (this.placetitle) {
35053                 case 'center' :
35054                     cls += ' masonry-center-title';
35055                     break;
35056                 case 'bottom' :
35057                     cls += ' masonry-bottom-title';
35058                     break;
35059                 default:
35060                     break;
35061             }
35062             
35063         } else {
35064             if(!this.html.length && !this.bgimage.length){
35065                 cls += ' masonry-center-title';
35066             }
35067
35068             if(!this.html.length && this.bgimage.length){
35069                 cls += ' masonry-bottom-title';
35070             }
35071         }
35072         
35073         if(this.cls){
35074             cls += ' ' + this.cls;
35075         }
35076         
35077         var cfg = {
35078             tag: (this.href.length) ? 'a' : 'div',
35079             cls: cls,
35080             cn: [
35081                 {
35082                     tag: 'div',
35083                     cls: 'masonry-brick-mask'
35084                 },
35085                 {
35086                     tag: 'div',
35087                     cls: 'masonry-brick-paragraph',
35088                     cn: []
35089                 }
35090             ]
35091         };
35092         
35093         if(this.href.length){
35094             cfg.href = this.href;
35095         }
35096         
35097         var cn = cfg.cn[1].cn;
35098         
35099         if(this.title.length){
35100             cn.push({
35101                 tag: 'h4',
35102                 cls: 'masonry-brick-title',
35103                 html: this.title
35104             });
35105         }
35106         
35107         if(this.html.length){
35108             cn.push({
35109                 tag: 'p',
35110                 cls: 'masonry-brick-text',
35111                 html: this.html
35112             });
35113         }
35114         
35115         if (!this.title.length && !this.html.length) {
35116             cfg.cn[1].cls += ' hide';
35117         }
35118         
35119         if(this.bgimage.length){
35120             cfg.cn.push({
35121                 tag: 'img',
35122                 cls: 'masonry-brick-image-view',
35123                 src: this.bgimage
35124             });
35125         }
35126         
35127         if(this.videourl.length){
35128             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35129             // youtube support only?
35130             cfg.cn.push({
35131                 tag: 'iframe',
35132                 cls: 'masonry-brick-image-view',
35133                 src: vurl,
35134                 frameborder : 0,
35135                 allowfullscreen : true
35136             });
35137         }
35138         
35139         return cfg;
35140         
35141     },
35142     
35143     getSplitAutoCreate : function()
35144     {
35145         var cls = 'masonry-brick masonry-brick-split';
35146         
35147         if(this.href.length){
35148             cls += ' masonry-brick-link';
35149         }
35150         
35151         if(this.bgimage.length){
35152             cls += ' masonry-brick-image';
35153         }
35154         
35155         if(this.size){
35156             cls += ' masonry-' + this.size + '-brick';
35157         }
35158         
35159         switch (this.placetitle) {
35160             case 'center' :
35161                 cls += ' masonry-center-title';
35162                 break;
35163             case 'bottom' :
35164                 cls += ' masonry-bottom-title';
35165                 break;
35166             default:
35167                 if(!this.bgimage.length){
35168                     cls += ' masonry-center-title';
35169                 }
35170
35171                 if(this.bgimage.length){
35172                     cls += ' masonry-bottom-title';
35173                 }
35174                 break;
35175         }
35176         
35177         if(this.cls){
35178             cls += ' ' + this.cls;
35179         }
35180         
35181         var cfg = {
35182             tag: (this.href.length) ? 'a' : 'div',
35183             cls: cls,
35184             cn: [
35185                 {
35186                     tag: 'div',
35187                     cls: 'masonry-brick-split-head',
35188                     cn: [
35189                         {
35190                             tag: 'div',
35191                             cls: 'masonry-brick-paragraph',
35192                             cn: []
35193                         }
35194                     ]
35195                 },
35196                 {
35197                     tag: 'div',
35198                     cls: 'masonry-brick-split-body',
35199                     cn: []
35200                 }
35201             ]
35202         };
35203         
35204         if(this.href.length){
35205             cfg.href = this.href;
35206         }
35207         
35208         if(this.title.length){
35209             cfg.cn[0].cn[0].cn.push({
35210                 tag: 'h4',
35211                 cls: 'masonry-brick-title',
35212                 html: this.title
35213             });
35214         }
35215         
35216         if(this.html.length){
35217             cfg.cn[1].cn.push({
35218                 tag: 'p',
35219                 cls: 'masonry-brick-text',
35220                 html: this.html
35221             });
35222         }
35223
35224         if(this.bgimage.length){
35225             cfg.cn[0].cn.push({
35226                 tag: 'img',
35227                 cls: 'masonry-brick-image-view',
35228                 src: this.bgimage
35229             });
35230         }
35231         
35232         if(this.videourl.length){
35233             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35234             // youtube support only?
35235             cfg.cn[0].cn.cn.push({
35236                 tag: 'iframe',
35237                 cls: 'masonry-brick-image-view',
35238                 src: vurl,
35239                 frameborder : 0,
35240                 allowfullscreen : true
35241             });
35242         }
35243         
35244         return cfg;
35245     },
35246     
35247     initEvents: function() 
35248     {
35249         switch (this.size) {
35250             case 'xs' :
35251                 this.x = 1;
35252                 this.y = 1;
35253                 break;
35254             case 'sm' :
35255                 this.x = 2;
35256                 this.y = 2;
35257                 break;
35258             case 'md' :
35259             case 'md-left' :
35260             case 'md-right' :
35261                 this.x = 3;
35262                 this.y = 3;
35263                 break;
35264             case 'tall' :
35265                 this.x = 2;
35266                 this.y = 3;
35267                 break;
35268             case 'wide' :
35269                 this.x = 3;
35270                 this.y = 2;
35271                 break;
35272             case 'wide-thin' :
35273                 this.x = 3;
35274                 this.y = 1;
35275                 break;
35276                         
35277             default :
35278                 break;
35279         }
35280         
35281         if(Roo.isTouch){
35282             this.el.on('touchstart', this.onTouchStart, this);
35283             this.el.on('touchmove', this.onTouchMove, this);
35284             this.el.on('touchend', this.onTouchEnd, this);
35285             this.el.on('contextmenu', this.onContextMenu, this);
35286         } else {
35287             this.el.on('mouseenter'  ,this.enter, this);
35288             this.el.on('mouseleave', this.leave, this);
35289             this.el.on('click', this.onClick, this);
35290         }
35291         
35292         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35293             this.parent().bricks.push(this);   
35294         }
35295         
35296     },
35297     
35298     onClick: function(e, el)
35299     {
35300         var time = this.endTimer - this.startTimer;
35301         // Roo.log(e.preventDefault());
35302         if(Roo.isTouch){
35303             if(time > 1000){
35304                 e.preventDefault();
35305                 return;
35306             }
35307         }
35308         
35309         if(!this.preventDefault){
35310             return;
35311         }
35312         
35313         e.preventDefault();
35314         
35315         if (this.activeClass != '') {
35316             this.selectBrick();
35317         }
35318         
35319         this.fireEvent('click', this, e);
35320     },
35321     
35322     enter: function(e, el)
35323     {
35324         e.preventDefault();
35325         
35326         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35327             return;
35328         }
35329         
35330         if(this.bgimage.length && this.html.length){
35331             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35332         }
35333     },
35334     
35335     leave: function(e, el)
35336     {
35337         e.preventDefault();
35338         
35339         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35340             return;
35341         }
35342         
35343         if(this.bgimage.length && this.html.length){
35344             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35345         }
35346     },
35347     
35348     onTouchStart: function(e, el)
35349     {
35350 //        e.preventDefault();
35351         
35352         this.touchmoved = false;
35353         
35354         if(!this.isFitContainer){
35355             return;
35356         }
35357         
35358         if(!this.bgimage.length || !this.html.length){
35359             return;
35360         }
35361         
35362         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35363         
35364         this.timer = new Date().getTime();
35365         
35366     },
35367     
35368     onTouchMove: function(e, el)
35369     {
35370         this.touchmoved = true;
35371     },
35372     
35373     onContextMenu : function(e,el)
35374     {
35375         e.preventDefault();
35376         e.stopPropagation();
35377         return false;
35378     },
35379     
35380     onTouchEnd: function(e, el)
35381     {
35382 //        e.preventDefault();
35383         
35384         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35385         
35386             this.leave(e,el);
35387             
35388             return;
35389         }
35390         
35391         if(!this.bgimage.length || !this.html.length){
35392             
35393             if(this.href.length){
35394                 window.location.href = this.href;
35395             }
35396             
35397             return;
35398         }
35399         
35400         if(!this.isFitContainer){
35401             return;
35402         }
35403         
35404         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35405         
35406         window.location.href = this.href;
35407     },
35408     
35409     //selection on single brick only
35410     selectBrick : function() {
35411         
35412         if (!this.parentId) {
35413             return;
35414         }
35415         
35416         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35417         var index = m.selectedBrick.indexOf(this.id);
35418         
35419         if ( index > -1) {
35420             m.selectedBrick.splice(index,1);
35421             this.el.removeClass(this.activeClass);
35422             return;
35423         }
35424         
35425         for(var i = 0; i < m.selectedBrick.length; i++) {
35426             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35427             b.el.removeClass(b.activeClass);
35428         }
35429         
35430         m.selectedBrick = [];
35431         
35432         m.selectedBrick.push(this.id);
35433         this.el.addClass(this.activeClass);
35434         return;
35435     },
35436     
35437     isSelected : function(){
35438         return this.el.hasClass(this.activeClass);
35439         
35440     }
35441 });
35442
35443 Roo.apply(Roo.bootstrap.MasonryBrick, {
35444     
35445     //groups: {},
35446     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35447      /**
35448     * register a Masonry Brick
35449     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35450     */
35451     
35452     register : function(brick)
35453     {
35454         //this.groups[brick.id] = brick;
35455         this.groups.add(brick.id, brick);
35456     },
35457     /**
35458     * fetch a  masonry brick based on the masonry brick ID
35459     * @param {string} the masonry brick to add
35460     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35461     */
35462     
35463     get: function(brick_id) 
35464     {
35465         // if (typeof(this.groups[brick_id]) == 'undefined') {
35466         //     return false;
35467         // }
35468         // return this.groups[brick_id] ;
35469         
35470         if(this.groups.key(brick_id)) {
35471             return this.groups.key(brick_id);
35472         }
35473         
35474         return false;
35475     }
35476     
35477     
35478     
35479 });
35480
35481  /*
35482  * - LGPL
35483  *
35484  * element
35485  * 
35486  */
35487
35488 /**
35489  * @class Roo.bootstrap.Brick
35490  * @extends Roo.bootstrap.Component
35491  * Bootstrap Brick class
35492  * 
35493  * @constructor
35494  * Create a new Brick
35495  * @param {Object} config The config object
35496  */
35497
35498 Roo.bootstrap.Brick = function(config){
35499     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35500     
35501     this.addEvents({
35502         // raw events
35503         /**
35504          * @event click
35505          * When a Brick is click
35506          * @param {Roo.bootstrap.Brick} this
35507          * @param {Roo.EventObject} e
35508          */
35509         "click" : true
35510     });
35511 };
35512
35513 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35514     
35515     /**
35516      * @cfg {String} title
35517      */   
35518     title : '',
35519     /**
35520      * @cfg {String} html
35521      */   
35522     html : '',
35523     /**
35524      * @cfg {String} bgimage
35525      */   
35526     bgimage : '',
35527     /**
35528      * @cfg {String} cls
35529      */   
35530     cls : '',
35531     /**
35532      * @cfg {String} href
35533      */   
35534     href : '',
35535     /**
35536      * @cfg {String} video
35537      */   
35538     video : '',
35539     /**
35540      * @cfg {Boolean} square
35541      */   
35542     square : true,
35543     
35544     getAutoCreate : function()
35545     {
35546         var cls = 'roo-brick';
35547         
35548         if(this.href.length){
35549             cls += ' roo-brick-link';
35550         }
35551         
35552         if(this.bgimage.length){
35553             cls += ' roo-brick-image';
35554         }
35555         
35556         if(!this.html.length && !this.bgimage.length){
35557             cls += ' roo-brick-center-title';
35558         }
35559         
35560         if(!this.html.length && this.bgimage.length){
35561             cls += ' roo-brick-bottom-title';
35562         }
35563         
35564         if(this.cls){
35565             cls += ' ' + this.cls;
35566         }
35567         
35568         var cfg = {
35569             tag: (this.href.length) ? 'a' : 'div',
35570             cls: cls,
35571             cn: [
35572                 {
35573                     tag: 'div',
35574                     cls: 'roo-brick-paragraph',
35575                     cn: []
35576                 }
35577             ]
35578         };
35579         
35580         if(this.href.length){
35581             cfg.href = this.href;
35582         }
35583         
35584         var cn = cfg.cn[0].cn;
35585         
35586         if(this.title.length){
35587             cn.push({
35588                 tag: 'h4',
35589                 cls: 'roo-brick-title',
35590                 html: this.title
35591             });
35592         }
35593         
35594         if(this.html.length){
35595             cn.push({
35596                 tag: 'p',
35597                 cls: 'roo-brick-text',
35598                 html: this.html
35599             });
35600         } else {
35601             cn.cls += ' hide';
35602         }
35603         
35604         if(this.bgimage.length){
35605             cfg.cn.push({
35606                 tag: 'img',
35607                 cls: 'roo-brick-image-view',
35608                 src: this.bgimage
35609             });
35610         }
35611         
35612         return cfg;
35613     },
35614     
35615     initEvents: function() 
35616     {
35617         if(this.title.length || this.html.length){
35618             this.el.on('mouseenter'  ,this.enter, this);
35619             this.el.on('mouseleave', this.leave, this);
35620         }
35621         
35622         Roo.EventManager.onWindowResize(this.resize, this); 
35623         
35624         if(this.bgimage.length){
35625             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35626             this.imageEl.on('load', this.onImageLoad, this);
35627             return;
35628         }
35629         
35630         this.resize();
35631     },
35632     
35633     onImageLoad : function()
35634     {
35635         this.resize();
35636     },
35637     
35638     resize : function()
35639     {
35640         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35641         
35642         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35643         
35644         if(this.bgimage.length){
35645             var image = this.el.select('.roo-brick-image-view', true).first();
35646             
35647             image.setWidth(paragraph.getWidth());
35648             
35649             if(this.square){
35650                 image.setHeight(paragraph.getWidth());
35651             }
35652             
35653             this.el.setHeight(image.getHeight());
35654             paragraph.setHeight(image.getHeight());
35655             
35656         }
35657         
35658     },
35659     
35660     enter: function(e, el)
35661     {
35662         e.preventDefault();
35663         
35664         if(this.bgimage.length){
35665             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35666             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35667         }
35668     },
35669     
35670     leave: function(e, el)
35671     {
35672         e.preventDefault();
35673         
35674         if(this.bgimage.length){
35675             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35676             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35677         }
35678     }
35679     
35680 });
35681
35682  
35683
35684  /*
35685  * - LGPL
35686  *
35687  * Number field 
35688  */
35689
35690 /**
35691  * @class Roo.bootstrap.NumberField
35692  * @extends Roo.bootstrap.Input
35693  * Bootstrap NumberField class
35694  * 
35695  * 
35696  * 
35697  * 
35698  * @constructor
35699  * Create a new NumberField
35700  * @param {Object} config The config object
35701  */
35702
35703 Roo.bootstrap.NumberField = function(config){
35704     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35705 };
35706
35707 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35708     
35709     /**
35710      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35711      */
35712     allowDecimals : true,
35713     /**
35714      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35715      */
35716     decimalSeparator : ".",
35717     /**
35718      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35719      */
35720     decimalPrecision : 2,
35721     /**
35722      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35723      */
35724     allowNegative : true,
35725     
35726     /**
35727      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35728      */
35729     allowZero: true,
35730     /**
35731      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35732      */
35733     minValue : Number.NEGATIVE_INFINITY,
35734     /**
35735      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35736      */
35737     maxValue : Number.MAX_VALUE,
35738     /**
35739      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35740      */
35741     minText : "The minimum value for this field is {0}",
35742     /**
35743      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35744      */
35745     maxText : "The maximum value for this field is {0}",
35746     /**
35747      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35748      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35749      */
35750     nanText : "{0} is not a valid number",
35751     /**
35752      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35753      */
35754     thousandsDelimiter : false,
35755     /**
35756      * @cfg {String} valueAlign alignment of value
35757      */
35758     valueAlign : "left",
35759
35760     getAutoCreate : function()
35761     {
35762         var hiddenInput = {
35763             tag: 'input',
35764             type: 'hidden',
35765             id: Roo.id(),
35766             cls: 'hidden-number-input'
35767         };
35768         
35769         if (this.name) {
35770             hiddenInput.name = this.name;
35771         }
35772         
35773         this.name = '';
35774         
35775         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35776         
35777         this.name = hiddenInput.name;
35778         
35779         if(cfg.cn.length > 0) {
35780             cfg.cn.push(hiddenInput);
35781         }
35782         
35783         return cfg;
35784     },
35785
35786     // private
35787     initEvents : function()
35788     {   
35789         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35790         
35791         var allowed = "0123456789";
35792         
35793         if(this.allowDecimals){
35794             allowed += this.decimalSeparator;
35795         }
35796         
35797         if(this.allowNegative){
35798             allowed += "-";
35799         }
35800         
35801         if(this.thousandsDelimiter) {
35802             allowed += ",";
35803         }
35804         
35805         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35806         
35807         var keyPress = function(e){
35808             
35809             var k = e.getKey();
35810             
35811             var c = e.getCharCode();
35812             
35813             if(
35814                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35815                     allowed.indexOf(String.fromCharCode(c)) === -1
35816             ){
35817                 e.stopEvent();
35818                 return;
35819             }
35820             
35821             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35822                 return;
35823             }
35824             
35825             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35826                 e.stopEvent();
35827             }
35828         };
35829         
35830         this.el.on("keypress", keyPress, this);
35831     },
35832     
35833     validateValue : function(value)
35834     {
35835         
35836         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35837             return false;
35838         }
35839         
35840         var num = this.parseValue(value);
35841         
35842         if(isNaN(num)){
35843             this.markInvalid(String.format(this.nanText, value));
35844             return false;
35845         }
35846         
35847         if(num < this.minValue){
35848             this.markInvalid(String.format(this.minText, this.minValue));
35849             return false;
35850         }
35851         
35852         if(num > this.maxValue){
35853             this.markInvalid(String.format(this.maxText, this.maxValue));
35854             return false;
35855         }
35856         
35857         return true;
35858     },
35859
35860     getValue : function()
35861     {
35862         var v = this.hiddenEl().getValue();
35863         
35864         return this.fixPrecision(this.parseValue(v));
35865     },
35866
35867     parseValue : function(value)
35868     {
35869         if(this.thousandsDelimiter) {
35870             value += "";
35871             r = new RegExp(",", "g");
35872             value = value.replace(r, "");
35873         }
35874         
35875         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35876         return isNaN(value) ? '' : value;
35877     },
35878
35879     fixPrecision : function(value)
35880     {
35881         if(this.thousandsDelimiter) {
35882             value += "";
35883             r = new RegExp(",", "g");
35884             value = value.replace(r, "");
35885         }
35886         
35887         var nan = isNaN(value);
35888         
35889         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35890             return nan ? '' : value;
35891         }
35892         return parseFloat(value).toFixed(this.decimalPrecision);
35893     },
35894
35895     setValue : function(v)
35896     {
35897         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35898         
35899         this.value = v;
35900         
35901         if(this.rendered){
35902             
35903             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35904             
35905             this.inputEl().dom.value = (v == '') ? '' :
35906                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35907             
35908             if(!this.allowZero && v === '0') {
35909                 this.hiddenEl().dom.value = '';
35910                 this.inputEl().dom.value = '';
35911             }
35912             
35913             this.validate();
35914         }
35915     },
35916
35917     decimalPrecisionFcn : function(v)
35918     {
35919         return Math.floor(v);
35920     },
35921
35922     beforeBlur : function()
35923     {
35924         var v = this.parseValue(this.getRawValue());
35925         
35926         if(v || v === 0 || v === ''){
35927             this.setValue(v);
35928         }
35929     },
35930     
35931     hiddenEl : function()
35932     {
35933         return this.el.select('input.hidden-number-input',true).first();
35934     }
35935     
35936 });
35937
35938  
35939
35940 /*
35941 * Licence: LGPL
35942 */
35943
35944 /**
35945  * @class Roo.bootstrap.DocumentSlider
35946  * @extends Roo.bootstrap.Component
35947  * Bootstrap DocumentSlider class
35948  * 
35949  * @constructor
35950  * Create a new DocumentViewer
35951  * @param {Object} config The config object
35952  */
35953
35954 Roo.bootstrap.DocumentSlider = function(config){
35955     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35956     
35957     this.files = [];
35958     
35959     this.addEvents({
35960         /**
35961          * @event initial
35962          * Fire after initEvent
35963          * @param {Roo.bootstrap.DocumentSlider} this
35964          */
35965         "initial" : true,
35966         /**
35967          * @event update
35968          * Fire after update
35969          * @param {Roo.bootstrap.DocumentSlider} this
35970          */
35971         "update" : true,
35972         /**
35973          * @event click
35974          * Fire after click
35975          * @param {Roo.bootstrap.DocumentSlider} this
35976          */
35977         "click" : true
35978     });
35979 };
35980
35981 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35982     
35983     files : false,
35984     
35985     indicator : 0,
35986     
35987     getAutoCreate : function()
35988     {
35989         var cfg = {
35990             tag : 'div',
35991             cls : 'roo-document-slider',
35992             cn : [
35993                 {
35994                     tag : 'div',
35995                     cls : 'roo-document-slider-header',
35996                     cn : [
35997                         {
35998                             tag : 'div',
35999                             cls : 'roo-document-slider-header-title'
36000                         }
36001                     ]
36002                 },
36003                 {
36004                     tag : 'div',
36005                     cls : 'roo-document-slider-body',
36006                     cn : [
36007                         {
36008                             tag : 'div',
36009                             cls : 'roo-document-slider-prev',
36010                             cn : [
36011                                 {
36012                                     tag : 'i',
36013                                     cls : 'fa fa-chevron-left'
36014                                 }
36015                             ]
36016                         },
36017                         {
36018                             tag : 'div',
36019                             cls : 'roo-document-slider-thumb',
36020                             cn : [
36021                                 {
36022                                     tag : 'img',
36023                                     cls : 'roo-document-slider-image'
36024                                 }
36025                             ]
36026                         },
36027                         {
36028                             tag : 'div',
36029                             cls : 'roo-document-slider-next',
36030                             cn : [
36031                                 {
36032                                     tag : 'i',
36033                                     cls : 'fa fa-chevron-right'
36034                                 }
36035                             ]
36036                         }
36037                     ]
36038                 }
36039             ]
36040         };
36041         
36042         return cfg;
36043     },
36044     
36045     initEvents : function()
36046     {
36047         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36048         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36049         
36050         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36051         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36052         
36053         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36054         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36055         
36056         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36057         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36058         
36059         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36060         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36061         
36062         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36063         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36064         
36065         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36066         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36067         
36068         this.thumbEl.on('click', this.onClick, this);
36069         
36070         this.prevIndicator.on('click', this.prev, this);
36071         
36072         this.nextIndicator.on('click', this.next, this);
36073         
36074     },
36075     
36076     initial : function()
36077     {
36078         if(this.files.length){
36079             this.indicator = 1;
36080             this.update()
36081         }
36082         
36083         this.fireEvent('initial', this);
36084     },
36085     
36086     update : function()
36087     {
36088         this.imageEl.attr('src', this.files[this.indicator - 1]);
36089         
36090         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36091         
36092         this.prevIndicator.show();
36093         
36094         if(this.indicator == 1){
36095             this.prevIndicator.hide();
36096         }
36097         
36098         this.nextIndicator.show();
36099         
36100         if(this.indicator == this.files.length){
36101             this.nextIndicator.hide();
36102         }
36103         
36104         this.thumbEl.scrollTo('top');
36105         
36106         this.fireEvent('update', this);
36107     },
36108     
36109     onClick : function(e)
36110     {
36111         e.preventDefault();
36112         
36113         this.fireEvent('click', this);
36114     },
36115     
36116     prev : function(e)
36117     {
36118         e.preventDefault();
36119         
36120         this.indicator = Math.max(1, this.indicator - 1);
36121         
36122         this.update();
36123     },
36124     
36125     next : function(e)
36126     {
36127         e.preventDefault();
36128         
36129         this.indicator = Math.min(this.files.length, this.indicator + 1);
36130         
36131         this.update();
36132     }
36133 });
36134 /*
36135  * - LGPL
36136  *
36137  * RadioSet
36138  *
36139  *
36140  */
36141
36142 /**
36143  * @class Roo.bootstrap.RadioSet
36144  * @extends Roo.bootstrap.Input
36145  * Bootstrap RadioSet class
36146  * @cfg {String} indicatorpos (left|right) default left
36147  * @cfg {Boolean} inline (true|false) inline the element (default true)
36148  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36149  * @constructor
36150  * Create a new RadioSet
36151  * @param {Object} config The config object
36152  */
36153
36154 Roo.bootstrap.RadioSet = function(config){
36155     
36156     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36157     
36158     this.radioes = [];
36159     
36160     Roo.bootstrap.RadioSet.register(this);
36161     
36162     this.addEvents({
36163         /**
36164         * @event check
36165         * Fires when the element is checked or unchecked.
36166         * @param {Roo.bootstrap.RadioSet} this This radio
36167         * @param {Roo.bootstrap.Radio} item The checked item
36168         */
36169        check : true,
36170        /**
36171         * @event click
36172         * Fires when the element is click.
36173         * @param {Roo.bootstrap.RadioSet} this This radio set
36174         * @param {Roo.bootstrap.Radio} item The checked item
36175         * @param {Roo.EventObject} e The event object
36176         */
36177        click : true
36178     });
36179     
36180 };
36181
36182 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36183
36184     radioes : false,
36185     
36186     inline : true,
36187     
36188     weight : '',
36189     
36190     indicatorpos : 'left',
36191     
36192     getAutoCreate : function()
36193     {
36194         var label = {
36195             tag : 'label',
36196             cls : 'roo-radio-set-label',
36197             cn : [
36198                 {
36199                     tag : 'span',
36200                     html : this.fieldLabel
36201                 }
36202             ]
36203         };
36204         if (Roo.bootstrap.version == 3) {
36205             
36206             
36207             if(this.indicatorpos == 'left'){
36208                 label.cn.unshift({
36209                     tag : 'i',
36210                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36211                     tooltip : 'This field is required'
36212                 });
36213             } else {
36214                 label.cn.push({
36215                     tag : 'i',
36216                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36217                     tooltip : 'This field is required'
36218                 });
36219             }
36220         }
36221         var items = {
36222             tag : 'div',
36223             cls : 'roo-radio-set-items'
36224         };
36225         
36226         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36227         
36228         if (align === 'left' && this.fieldLabel.length) {
36229             
36230             items = {
36231                 cls : "roo-radio-set-right", 
36232                 cn: [
36233                     items
36234                 ]
36235             };
36236             
36237             if(this.labelWidth > 12){
36238                 label.style = "width: " + this.labelWidth + 'px';
36239             }
36240             
36241             if(this.labelWidth < 13 && this.labelmd == 0){
36242                 this.labelmd = this.labelWidth;
36243             }
36244             
36245             if(this.labellg > 0){
36246                 label.cls += ' col-lg-' + this.labellg;
36247                 items.cls += ' col-lg-' + (12 - this.labellg);
36248             }
36249             
36250             if(this.labelmd > 0){
36251                 label.cls += ' col-md-' + this.labelmd;
36252                 items.cls += ' col-md-' + (12 - this.labelmd);
36253             }
36254             
36255             if(this.labelsm > 0){
36256                 label.cls += ' col-sm-' + this.labelsm;
36257                 items.cls += ' col-sm-' + (12 - this.labelsm);
36258             }
36259             
36260             if(this.labelxs > 0){
36261                 label.cls += ' col-xs-' + this.labelxs;
36262                 items.cls += ' col-xs-' + (12 - this.labelxs);
36263             }
36264         }
36265         
36266         var cfg = {
36267             tag : 'div',
36268             cls : 'roo-radio-set',
36269             cn : [
36270                 {
36271                     tag : 'input',
36272                     cls : 'roo-radio-set-input',
36273                     type : 'hidden',
36274                     name : this.name,
36275                     value : this.value ? this.value :  ''
36276                 },
36277                 label,
36278                 items
36279             ]
36280         };
36281         
36282         if(this.weight.length){
36283             cfg.cls += ' roo-radio-' + this.weight;
36284         }
36285         
36286         if(this.inline) {
36287             cfg.cls += ' roo-radio-set-inline';
36288         }
36289         
36290         var settings=this;
36291         ['xs','sm','md','lg'].map(function(size){
36292             if (settings[size]) {
36293                 cfg.cls += ' col-' + size + '-' + settings[size];
36294             }
36295         });
36296         
36297         return cfg;
36298         
36299     },
36300
36301     initEvents : function()
36302     {
36303         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36304         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36305         
36306         if(!this.fieldLabel.length){
36307             this.labelEl.hide();
36308         }
36309         
36310         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36311         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36312         
36313         this.indicator = this.indicatorEl();
36314         
36315         if(this.indicator){
36316             this.indicator.addClass('invisible');
36317         }
36318         
36319         this.originalValue = this.getValue();
36320         
36321     },
36322     
36323     inputEl: function ()
36324     {
36325         return this.el.select('.roo-radio-set-input', true).first();
36326     },
36327     
36328     getChildContainer : function()
36329     {
36330         return this.itemsEl;
36331     },
36332     
36333     register : function(item)
36334     {
36335         this.radioes.push(item);
36336         
36337     },
36338     
36339     validate : function()
36340     {   
36341         if(this.getVisibilityEl().hasClass('hidden')){
36342             return true;
36343         }
36344         
36345         var valid = false;
36346         
36347         Roo.each(this.radioes, function(i){
36348             if(!i.checked){
36349                 return;
36350             }
36351             
36352             valid = true;
36353             return false;
36354         });
36355         
36356         if(this.allowBlank) {
36357             return true;
36358         }
36359         
36360         if(this.disabled || valid){
36361             this.markValid();
36362             return true;
36363         }
36364         
36365         this.markInvalid();
36366         return false;
36367         
36368     },
36369     
36370     markValid : function()
36371     {
36372         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36373             this.indicatorEl().removeClass('visible');
36374             this.indicatorEl().addClass('invisible');
36375         }
36376         
36377         
36378         if (Roo.bootstrap.version == 3) {
36379             this.el.removeClass([this.invalidClass, this.validClass]);
36380             this.el.addClass(this.validClass);
36381         } else {
36382             this.el.removeClass(['is-invalid','is-valid']);
36383             this.el.addClass(['is-valid']);
36384         }
36385         this.fireEvent('valid', this);
36386     },
36387     
36388     markInvalid : function(msg)
36389     {
36390         if(this.allowBlank || this.disabled){
36391             return;
36392         }
36393         
36394         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36395             this.indicatorEl().removeClass('invisible');
36396             this.indicatorEl().addClass('visible');
36397         }
36398         if (Roo.bootstrap.version == 3) {
36399             this.el.removeClass([this.invalidClass, this.validClass]);
36400             this.el.addClass(this.invalidClass);
36401         } else {
36402             this.el.removeClass(['is-invalid','is-valid']);
36403             this.el.addClass(['is-invalid']);
36404         }
36405         
36406         this.fireEvent('invalid', this, msg);
36407         
36408     },
36409     
36410     setValue : function(v, suppressEvent)
36411     {   
36412         if(this.value === v){
36413             return;
36414         }
36415         
36416         this.value = v;
36417         
36418         if(this.rendered){
36419             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36420         }
36421         
36422         Roo.each(this.radioes, function(i){
36423             i.checked = false;
36424             i.el.removeClass('checked');
36425         });
36426         
36427         Roo.each(this.radioes, function(i){
36428             
36429             if(i.value === v || i.value.toString() === v.toString()){
36430                 i.checked = true;
36431                 i.el.addClass('checked');
36432                 
36433                 if(suppressEvent !== true){
36434                     this.fireEvent('check', this, i);
36435                 }
36436                 
36437                 return false;
36438             }
36439             
36440         }, this);
36441         
36442         this.validate();
36443     },
36444     
36445     clearInvalid : function(){
36446         
36447         if(!this.el || this.preventMark){
36448             return;
36449         }
36450         
36451         this.el.removeClass([this.invalidClass]);
36452         
36453         this.fireEvent('valid', this);
36454     }
36455     
36456 });
36457
36458 Roo.apply(Roo.bootstrap.RadioSet, {
36459     
36460     groups: {},
36461     
36462     register : function(set)
36463     {
36464         this.groups[set.name] = set;
36465     },
36466     
36467     get: function(name) 
36468     {
36469         if (typeof(this.groups[name]) == 'undefined') {
36470             return false;
36471         }
36472         
36473         return this.groups[name] ;
36474     }
36475     
36476 });
36477 /*
36478  * Based on:
36479  * Ext JS Library 1.1.1
36480  * Copyright(c) 2006-2007, Ext JS, LLC.
36481  *
36482  * Originally Released Under LGPL - original licence link has changed is not relivant.
36483  *
36484  * Fork - LGPL
36485  * <script type="text/javascript">
36486  */
36487
36488
36489 /**
36490  * @class Roo.bootstrap.SplitBar
36491  * @extends Roo.util.Observable
36492  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36493  * <br><br>
36494  * Usage:
36495  * <pre><code>
36496 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36497                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36498 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36499 split.minSize = 100;
36500 split.maxSize = 600;
36501 split.animate = true;
36502 split.on('moved', splitterMoved);
36503 </code></pre>
36504  * @constructor
36505  * Create a new SplitBar
36506  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36507  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36508  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36509  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36510                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36511                         position of the SplitBar).
36512  */
36513 Roo.bootstrap.SplitBar = function(cfg){
36514     
36515     /** @private */
36516     
36517     //{
36518     //  dragElement : elm
36519     //  resizingElement: el,
36520         // optional..
36521     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36522     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36523         // existingProxy ???
36524     //}
36525     
36526     this.el = Roo.get(cfg.dragElement, true);
36527     this.el.dom.unselectable = "on";
36528     /** @private */
36529     this.resizingEl = Roo.get(cfg.resizingElement, true);
36530
36531     /**
36532      * @private
36533      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36534      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36535      * @type Number
36536      */
36537     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36538     
36539     /**
36540      * The minimum size of the resizing element. (Defaults to 0)
36541      * @type Number
36542      */
36543     this.minSize = 0;
36544     
36545     /**
36546      * The maximum size of the resizing element. (Defaults to 2000)
36547      * @type Number
36548      */
36549     this.maxSize = 2000;
36550     
36551     /**
36552      * Whether to animate the transition to the new size
36553      * @type Boolean
36554      */
36555     this.animate = false;
36556     
36557     /**
36558      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36559      * @type Boolean
36560      */
36561     this.useShim = false;
36562     
36563     /** @private */
36564     this.shim = null;
36565     
36566     if(!cfg.existingProxy){
36567         /** @private */
36568         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36569     }else{
36570         this.proxy = Roo.get(cfg.existingProxy).dom;
36571     }
36572     /** @private */
36573     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36574     
36575     /** @private */
36576     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36577     
36578     /** @private */
36579     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36580     
36581     /** @private */
36582     this.dragSpecs = {};
36583     
36584     /**
36585      * @private The adapter to use to positon and resize elements
36586      */
36587     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36588     this.adapter.init(this);
36589     
36590     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36591         /** @private */
36592         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36593         this.el.addClass("roo-splitbar-h");
36594     }else{
36595         /** @private */
36596         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36597         this.el.addClass("roo-splitbar-v");
36598     }
36599     
36600     this.addEvents({
36601         /**
36602          * @event resize
36603          * Fires when the splitter is moved (alias for {@link #event-moved})
36604          * @param {Roo.bootstrap.SplitBar} this
36605          * @param {Number} newSize the new width or height
36606          */
36607         "resize" : true,
36608         /**
36609          * @event moved
36610          * Fires when the splitter is moved
36611          * @param {Roo.bootstrap.SplitBar} this
36612          * @param {Number} newSize the new width or height
36613          */
36614         "moved" : true,
36615         /**
36616          * @event beforeresize
36617          * Fires before the splitter is dragged
36618          * @param {Roo.bootstrap.SplitBar} this
36619          */
36620         "beforeresize" : true,
36621
36622         "beforeapply" : true
36623     });
36624
36625     Roo.util.Observable.call(this);
36626 };
36627
36628 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36629     onStartProxyDrag : function(x, y){
36630         this.fireEvent("beforeresize", this);
36631         if(!this.overlay){
36632             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36633             o.unselectable();
36634             o.enableDisplayMode("block");
36635             // all splitbars share the same overlay
36636             Roo.bootstrap.SplitBar.prototype.overlay = o;
36637         }
36638         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36639         this.overlay.show();
36640         Roo.get(this.proxy).setDisplayed("block");
36641         var size = this.adapter.getElementSize(this);
36642         this.activeMinSize = this.getMinimumSize();;
36643         this.activeMaxSize = this.getMaximumSize();;
36644         var c1 = size - this.activeMinSize;
36645         var c2 = Math.max(this.activeMaxSize - size, 0);
36646         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36647             this.dd.resetConstraints();
36648             this.dd.setXConstraint(
36649                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36650                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36651             );
36652             this.dd.setYConstraint(0, 0);
36653         }else{
36654             this.dd.resetConstraints();
36655             this.dd.setXConstraint(0, 0);
36656             this.dd.setYConstraint(
36657                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36658                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36659             );
36660          }
36661         this.dragSpecs.startSize = size;
36662         this.dragSpecs.startPoint = [x, y];
36663         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36664     },
36665     
36666     /** 
36667      * @private Called after the drag operation by the DDProxy
36668      */
36669     onEndProxyDrag : function(e){
36670         Roo.get(this.proxy).setDisplayed(false);
36671         var endPoint = Roo.lib.Event.getXY(e);
36672         if(this.overlay){
36673             this.overlay.hide();
36674         }
36675         var newSize;
36676         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36677             newSize = this.dragSpecs.startSize + 
36678                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36679                     endPoint[0] - this.dragSpecs.startPoint[0] :
36680                     this.dragSpecs.startPoint[0] - endPoint[0]
36681                 );
36682         }else{
36683             newSize = this.dragSpecs.startSize + 
36684                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36685                     endPoint[1] - this.dragSpecs.startPoint[1] :
36686                     this.dragSpecs.startPoint[1] - endPoint[1]
36687                 );
36688         }
36689         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36690         if(newSize != this.dragSpecs.startSize){
36691             if(this.fireEvent('beforeapply', this, newSize) !== false){
36692                 this.adapter.setElementSize(this, newSize);
36693                 this.fireEvent("moved", this, newSize);
36694                 this.fireEvent("resize", this, newSize);
36695             }
36696         }
36697     },
36698     
36699     /**
36700      * Get the adapter this SplitBar uses
36701      * @return The adapter object
36702      */
36703     getAdapter : function(){
36704         return this.adapter;
36705     },
36706     
36707     /**
36708      * Set the adapter this SplitBar uses
36709      * @param {Object} adapter A SplitBar adapter object
36710      */
36711     setAdapter : function(adapter){
36712         this.adapter = adapter;
36713         this.adapter.init(this);
36714     },
36715     
36716     /**
36717      * Gets the minimum size for the resizing element
36718      * @return {Number} The minimum size
36719      */
36720     getMinimumSize : function(){
36721         return this.minSize;
36722     },
36723     
36724     /**
36725      * Sets the minimum size for the resizing element
36726      * @param {Number} minSize The minimum size
36727      */
36728     setMinimumSize : function(minSize){
36729         this.minSize = minSize;
36730     },
36731     
36732     /**
36733      * Gets the maximum size for the resizing element
36734      * @return {Number} The maximum size
36735      */
36736     getMaximumSize : function(){
36737         return this.maxSize;
36738     },
36739     
36740     /**
36741      * Sets the maximum size for the resizing element
36742      * @param {Number} maxSize The maximum size
36743      */
36744     setMaximumSize : function(maxSize){
36745         this.maxSize = maxSize;
36746     },
36747     
36748     /**
36749      * Sets the initialize size for the resizing element
36750      * @param {Number} size The initial size
36751      */
36752     setCurrentSize : function(size){
36753         var oldAnimate = this.animate;
36754         this.animate = false;
36755         this.adapter.setElementSize(this, size);
36756         this.animate = oldAnimate;
36757     },
36758     
36759     /**
36760      * Destroy this splitbar. 
36761      * @param {Boolean} removeEl True to remove the element
36762      */
36763     destroy : function(removeEl){
36764         if(this.shim){
36765             this.shim.remove();
36766         }
36767         this.dd.unreg();
36768         this.proxy.parentNode.removeChild(this.proxy);
36769         if(removeEl){
36770             this.el.remove();
36771         }
36772     }
36773 });
36774
36775 /**
36776  * @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.
36777  */
36778 Roo.bootstrap.SplitBar.createProxy = function(dir){
36779     var proxy = new Roo.Element(document.createElement("div"));
36780     proxy.unselectable();
36781     var cls = 'roo-splitbar-proxy';
36782     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36783     document.body.appendChild(proxy.dom);
36784     return proxy.dom;
36785 };
36786
36787 /** 
36788  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36789  * Default Adapter. It assumes the splitter and resizing element are not positioned
36790  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36791  */
36792 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36793 };
36794
36795 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36796     // do nothing for now
36797     init : function(s){
36798     
36799     },
36800     /**
36801      * Called before drag operations to get the current size of the resizing element. 
36802      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36803      */
36804      getElementSize : function(s){
36805         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36806             return s.resizingEl.getWidth();
36807         }else{
36808             return s.resizingEl.getHeight();
36809         }
36810     },
36811     
36812     /**
36813      * Called after drag operations to set the size of the resizing element.
36814      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36815      * @param {Number} newSize The new size to set
36816      * @param {Function} onComplete A function to be invoked when resizing is complete
36817      */
36818     setElementSize : function(s, newSize, onComplete){
36819         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36820             if(!s.animate){
36821                 s.resizingEl.setWidth(newSize);
36822                 if(onComplete){
36823                     onComplete(s, newSize);
36824                 }
36825             }else{
36826                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36827             }
36828         }else{
36829             
36830             if(!s.animate){
36831                 s.resizingEl.setHeight(newSize);
36832                 if(onComplete){
36833                     onComplete(s, newSize);
36834                 }
36835             }else{
36836                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36837             }
36838         }
36839     }
36840 };
36841
36842 /** 
36843  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36844  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36845  * Adapter that  moves the splitter element to align with the resized sizing element. 
36846  * Used with an absolute positioned SplitBar.
36847  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36848  * document.body, make sure you assign an id to the body element.
36849  */
36850 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36851     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36852     this.container = Roo.get(container);
36853 };
36854
36855 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36856     init : function(s){
36857         this.basic.init(s);
36858     },
36859     
36860     getElementSize : function(s){
36861         return this.basic.getElementSize(s);
36862     },
36863     
36864     setElementSize : function(s, newSize, onComplete){
36865         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36866     },
36867     
36868     moveSplitter : function(s){
36869         var yes = Roo.bootstrap.SplitBar;
36870         switch(s.placement){
36871             case yes.LEFT:
36872                 s.el.setX(s.resizingEl.getRight());
36873                 break;
36874             case yes.RIGHT:
36875                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36876                 break;
36877             case yes.TOP:
36878                 s.el.setY(s.resizingEl.getBottom());
36879                 break;
36880             case yes.BOTTOM:
36881                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36882                 break;
36883         }
36884     }
36885 };
36886
36887 /**
36888  * Orientation constant - Create a vertical SplitBar
36889  * @static
36890  * @type Number
36891  */
36892 Roo.bootstrap.SplitBar.VERTICAL = 1;
36893
36894 /**
36895  * Orientation constant - Create a horizontal SplitBar
36896  * @static
36897  * @type Number
36898  */
36899 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36900
36901 /**
36902  * Placement constant - The resizing element is to the left of the splitter element
36903  * @static
36904  * @type Number
36905  */
36906 Roo.bootstrap.SplitBar.LEFT = 1;
36907
36908 /**
36909  * Placement constant - The resizing element is to the right of the splitter element
36910  * @static
36911  * @type Number
36912  */
36913 Roo.bootstrap.SplitBar.RIGHT = 2;
36914
36915 /**
36916  * Placement constant - The resizing element is positioned above the splitter element
36917  * @static
36918  * @type Number
36919  */
36920 Roo.bootstrap.SplitBar.TOP = 3;
36921
36922 /**
36923  * Placement constant - The resizing element is positioned under splitter element
36924  * @static
36925  * @type Number
36926  */
36927 Roo.bootstrap.SplitBar.BOTTOM = 4;
36928 Roo.namespace("Roo.bootstrap.layout");/*
36929  * Based on:
36930  * Ext JS Library 1.1.1
36931  * Copyright(c) 2006-2007, Ext JS, LLC.
36932  *
36933  * Originally Released Under LGPL - original licence link has changed is not relivant.
36934  *
36935  * Fork - LGPL
36936  * <script type="text/javascript">
36937  */
36938
36939 /**
36940  * @class Roo.bootstrap.layout.Manager
36941  * @extends Roo.bootstrap.Component
36942  * Base class for layout managers.
36943  */
36944 Roo.bootstrap.layout.Manager = function(config)
36945 {
36946     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36947
36948
36949
36950
36951
36952     /** false to disable window resize monitoring @type Boolean */
36953     this.monitorWindowResize = true;
36954     this.regions = {};
36955     this.addEvents({
36956         /**
36957          * @event layout
36958          * Fires when a layout is performed.
36959          * @param {Roo.LayoutManager} this
36960          */
36961         "layout" : true,
36962         /**
36963          * @event regionresized
36964          * Fires when the user resizes a region.
36965          * @param {Roo.LayoutRegion} region The resized region
36966          * @param {Number} newSize The new size (width for east/west, height for north/south)
36967          */
36968         "regionresized" : true,
36969         /**
36970          * @event regioncollapsed
36971          * Fires when a region is collapsed.
36972          * @param {Roo.LayoutRegion} region The collapsed region
36973          */
36974         "regioncollapsed" : true,
36975         /**
36976          * @event regionexpanded
36977          * Fires when a region is expanded.
36978          * @param {Roo.LayoutRegion} region The expanded region
36979          */
36980         "regionexpanded" : true
36981     });
36982     this.updating = false;
36983
36984     if (config.el) {
36985         this.el = Roo.get(config.el);
36986         this.initEvents();
36987     }
36988
36989 };
36990
36991 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36992
36993
36994     regions : null,
36995
36996     monitorWindowResize : true,
36997
36998
36999     updating : false,
37000
37001
37002     onRender : function(ct, position)
37003     {
37004         if(!this.el){
37005             this.el = Roo.get(ct);
37006             this.initEvents();
37007         }
37008         //this.fireEvent('render',this);
37009     },
37010
37011
37012     initEvents: function()
37013     {
37014
37015
37016         // ie scrollbar fix
37017         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37018             document.body.scroll = "no";
37019         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37020             this.el.position('relative');
37021         }
37022         this.id = this.el.id;
37023         this.el.addClass("roo-layout-container");
37024         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37025         if(this.el.dom != document.body ) {
37026             this.el.on('resize', this.layout,this);
37027             this.el.on('show', this.layout,this);
37028         }
37029
37030     },
37031
37032     /**
37033      * Returns true if this layout is currently being updated
37034      * @return {Boolean}
37035      */
37036     isUpdating : function(){
37037         return this.updating;
37038     },
37039
37040     /**
37041      * Suspend the LayoutManager from doing auto-layouts while
37042      * making multiple add or remove calls
37043      */
37044     beginUpdate : function(){
37045         this.updating = true;
37046     },
37047
37048     /**
37049      * Restore auto-layouts and optionally disable the manager from performing a layout
37050      * @param {Boolean} noLayout true to disable a layout update
37051      */
37052     endUpdate : function(noLayout){
37053         this.updating = false;
37054         if(!noLayout){
37055             this.layout();
37056         }
37057     },
37058
37059     layout: function(){
37060         // abstract...
37061     },
37062
37063     onRegionResized : function(region, newSize){
37064         this.fireEvent("regionresized", region, newSize);
37065         this.layout();
37066     },
37067
37068     onRegionCollapsed : function(region){
37069         this.fireEvent("regioncollapsed", region);
37070     },
37071
37072     onRegionExpanded : function(region){
37073         this.fireEvent("regionexpanded", region);
37074     },
37075
37076     /**
37077      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37078      * performs box-model adjustments.
37079      * @return {Object} The size as an object {width: (the width), height: (the height)}
37080      */
37081     getViewSize : function()
37082     {
37083         var size;
37084         if(this.el.dom != document.body){
37085             size = this.el.getSize();
37086         }else{
37087             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37088         }
37089         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37090         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37091         return size;
37092     },
37093
37094     /**
37095      * Returns the Element this layout is bound to.
37096      * @return {Roo.Element}
37097      */
37098     getEl : function(){
37099         return this.el;
37100     },
37101
37102     /**
37103      * Returns the specified region.
37104      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37105      * @return {Roo.LayoutRegion}
37106      */
37107     getRegion : function(target){
37108         return this.regions[target.toLowerCase()];
37109     },
37110
37111     onWindowResize : function(){
37112         if(this.monitorWindowResize){
37113             this.layout();
37114         }
37115     }
37116 });
37117 /*
37118  * Based on:
37119  * Ext JS Library 1.1.1
37120  * Copyright(c) 2006-2007, Ext JS, LLC.
37121  *
37122  * Originally Released Under LGPL - original licence link has changed is not relivant.
37123  *
37124  * Fork - LGPL
37125  * <script type="text/javascript">
37126  */
37127 /**
37128  * @class Roo.bootstrap.layout.Border
37129  * @extends Roo.bootstrap.layout.Manager
37130  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37131  * please see: examples/bootstrap/nested.html<br><br>
37132  
37133 <b>The container the layout is rendered into can be either the body element or any other element.
37134 If it is not the body element, the container needs to either be an absolute positioned element,
37135 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37136 the container size if it is not the body element.</b>
37137
37138 * @constructor
37139 * Create a new Border
37140 * @param {Object} config Configuration options
37141  */
37142 Roo.bootstrap.layout.Border = function(config){
37143     config = config || {};
37144     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37145     
37146     
37147     
37148     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37149         if(config[region]){
37150             config[region].region = region;
37151             this.addRegion(config[region]);
37152         }
37153     },this);
37154     
37155 };
37156
37157 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37158
37159 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37160     
37161     parent : false, // this might point to a 'nest' or a ???
37162     
37163     /**
37164      * Creates and adds a new region if it doesn't already exist.
37165      * @param {String} target The target region key (north, south, east, west or center).
37166      * @param {Object} config The regions config object
37167      * @return {BorderLayoutRegion} The new region
37168      */
37169     addRegion : function(config)
37170     {
37171         if(!this.regions[config.region]){
37172             var r = this.factory(config);
37173             this.bindRegion(r);
37174         }
37175         return this.regions[config.region];
37176     },
37177
37178     // private (kinda)
37179     bindRegion : function(r){
37180         this.regions[r.config.region] = r;
37181         
37182         r.on("visibilitychange",    this.layout, this);
37183         r.on("paneladded",          this.layout, this);
37184         r.on("panelremoved",        this.layout, this);
37185         r.on("invalidated",         this.layout, this);
37186         r.on("resized",             this.onRegionResized, this);
37187         r.on("collapsed",           this.onRegionCollapsed, this);
37188         r.on("expanded",            this.onRegionExpanded, this);
37189     },
37190
37191     /**
37192      * Performs a layout update.
37193      */
37194     layout : function()
37195     {
37196         if(this.updating) {
37197             return;
37198         }
37199         
37200         // render all the rebions if they have not been done alreayd?
37201         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37202             if(this.regions[region] && !this.regions[region].bodyEl){
37203                 this.regions[region].onRender(this.el)
37204             }
37205         },this);
37206         
37207         var size = this.getViewSize();
37208         var w = size.width;
37209         var h = size.height;
37210         var centerW = w;
37211         var centerH = h;
37212         var centerY = 0;
37213         var centerX = 0;
37214         //var x = 0, y = 0;
37215
37216         var rs = this.regions;
37217         var north = rs["north"];
37218         var south = rs["south"]; 
37219         var west = rs["west"];
37220         var east = rs["east"];
37221         var center = rs["center"];
37222         //if(this.hideOnLayout){ // not supported anymore
37223             //c.el.setStyle("display", "none");
37224         //}
37225         if(north && north.isVisible()){
37226             var b = north.getBox();
37227             var m = north.getMargins();
37228             b.width = w - (m.left+m.right);
37229             b.x = m.left;
37230             b.y = m.top;
37231             centerY = b.height + b.y + m.bottom;
37232             centerH -= centerY;
37233             north.updateBox(this.safeBox(b));
37234         }
37235         if(south && south.isVisible()){
37236             var b = south.getBox();
37237             var m = south.getMargins();
37238             b.width = w - (m.left+m.right);
37239             b.x = m.left;
37240             var totalHeight = (b.height + m.top + m.bottom);
37241             b.y = h - totalHeight + m.top;
37242             centerH -= totalHeight;
37243             south.updateBox(this.safeBox(b));
37244         }
37245         if(west && west.isVisible()){
37246             var b = west.getBox();
37247             var m = west.getMargins();
37248             b.height = centerH - (m.top+m.bottom);
37249             b.x = m.left;
37250             b.y = centerY + m.top;
37251             var totalWidth = (b.width + m.left + m.right);
37252             centerX += totalWidth;
37253             centerW -= totalWidth;
37254             west.updateBox(this.safeBox(b));
37255         }
37256         if(east && east.isVisible()){
37257             var b = east.getBox();
37258             var m = east.getMargins();
37259             b.height = centerH - (m.top+m.bottom);
37260             var totalWidth = (b.width + m.left + m.right);
37261             b.x = w - totalWidth + m.left;
37262             b.y = centerY + m.top;
37263             centerW -= totalWidth;
37264             east.updateBox(this.safeBox(b));
37265         }
37266         if(center){
37267             var m = center.getMargins();
37268             var centerBox = {
37269                 x: centerX + m.left,
37270                 y: centerY + m.top,
37271                 width: centerW - (m.left+m.right),
37272                 height: centerH - (m.top+m.bottom)
37273             };
37274             //if(this.hideOnLayout){
37275                 //center.el.setStyle("display", "block");
37276             //}
37277             center.updateBox(this.safeBox(centerBox));
37278         }
37279         this.el.repaint();
37280         this.fireEvent("layout", this);
37281     },
37282
37283     // private
37284     safeBox : function(box){
37285         box.width = Math.max(0, box.width);
37286         box.height = Math.max(0, box.height);
37287         return box;
37288     },
37289
37290     /**
37291      * Adds a ContentPanel (or subclass) to this layout.
37292      * @param {String} target The target region key (north, south, east, west or center).
37293      * @param {Roo.ContentPanel} panel The panel to add
37294      * @return {Roo.ContentPanel} The added panel
37295      */
37296     add : function(target, panel){
37297          
37298         target = target.toLowerCase();
37299         return this.regions[target].add(panel);
37300     },
37301
37302     /**
37303      * Remove a ContentPanel (or subclass) to this layout.
37304      * @param {String} target The target region key (north, south, east, west or center).
37305      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37306      * @return {Roo.ContentPanel} The removed panel
37307      */
37308     remove : function(target, panel){
37309         target = target.toLowerCase();
37310         return this.regions[target].remove(panel);
37311     },
37312
37313     /**
37314      * Searches all regions for a panel with the specified id
37315      * @param {String} panelId
37316      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37317      */
37318     findPanel : function(panelId){
37319         var rs = this.regions;
37320         for(var target in rs){
37321             if(typeof rs[target] != "function"){
37322                 var p = rs[target].getPanel(panelId);
37323                 if(p){
37324                     return p;
37325                 }
37326             }
37327         }
37328         return null;
37329     },
37330
37331     /**
37332      * Searches all regions for a panel with the specified id and activates (shows) it.
37333      * @param {String/ContentPanel} panelId The panels id or the panel itself
37334      * @return {Roo.ContentPanel} The shown panel or null
37335      */
37336     showPanel : function(panelId) {
37337       var rs = this.regions;
37338       for(var target in rs){
37339          var r = rs[target];
37340          if(typeof r != "function"){
37341             if(r.hasPanel(panelId)){
37342                return r.showPanel(panelId);
37343             }
37344          }
37345       }
37346       return null;
37347    },
37348
37349    /**
37350      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37351      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37352      */
37353    /*
37354     restoreState : function(provider){
37355         if(!provider){
37356             provider = Roo.state.Manager;
37357         }
37358         var sm = new Roo.LayoutStateManager();
37359         sm.init(this, provider);
37360     },
37361 */
37362  
37363  
37364     /**
37365      * Adds a xtype elements to the layout.
37366      * <pre><code>
37367
37368 layout.addxtype({
37369        xtype : 'ContentPanel',
37370        region: 'west',
37371        items: [ .... ]
37372    }
37373 );
37374
37375 layout.addxtype({
37376         xtype : 'NestedLayoutPanel',
37377         region: 'west',
37378         layout: {
37379            center: { },
37380            west: { }   
37381         },
37382         items : [ ... list of content panels or nested layout panels.. ]
37383    }
37384 );
37385 </code></pre>
37386      * @param {Object} cfg Xtype definition of item to add.
37387      */
37388     addxtype : function(cfg)
37389     {
37390         // basically accepts a pannel...
37391         // can accept a layout region..!?!?
37392         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37393         
37394         
37395         // theory?  children can only be panels??
37396         
37397         //if (!cfg.xtype.match(/Panel$/)) {
37398         //    return false;
37399         //}
37400         var ret = false;
37401         
37402         if (typeof(cfg.region) == 'undefined') {
37403             Roo.log("Failed to add Panel, region was not set");
37404             Roo.log(cfg);
37405             return false;
37406         }
37407         var region = cfg.region;
37408         delete cfg.region;
37409         
37410           
37411         var xitems = [];
37412         if (cfg.items) {
37413             xitems = cfg.items;
37414             delete cfg.items;
37415         }
37416         var nb = false;
37417         
37418         if ( region == 'center') {
37419             Roo.log("Center: " + cfg.title);
37420         }
37421         
37422         
37423         switch(cfg.xtype) 
37424         {
37425             case 'Content':  // ContentPanel (el, cfg)
37426             case 'Scroll':  // ContentPanel (el, cfg)
37427             case 'View': 
37428                 cfg.autoCreate = cfg.autoCreate || true;
37429                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37430                 //} else {
37431                 //    var el = this.el.createChild();
37432                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37433                 //}
37434                 
37435                 this.add(region, ret);
37436                 break;
37437             
37438             /*
37439             case 'TreePanel': // our new panel!
37440                 cfg.el = this.el.createChild();
37441                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37442                 this.add(region, ret);
37443                 break;
37444             */
37445             
37446             case 'Nest': 
37447                 // create a new Layout (which is  a Border Layout...
37448                 
37449                 var clayout = cfg.layout;
37450                 clayout.el  = this.el.createChild();
37451                 clayout.items   = clayout.items  || [];
37452                 
37453                 delete cfg.layout;
37454                 
37455                 // replace this exitems with the clayout ones..
37456                 xitems = clayout.items;
37457                  
37458                 // force background off if it's in center...
37459                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37460                     cfg.background = false;
37461                 }
37462                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37463                 
37464                 
37465                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37466                 //console.log('adding nested layout panel '  + cfg.toSource());
37467                 this.add(region, ret);
37468                 nb = {}; /// find first...
37469                 break;
37470             
37471             case 'Grid':
37472                 
37473                 // needs grid and region
37474                 
37475                 //var el = this.getRegion(region).el.createChild();
37476                 /*
37477                  *var el = this.el.createChild();
37478                 // create the grid first...
37479                 cfg.grid.container = el;
37480                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37481                 */
37482                 
37483                 if (region == 'center' && this.active ) {
37484                     cfg.background = false;
37485                 }
37486                 
37487                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37488                 
37489                 this.add(region, ret);
37490                 /*
37491                 if (cfg.background) {
37492                     // render grid on panel activation (if panel background)
37493                     ret.on('activate', function(gp) {
37494                         if (!gp.grid.rendered) {
37495                     //        gp.grid.render(el);
37496                         }
37497                     });
37498                 } else {
37499                   //  cfg.grid.render(el);
37500                 }
37501                 */
37502                 break;
37503            
37504            
37505             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37506                 // it was the old xcomponent building that caused this before.
37507                 // espeically if border is the top element in the tree.
37508                 ret = this;
37509                 break; 
37510                 
37511                     
37512                 
37513                 
37514                 
37515             default:
37516                 /*
37517                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37518                     
37519                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37520                     this.add(region, ret);
37521                 } else {
37522                 */
37523                     Roo.log(cfg);
37524                     throw "Can not add '" + cfg.xtype + "' to Border";
37525                     return null;
37526              
37527                                 
37528              
37529         }
37530         this.beginUpdate();
37531         // add children..
37532         var region = '';
37533         var abn = {};
37534         Roo.each(xitems, function(i)  {
37535             region = nb && i.region ? i.region : false;
37536             
37537             var add = ret.addxtype(i);
37538            
37539             if (region) {
37540                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37541                 if (!i.background) {
37542                     abn[region] = nb[region] ;
37543                 }
37544             }
37545             
37546         });
37547         this.endUpdate();
37548
37549         // make the last non-background panel active..
37550         //if (nb) { Roo.log(abn); }
37551         if (nb) {
37552             
37553             for(var r in abn) {
37554                 region = this.getRegion(r);
37555                 if (region) {
37556                     // tried using nb[r], but it does not work..
37557                      
37558                     region.showPanel(abn[r]);
37559                    
37560                 }
37561             }
37562         }
37563         return ret;
37564         
37565     },
37566     
37567     
37568 // private
37569     factory : function(cfg)
37570     {
37571         
37572         var validRegions = Roo.bootstrap.layout.Border.regions;
37573
37574         var target = cfg.region;
37575         cfg.mgr = this;
37576         
37577         var r = Roo.bootstrap.layout;
37578         Roo.log(target);
37579         switch(target){
37580             case "north":
37581                 return new r.North(cfg);
37582             case "south":
37583                 return new r.South(cfg);
37584             case "east":
37585                 return new r.East(cfg);
37586             case "west":
37587                 return new r.West(cfg);
37588             case "center":
37589                 return new r.Center(cfg);
37590         }
37591         throw 'Layout region "'+target+'" not supported.';
37592     }
37593     
37594     
37595 });
37596  /*
37597  * Based on:
37598  * Ext JS Library 1.1.1
37599  * Copyright(c) 2006-2007, Ext JS, LLC.
37600  *
37601  * Originally Released Under LGPL - original licence link has changed is not relivant.
37602  *
37603  * Fork - LGPL
37604  * <script type="text/javascript">
37605  */
37606  
37607 /**
37608  * @class Roo.bootstrap.layout.Basic
37609  * @extends Roo.util.Observable
37610  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37611  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37612  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37613  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37614  * @cfg {string}   region  the region that it inhabits..
37615  * @cfg {bool}   skipConfig skip config?
37616  * 
37617
37618  */
37619 Roo.bootstrap.layout.Basic = function(config){
37620     
37621     this.mgr = config.mgr;
37622     
37623     this.position = config.region;
37624     
37625     var skipConfig = config.skipConfig;
37626     
37627     this.events = {
37628         /**
37629          * @scope Roo.BasicLayoutRegion
37630          */
37631         
37632         /**
37633          * @event beforeremove
37634          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37635          * @param {Roo.LayoutRegion} this
37636          * @param {Roo.ContentPanel} panel The panel
37637          * @param {Object} e The cancel event object
37638          */
37639         "beforeremove" : true,
37640         /**
37641          * @event invalidated
37642          * Fires when the layout for this region is changed.
37643          * @param {Roo.LayoutRegion} this
37644          */
37645         "invalidated" : true,
37646         /**
37647          * @event visibilitychange
37648          * Fires when this region is shown or hidden 
37649          * @param {Roo.LayoutRegion} this
37650          * @param {Boolean} visibility true or false
37651          */
37652         "visibilitychange" : true,
37653         /**
37654          * @event paneladded
37655          * Fires when a panel is added. 
37656          * @param {Roo.LayoutRegion} this
37657          * @param {Roo.ContentPanel} panel The panel
37658          */
37659         "paneladded" : true,
37660         /**
37661          * @event panelremoved
37662          * Fires when a panel is removed. 
37663          * @param {Roo.LayoutRegion} this
37664          * @param {Roo.ContentPanel} panel The panel
37665          */
37666         "panelremoved" : true,
37667         /**
37668          * @event beforecollapse
37669          * Fires when this region before collapse.
37670          * @param {Roo.LayoutRegion} this
37671          */
37672         "beforecollapse" : true,
37673         /**
37674          * @event collapsed
37675          * Fires when this region is collapsed.
37676          * @param {Roo.LayoutRegion} this
37677          */
37678         "collapsed" : true,
37679         /**
37680          * @event expanded
37681          * Fires when this region is expanded.
37682          * @param {Roo.LayoutRegion} this
37683          */
37684         "expanded" : true,
37685         /**
37686          * @event slideshow
37687          * Fires when this region is slid into view.
37688          * @param {Roo.LayoutRegion} this
37689          */
37690         "slideshow" : true,
37691         /**
37692          * @event slidehide
37693          * Fires when this region slides out of view. 
37694          * @param {Roo.LayoutRegion} this
37695          */
37696         "slidehide" : true,
37697         /**
37698          * @event panelactivated
37699          * Fires when a panel is activated. 
37700          * @param {Roo.LayoutRegion} this
37701          * @param {Roo.ContentPanel} panel The activated panel
37702          */
37703         "panelactivated" : true,
37704         /**
37705          * @event resized
37706          * Fires when the user resizes this region. 
37707          * @param {Roo.LayoutRegion} this
37708          * @param {Number} newSize The new size (width for east/west, height for north/south)
37709          */
37710         "resized" : true
37711     };
37712     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37713     this.panels = new Roo.util.MixedCollection();
37714     this.panels.getKey = this.getPanelId.createDelegate(this);
37715     this.box = null;
37716     this.activePanel = null;
37717     // ensure listeners are added...
37718     
37719     if (config.listeners || config.events) {
37720         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37721             listeners : config.listeners || {},
37722             events : config.events || {}
37723         });
37724     }
37725     
37726     if(skipConfig !== true){
37727         this.applyConfig(config);
37728     }
37729 };
37730
37731 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37732 {
37733     getPanelId : function(p){
37734         return p.getId();
37735     },
37736     
37737     applyConfig : function(config){
37738         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37739         this.config = config;
37740         
37741     },
37742     
37743     /**
37744      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37745      * the width, for horizontal (north, south) the height.
37746      * @param {Number} newSize The new width or height
37747      */
37748     resizeTo : function(newSize){
37749         var el = this.el ? this.el :
37750                  (this.activePanel ? this.activePanel.getEl() : null);
37751         if(el){
37752             switch(this.position){
37753                 case "east":
37754                 case "west":
37755                     el.setWidth(newSize);
37756                     this.fireEvent("resized", this, newSize);
37757                 break;
37758                 case "north":
37759                 case "south":
37760                     el.setHeight(newSize);
37761                     this.fireEvent("resized", this, newSize);
37762                 break;                
37763             }
37764         }
37765     },
37766     
37767     getBox : function(){
37768         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37769     },
37770     
37771     getMargins : function(){
37772         return this.margins;
37773     },
37774     
37775     updateBox : function(box){
37776         this.box = box;
37777         var el = this.activePanel.getEl();
37778         el.dom.style.left = box.x + "px";
37779         el.dom.style.top = box.y + "px";
37780         this.activePanel.setSize(box.width, box.height);
37781     },
37782     
37783     /**
37784      * Returns the container element for this region.
37785      * @return {Roo.Element}
37786      */
37787     getEl : function(){
37788         return this.activePanel;
37789     },
37790     
37791     /**
37792      * Returns true if this region is currently visible.
37793      * @return {Boolean}
37794      */
37795     isVisible : function(){
37796         return this.activePanel ? true : false;
37797     },
37798     
37799     setActivePanel : function(panel){
37800         panel = this.getPanel(panel);
37801         if(this.activePanel && this.activePanel != panel){
37802             this.activePanel.setActiveState(false);
37803             this.activePanel.getEl().setLeftTop(-10000,-10000);
37804         }
37805         this.activePanel = panel;
37806         panel.setActiveState(true);
37807         if(this.box){
37808             panel.setSize(this.box.width, this.box.height);
37809         }
37810         this.fireEvent("panelactivated", this, panel);
37811         this.fireEvent("invalidated");
37812     },
37813     
37814     /**
37815      * Show the specified panel.
37816      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37817      * @return {Roo.ContentPanel} The shown panel or null
37818      */
37819     showPanel : function(panel){
37820         panel = this.getPanel(panel);
37821         if(panel){
37822             this.setActivePanel(panel);
37823         }
37824         return panel;
37825     },
37826     
37827     /**
37828      * Get the active panel for this region.
37829      * @return {Roo.ContentPanel} The active panel or null
37830      */
37831     getActivePanel : function(){
37832         return this.activePanel;
37833     },
37834     
37835     /**
37836      * Add the passed ContentPanel(s)
37837      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37838      * @return {Roo.ContentPanel} The panel added (if only one was added)
37839      */
37840     add : function(panel){
37841         if(arguments.length > 1){
37842             for(var i = 0, len = arguments.length; i < len; i++) {
37843                 this.add(arguments[i]);
37844             }
37845             return null;
37846         }
37847         if(this.hasPanel(panel)){
37848             this.showPanel(panel);
37849             return panel;
37850         }
37851         var el = panel.getEl();
37852         if(el.dom.parentNode != this.mgr.el.dom){
37853             this.mgr.el.dom.appendChild(el.dom);
37854         }
37855         if(panel.setRegion){
37856             panel.setRegion(this);
37857         }
37858         this.panels.add(panel);
37859         el.setStyle("position", "absolute");
37860         if(!panel.background){
37861             this.setActivePanel(panel);
37862             if(this.config.initialSize && this.panels.getCount()==1){
37863                 this.resizeTo(this.config.initialSize);
37864             }
37865         }
37866         this.fireEvent("paneladded", this, panel);
37867         return panel;
37868     },
37869     
37870     /**
37871      * Returns true if the panel is in this region.
37872      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37873      * @return {Boolean}
37874      */
37875     hasPanel : function(panel){
37876         if(typeof panel == "object"){ // must be panel obj
37877             panel = panel.getId();
37878         }
37879         return this.getPanel(panel) ? true : false;
37880     },
37881     
37882     /**
37883      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37884      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37885      * @param {Boolean} preservePanel Overrides the config preservePanel option
37886      * @return {Roo.ContentPanel} The panel that was removed
37887      */
37888     remove : function(panel, preservePanel){
37889         panel = this.getPanel(panel);
37890         if(!panel){
37891             return null;
37892         }
37893         var e = {};
37894         this.fireEvent("beforeremove", this, panel, e);
37895         if(e.cancel === true){
37896             return null;
37897         }
37898         var panelId = panel.getId();
37899         this.panels.removeKey(panelId);
37900         return panel;
37901     },
37902     
37903     /**
37904      * Returns the panel specified or null if it's not in this region.
37905      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37906      * @return {Roo.ContentPanel}
37907      */
37908     getPanel : function(id){
37909         if(typeof id == "object"){ // must be panel obj
37910             return id;
37911         }
37912         return this.panels.get(id);
37913     },
37914     
37915     /**
37916      * Returns this regions position (north/south/east/west/center).
37917      * @return {String} 
37918      */
37919     getPosition: function(){
37920         return this.position;    
37921     }
37922 });/*
37923  * Based on:
37924  * Ext JS Library 1.1.1
37925  * Copyright(c) 2006-2007, Ext JS, LLC.
37926  *
37927  * Originally Released Under LGPL - original licence link has changed is not relivant.
37928  *
37929  * Fork - LGPL
37930  * <script type="text/javascript">
37931  */
37932  
37933 /**
37934  * @class Roo.bootstrap.layout.Region
37935  * @extends Roo.bootstrap.layout.Basic
37936  * This class represents a region in a layout manager.
37937  
37938  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37939  * @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})
37940  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37941  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37942  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37943  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37944  * @cfg {String}    title           The title for the region (overrides panel titles)
37945  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37946  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37947  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37948  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37949  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37950  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37951  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37952  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37953  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37954  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37955
37956  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37957  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37958  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37959  * @cfg {Number}    width           For East/West panels
37960  * @cfg {Number}    height          For North/South panels
37961  * @cfg {Boolean}   split           To show the splitter
37962  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37963  * 
37964  * @cfg {string}   cls             Extra CSS classes to add to region
37965  * 
37966  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37967  * @cfg {string}   region  the region that it inhabits..
37968  *
37969
37970  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37971  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37972
37973  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37974  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37975  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37976  */
37977 Roo.bootstrap.layout.Region = function(config)
37978 {
37979     this.applyConfig(config);
37980
37981     var mgr = config.mgr;
37982     var pos = config.region;
37983     config.skipConfig = true;
37984     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37985     
37986     if (mgr.el) {
37987         this.onRender(mgr.el);   
37988     }
37989      
37990     this.visible = true;
37991     this.collapsed = false;
37992     this.unrendered_panels = [];
37993 };
37994
37995 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37996
37997     position: '', // set by wrapper (eg. north/south etc..)
37998     unrendered_panels : null,  // unrendered panels.
37999     
38000     tabPosition : false,
38001     
38002     mgr: false, // points to 'Border'
38003     
38004     
38005     createBody : function(){
38006         /** This region's body element 
38007         * @type Roo.Element */
38008         this.bodyEl = this.el.createChild({
38009                 tag: "div",
38010                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38011         });
38012     },
38013
38014     onRender: function(ctr, pos)
38015     {
38016         var dh = Roo.DomHelper;
38017         /** This region's container element 
38018         * @type Roo.Element */
38019         this.el = dh.append(ctr.dom, {
38020                 tag: "div",
38021                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38022             }, true);
38023         /** This region's title element 
38024         * @type Roo.Element */
38025     
38026         this.titleEl = dh.append(this.el.dom,  {
38027                 tag: "div",
38028                 unselectable: "on",
38029                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38030                 children:[
38031                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38032                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38033                 ]
38034             }, true);
38035         
38036         this.titleEl.enableDisplayMode();
38037         /** This region's title text element 
38038         * @type HTMLElement */
38039         this.titleTextEl = this.titleEl.dom.firstChild;
38040         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38041         /*
38042         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38043         this.closeBtn.enableDisplayMode();
38044         this.closeBtn.on("click", this.closeClicked, this);
38045         this.closeBtn.hide();
38046     */
38047         this.createBody(this.config);
38048         if(this.config.hideWhenEmpty){
38049             this.hide();
38050             this.on("paneladded", this.validateVisibility, this);
38051             this.on("panelremoved", this.validateVisibility, this);
38052         }
38053         if(this.autoScroll){
38054             this.bodyEl.setStyle("overflow", "auto");
38055         }else{
38056             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38057         }
38058         //if(c.titlebar !== false){
38059             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38060                 this.titleEl.hide();
38061             }else{
38062                 this.titleEl.show();
38063                 if(this.config.title){
38064                     this.titleTextEl.innerHTML = this.config.title;
38065                 }
38066             }
38067         //}
38068         if(this.config.collapsed){
38069             this.collapse(true);
38070         }
38071         if(this.config.hidden){
38072             this.hide();
38073         }
38074         
38075         if (this.unrendered_panels && this.unrendered_panels.length) {
38076             for (var i =0;i< this.unrendered_panels.length; i++) {
38077                 this.add(this.unrendered_panels[i]);
38078             }
38079             this.unrendered_panels = null;
38080             
38081         }
38082         
38083     },
38084     
38085     applyConfig : function(c)
38086     {
38087         /*
38088          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38089             var dh = Roo.DomHelper;
38090             if(c.titlebar !== false){
38091                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38092                 this.collapseBtn.on("click", this.collapse, this);
38093                 this.collapseBtn.enableDisplayMode();
38094                 /*
38095                 if(c.showPin === true || this.showPin){
38096                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38097                     this.stickBtn.enableDisplayMode();
38098                     this.stickBtn.on("click", this.expand, this);
38099                     this.stickBtn.hide();
38100                 }
38101                 
38102             }
38103             */
38104             /** This region's collapsed element
38105             * @type Roo.Element */
38106             /*
38107              *
38108             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38109                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38110             ]}, true);
38111             
38112             if(c.floatable !== false){
38113                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38114                this.collapsedEl.on("click", this.collapseClick, this);
38115             }
38116
38117             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38118                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38119                    id: "message", unselectable: "on", style:{"float":"left"}});
38120                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38121              }
38122             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38123             this.expandBtn.on("click", this.expand, this);
38124             
38125         }
38126         
38127         if(this.collapseBtn){
38128             this.collapseBtn.setVisible(c.collapsible == true);
38129         }
38130         
38131         this.cmargins = c.cmargins || this.cmargins ||
38132                          (this.position == "west" || this.position == "east" ?
38133                              {top: 0, left: 2, right:2, bottom: 0} :
38134                              {top: 2, left: 0, right:0, bottom: 2});
38135         */
38136         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38137         
38138         
38139         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38140         
38141         this.autoScroll = c.autoScroll || false;
38142         
38143         
38144        
38145         
38146         this.duration = c.duration || .30;
38147         this.slideDuration = c.slideDuration || .45;
38148         this.config = c;
38149        
38150     },
38151     /**
38152      * Returns true if this region is currently visible.
38153      * @return {Boolean}
38154      */
38155     isVisible : function(){
38156         return this.visible;
38157     },
38158
38159     /**
38160      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38161      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38162      */
38163     //setCollapsedTitle : function(title){
38164     //    title = title || "&#160;";
38165      //   if(this.collapsedTitleTextEl){
38166       //      this.collapsedTitleTextEl.innerHTML = title;
38167        // }
38168     //},
38169
38170     getBox : function(){
38171         var b;
38172       //  if(!this.collapsed){
38173             b = this.el.getBox(false, true);
38174        // }else{
38175           //  b = this.collapsedEl.getBox(false, true);
38176         //}
38177         return b;
38178     },
38179
38180     getMargins : function(){
38181         return this.margins;
38182         //return this.collapsed ? this.cmargins : this.margins;
38183     },
38184 /*
38185     highlight : function(){
38186         this.el.addClass("x-layout-panel-dragover");
38187     },
38188
38189     unhighlight : function(){
38190         this.el.removeClass("x-layout-panel-dragover");
38191     },
38192 */
38193     updateBox : function(box)
38194     {
38195         if (!this.bodyEl) {
38196             return; // not rendered yet..
38197         }
38198         
38199         this.box = box;
38200         if(!this.collapsed){
38201             this.el.dom.style.left = box.x + "px";
38202             this.el.dom.style.top = box.y + "px";
38203             this.updateBody(box.width, box.height);
38204         }else{
38205             this.collapsedEl.dom.style.left = box.x + "px";
38206             this.collapsedEl.dom.style.top = box.y + "px";
38207             this.collapsedEl.setSize(box.width, box.height);
38208         }
38209         if(this.tabs){
38210             this.tabs.autoSizeTabs();
38211         }
38212     },
38213
38214     updateBody : function(w, h)
38215     {
38216         if(w !== null){
38217             this.el.setWidth(w);
38218             w -= this.el.getBorderWidth("rl");
38219             if(this.config.adjustments){
38220                 w += this.config.adjustments[0];
38221             }
38222         }
38223         if(h !== null && h > 0){
38224             this.el.setHeight(h);
38225             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38226             h -= this.el.getBorderWidth("tb");
38227             if(this.config.adjustments){
38228                 h += this.config.adjustments[1];
38229             }
38230             this.bodyEl.setHeight(h);
38231             if(this.tabs){
38232                 h = this.tabs.syncHeight(h);
38233             }
38234         }
38235         if(this.panelSize){
38236             w = w !== null ? w : this.panelSize.width;
38237             h = h !== null ? h : this.panelSize.height;
38238         }
38239         if(this.activePanel){
38240             var el = this.activePanel.getEl();
38241             w = w !== null ? w : el.getWidth();
38242             h = h !== null ? h : el.getHeight();
38243             this.panelSize = {width: w, height: h};
38244             this.activePanel.setSize(w, h);
38245         }
38246         if(Roo.isIE && this.tabs){
38247             this.tabs.el.repaint();
38248         }
38249     },
38250
38251     /**
38252      * Returns the container element for this region.
38253      * @return {Roo.Element}
38254      */
38255     getEl : function(){
38256         return this.el;
38257     },
38258
38259     /**
38260      * Hides this region.
38261      */
38262     hide : function(){
38263         //if(!this.collapsed){
38264             this.el.dom.style.left = "-2000px";
38265             this.el.hide();
38266         //}else{
38267          //   this.collapsedEl.dom.style.left = "-2000px";
38268          //   this.collapsedEl.hide();
38269        // }
38270         this.visible = false;
38271         this.fireEvent("visibilitychange", this, false);
38272     },
38273
38274     /**
38275      * Shows this region if it was previously hidden.
38276      */
38277     show : function(){
38278         //if(!this.collapsed){
38279             this.el.show();
38280         //}else{
38281         //    this.collapsedEl.show();
38282        // }
38283         this.visible = true;
38284         this.fireEvent("visibilitychange", this, true);
38285     },
38286 /*
38287     closeClicked : function(){
38288         if(this.activePanel){
38289             this.remove(this.activePanel);
38290         }
38291     },
38292
38293     collapseClick : function(e){
38294         if(this.isSlid){
38295            e.stopPropagation();
38296            this.slideIn();
38297         }else{
38298            e.stopPropagation();
38299            this.slideOut();
38300         }
38301     },
38302 */
38303     /**
38304      * Collapses this region.
38305      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38306      */
38307     /*
38308     collapse : function(skipAnim, skipCheck = false){
38309         if(this.collapsed) {
38310             return;
38311         }
38312         
38313         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38314             
38315             this.collapsed = true;
38316             if(this.split){
38317                 this.split.el.hide();
38318             }
38319             if(this.config.animate && skipAnim !== true){
38320                 this.fireEvent("invalidated", this);
38321                 this.animateCollapse();
38322             }else{
38323                 this.el.setLocation(-20000,-20000);
38324                 this.el.hide();
38325                 this.collapsedEl.show();
38326                 this.fireEvent("collapsed", this);
38327                 this.fireEvent("invalidated", this);
38328             }
38329         }
38330         
38331     },
38332 */
38333     animateCollapse : function(){
38334         // overridden
38335     },
38336
38337     /**
38338      * Expands this region if it was previously collapsed.
38339      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38340      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38341      */
38342     /*
38343     expand : function(e, skipAnim){
38344         if(e) {
38345             e.stopPropagation();
38346         }
38347         if(!this.collapsed || this.el.hasActiveFx()) {
38348             return;
38349         }
38350         if(this.isSlid){
38351             this.afterSlideIn();
38352             skipAnim = true;
38353         }
38354         this.collapsed = false;
38355         if(this.config.animate && skipAnim !== true){
38356             this.animateExpand();
38357         }else{
38358             this.el.show();
38359             if(this.split){
38360                 this.split.el.show();
38361             }
38362             this.collapsedEl.setLocation(-2000,-2000);
38363             this.collapsedEl.hide();
38364             this.fireEvent("invalidated", this);
38365             this.fireEvent("expanded", this);
38366         }
38367     },
38368 */
38369     animateExpand : function(){
38370         // overridden
38371     },
38372
38373     initTabs : function()
38374     {
38375         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38376         
38377         var ts = new Roo.bootstrap.panel.Tabs({
38378             el: this.bodyEl.dom,
38379             region : this,
38380             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38381             disableTooltips: this.config.disableTabTips,
38382             toolbar : this.config.toolbar
38383         });
38384         
38385         if(this.config.hideTabs){
38386             ts.stripWrap.setDisplayed(false);
38387         }
38388         this.tabs = ts;
38389         ts.resizeTabs = this.config.resizeTabs === true;
38390         ts.minTabWidth = this.config.minTabWidth || 40;
38391         ts.maxTabWidth = this.config.maxTabWidth || 250;
38392         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38393         ts.monitorResize = false;
38394         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38395         ts.bodyEl.addClass('roo-layout-tabs-body');
38396         this.panels.each(this.initPanelAsTab, this);
38397     },
38398
38399     initPanelAsTab : function(panel){
38400         var ti = this.tabs.addTab(
38401             panel.getEl().id,
38402             panel.getTitle(),
38403             null,
38404             this.config.closeOnTab && panel.isClosable(),
38405             panel.tpl
38406         );
38407         if(panel.tabTip !== undefined){
38408             ti.setTooltip(panel.tabTip);
38409         }
38410         ti.on("activate", function(){
38411               this.setActivePanel(panel);
38412         }, this);
38413         
38414         if(this.config.closeOnTab){
38415             ti.on("beforeclose", function(t, e){
38416                 e.cancel = true;
38417                 this.remove(panel);
38418             }, this);
38419         }
38420         
38421         panel.tabItem = ti;
38422         
38423         return ti;
38424     },
38425
38426     updatePanelTitle : function(panel, title)
38427     {
38428         if(this.activePanel == panel){
38429             this.updateTitle(title);
38430         }
38431         if(this.tabs){
38432             var ti = this.tabs.getTab(panel.getEl().id);
38433             ti.setText(title);
38434             if(panel.tabTip !== undefined){
38435                 ti.setTooltip(panel.tabTip);
38436             }
38437         }
38438     },
38439
38440     updateTitle : function(title){
38441         if(this.titleTextEl && !this.config.title){
38442             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38443         }
38444     },
38445
38446     setActivePanel : function(panel)
38447     {
38448         panel = this.getPanel(panel);
38449         if(this.activePanel && this.activePanel != panel){
38450             if(this.activePanel.setActiveState(false) === false){
38451                 return;
38452             }
38453         }
38454         this.activePanel = panel;
38455         panel.setActiveState(true);
38456         if(this.panelSize){
38457             panel.setSize(this.panelSize.width, this.panelSize.height);
38458         }
38459         if(this.closeBtn){
38460             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38461         }
38462         this.updateTitle(panel.getTitle());
38463         if(this.tabs){
38464             this.fireEvent("invalidated", this);
38465         }
38466         this.fireEvent("panelactivated", this, panel);
38467     },
38468
38469     /**
38470      * Shows the specified panel.
38471      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38472      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38473      */
38474     showPanel : function(panel)
38475     {
38476         panel = this.getPanel(panel);
38477         if(panel){
38478             if(this.tabs){
38479                 var tab = this.tabs.getTab(panel.getEl().id);
38480                 if(tab.isHidden()){
38481                     this.tabs.unhideTab(tab.id);
38482                 }
38483                 tab.activate();
38484             }else{
38485                 this.setActivePanel(panel);
38486             }
38487         }
38488         return panel;
38489     },
38490
38491     /**
38492      * Get the active panel for this region.
38493      * @return {Roo.ContentPanel} The active panel or null
38494      */
38495     getActivePanel : function(){
38496         return this.activePanel;
38497     },
38498
38499     validateVisibility : function(){
38500         if(this.panels.getCount() < 1){
38501             this.updateTitle("&#160;");
38502             this.closeBtn.hide();
38503             this.hide();
38504         }else{
38505             if(!this.isVisible()){
38506                 this.show();
38507             }
38508         }
38509     },
38510
38511     /**
38512      * Adds the passed ContentPanel(s) to this region.
38513      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38514      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38515      */
38516     add : function(panel)
38517     {
38518         if(arguments.length > 1){
38519             for(var i = 0, len = arguments.length; i < len; i++) {
38520                 this.add(arguments[i]);
38521             }
38522             return null;
38523         }
38524         
38525         // if we have not been rendered yet, then we can not really do much of this..
38526         if (!this.bodyEl) {
38527             this.unrendered_panels.push(panel);
38528             return panel;
38529         }
38530         
38531         
38532         
38533         
38534         if(this.hasPanel(panel)){
38535             this.showPanel(panel);
38536             return panel;
38537         }
38538         panel.setRegion(this);
38539         this.panels.add(panel);
38540        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38541             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38542             // and hide them... ???
38543             this.bodyEl.dom.appendChild(panel.getEl().dom);
38544             if(panel.background !== true){
38545                 this.setActivePanel(panel);
38546             }
38547             this.fireEvent("paneladded", this, panel);
38548             return panel;
38549         }
38550         */
38551         if(!this.tabs){
38552             this.initTabs();
38553         }else{
38554             this.initPanelAsTab(panel);
38555         }
38556         
38557         
38558         if(panel.background !== true){
38559             this.tabs.activate(panel.getEl().id);
38560         }
38561         this.fireEvent("paneladded", this, panel);
38562         return panel;
38563     },
38564
38565     /**
38566      * Hides the tab for the specified panel.
38567      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38568      */
38569     hidePanel : function(panel){
38570         if(this.tabs && (panel = this.getPanel(panel))){
38571             this.tabs.hideTab(panel.getEl().id);
38572         }
38573     },
38574
38575     /**
38576      * Unhides the tab for a previously hidden panel.
38577      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38578      */
38579     unhidePanel : function(panel){
38580         if(this.tabs && (panel = this.getPanel(panel))){
38581             this.tabs.unhideTab(panel.getEl().id);
38582         }
38583     },
38584
38585     clearPanels : function(){
38586         while(this.panels.getCount() > 0){
38587              this.remove(this.panels.first());
38588         }
38589     },
38590
38591     /**
38592      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38593      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38594      * @param {Boolean} preservePanel Overrides the config preservePanel option
38595      * @return {Roo.ContentPanel} The panel that was removed
38596      */
38597     remove : function(panel, preservePanel)
38598     {
38599         panel = this.getPanel(panel);
38600         if(!panel){
38601             return null;
38602         }
38603         var e = {};
38604         this.fireEvent("beforeremove", this, panel, e);
38605         if(e.cancel === true){
38606             return null;
38607         }
38608         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38609         var panelId = panel.getId();
38610         this.panels.removeKey(panelId);
38611         if(preservePanel){
38612             document.body.appendChild(panel.getEl().dom);
38613         }
38614         if(this.tabs){
38615             this.tabs.removeTab(panel.getEl().id);
38616         }else if (!preservePanel){
38617             this.bodyEl.dom.removeChild(panel.getEl().dom);
38618         }
38619         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38620             var p = this.panels.first();
38621             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38622             tempEl.appendChild(p.getEl().dom);
38623             this.bodyEl.update("");
38624             this.bodyEl.dom.appendChild(p.getEl().dom);
38625             tempEl = null;
38626             this.updateTitle(p.getTitle());
38627             this.tabs = null;
38628             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38629             this.setActivePanel(p);
38630         }
38631         panel.setRegion(null);
38632         if(this.activePanel == panel){
38633             this.activePanel = null;
38634         }
38635         if(this.config.autoDestroy !== false && preservePanel !== true){
38636             try{panel.destroy();}catch(e){}
38637         }
38638         this.fireEvent("panelremoved", this, panel);
38639         return panel;
38640     },
38641
38642     /**
38643      * Returns the TabPanel component used by this region
38644      * @return {Roo.TabPanel}
38645      */
38646     getTabs : function(){
38647         return this.tabs;
38648     },
38649
38650     createTool : function(parentEl, className){
38651         var btn = Roo.DomHelper.append(parentEl, {
38652             tag: "div",
38653             cls: "x-layout-tools-button",
38654             children: [ {
38655                 tag: "div",
38656                 cls: "roo-layout-tools-button-inner " + className,
38657                 html: "&#160;"
38658             }]
38659         }, true);
38660         btn.addClassOnOver("roo-layout-tools-button-over");
38661         return btn;
38662     }
38663 });/*
38664  * Based on:
38665  * Ext JS Library 1.1.1
38666  * Copyright(c) 2006-2007, Ext JS, LLC.
38667  *
38668  * Originally Released Under LGPL - original licence link has changed is not relivant.
38669  *
38670  * Fork - LGPL
38671  * <script type="text/javascript">
38672  */
38673  
38674
38675
38676 /**
38677  * @class Roo.SplitLayoutRegion
38678  * @extends Roo.LayoutRegion
38679  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38680  */
38681 Roo.bootstrap.layout.Split = function(config){
38682     this.cursor = config.cursor;
38683     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38684 };
38685
38686 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38687 {
38688     splitTip : "Drag to resize.",
38689     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38690     useSplitTips : false,
38691
38692     applyConfig : function(config){
38693         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38694     },
38695     
38696     onRender : function(ctr,pos) {
38697         
38698         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38699         if(!this.config.split){
38700             return;
38701         }
38702         if(!this.split){
38703             
38704             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38705                             tag: "div",
38706                             id: this.el.id + "-split",
38707                             cls: "roo-layout-split roo-layout-split-"+this.position,
38708                             html: "&#160;"
38709             });
38710             /** The SplitBar for this region 
38711             * @type Roo.SplitBar */
38712             // does not exist yet...
38713             Roo.log([this.position, this.orientation]);
38714             
38715             this.split = new Roo.bootstrap.SplitBar({
38716                 dragElement : splitEl,
38717                 resizingElement: this.el,
38718                 orientation : this.orientation
38719             });
38720             
38721             this.split.on("moved", this.onSplitMove, this);
38722             this.split.useShim = this.config.useShim === true;
38723             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38724             if(this.useSplitTips){
38725                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38726             }
38727             //if(config.collapsible){
38728             //    this.split.el.on("dblclick", this.collapse,  this);
38729             //}
38730         }
38731         if(typeof this.config.minSize != "undefined"){
38732             this.split.minSize = this.config.minSize;
38733         }
38734         if(typeof this.config.maxSize != "undefined"){
38735             this.split.maxSize = this.config.maxSize;
38736         }
38737         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38738             this.hideSplitter();
38739         }
38740         
38741     },
38742
38743     getHMaxSize : function(){
38744          var cmax = this.config.maxSize || 10000;
38745          var center = this.mgr.getRegion("center");
38746          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38747     },
38748
38749     getVMaxSize : function(){
38750          var cmax = this.config.maxSize || 10000;
38751          var center = this.mgr.getRegion("center");
38752          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38753     },
38754
38755     onSplitMove : function(split, newSize){
38756         this.fireEvent("resized", this, newSize);
38757     },
38758     
38759     /** 
38760      * Returns the {@link Roo.SplitBar} for this region.
38761      * @return {Roo.SplitBar}
38762      */
38763     getSplitBar : function(){
38764         return this.split;
38765     },
38766     
38767     hide : function(){
38768         this.hideSplitter();
38769         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38770     },
38771
38772     hideSplitter : function(){
38773         if(this.split){
38774             this.split.el.setLocation(-2000,-2000);
38775             this.split.el.hide();
38776         }
38777     },
38778
38779     show : function(){
38780         if(this.split){
38781             this.split.el.show();
38782         }
38783         Roo.bootstrap.layout.Split.superclass.show.call(this);
38784     },
38785     
38786     beforeSlide: function(){
38787         if(Roo.isGecko){// firefox overflow auto bug workaround
38788             this.bodyEl.clip();
38789             if(this.tabs) {
38790                 this.tabs.bodyEl.clip();
38791             }
38792             if(this.activePanel){
38793                 this.activePanel.getEl().clip();
38794                 
38795                 if(this.activePanel.beforeSlide){
38796                     this.activePanel.beforeSlide();
38797                 }
38798             }
38799         }
38800     },
38801     
38802     afterSlide : function(){
38803         if(Roo.isGecko){// firefox overflow auto bug workaround
38804             this.bodyEl.unclip();
38805             if(this.tabs) {
38806                 this.tabs.bodyEl.unclip();
38807             }
38808             if(this.activePanel){
38809                 this.activePanel.getEl().unclip();
38810                 if(this.activePanel.afterSlide){
38811                     this.activePanel.afterSlide();
38812                 }
38813             }
38814         }
38815     },
38816
38817     initAutoHide : function(){
38818         if(this.autoHide !== false){
38819             if(!this.autoHideHd){
38820                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38821                 this.autoHideHd = {
38822                     "mouseout": function(e){
38823                         if(!e.within(this.el, true)){
38824                             st.delay(500);
38825                         }
38826                     },
38827                     "mouseover" : function(e){
38828                         st.cancel();
38829                     },
38830                     scope : this
38831                 };
38832             }
38833             this.el.on(this.autoHideHd);
38834         }
38835     },
38836
38837     clearAutoHide : function(){
38838         if(this.autoHide !== false){
38839             this.el.un("mouseout", this.autoHideHd.mouseout);
38840             this.el.un("mouseover", this.autoHideHd.mouseover);
38841         }
38842     },
38843
38844     clearMonitor : function(){
38845         Roo.get(document).un("click", this.slideInIf, this);
38846     },
38847
38848     // these names are backwards but not changed for compat
38849     slideOut : function(){
38850         if(this.isSlid || this.el.hasActiveFx()){
38851             return;
38852         }
38853         this.isSlid = true;
38854         if(this.collapseBtn){
38855             this.collapseBtn.hide();
38856         }
38857         this.closeBtnState = this.closeBtn.getStyle('display');
38858         this.closeBtn.hide();
38859         if(this.stickBtn){
38860             this.stickBtn.show();
38861         }
38862         this.el.show();
38863         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38864         this.beforeSlide();
38865         this.el.setStyle("z-index", 10001);
38866         this.el.slideIn(this.getSlideAnchor(), {
38867             callback: function(){
38868                 this.afterSlide();
38869                 this.initAutoHide();
38870                 Roo.get(document).on("click", this.slideInIf, this);
38871                 this.fireEvent("slideshow", this);
38872             },
38873             scope: this,
38874             block: true
38875         });
38876     },
38877
38878     afterSlideIn : function(){
38879         this.clearAutoHide();
38880         this.isSlid = false;
38881         this.clearMonitor();
38882         this.el.setStyle("z-index", "");
38883         if(this.collapseBtn){
38884             this.collapseBtn.show();
38885         }
38886         this.closeBtn.setStyle('display', this.closeBtnState);
38887         if(this.stickBtn){
38888             this.stickBtn.hide();
38889         }
38890         this.fireEvent("slidehide", this);
38891     },
38892
38893     slideIn : function(cb){
38894         if(!this.isSlid || this.el.hasActiveFx()){
38895             Roo.callback(cb);
38896             return;
38897         }
38898         this.isSlid = false;
38899         this.beforeSlide();
38900         this.el.slideOut(this.getSlideAnchor(), {
38901             callback: function(){
38902                 this.el.setLeftTop(-10000, -10000);
38903                 this.afterSlide();
38904                 this.afterSlideIn();
38905                 Roo.callback(cb);
38906             },
38907             scope: this,
38908             block: true
38909         });
38910     },
38911     
38912     slideInIf : function(e){
38913         if(!e.within(this.el)){
38914             this.slideIn();
38915         }
38916     },
38917
38918     animateCollapse : function(){
38919         this.beforeSlide();
38920         this.el.setStyle("z-index", 20000);
38921         var anchor = this.getSlideAnchor();
38922         this.el.slideOut(anchor, {
38923             callback : function(){
38924                 this.el.setStyle("z-index", "");
38925                 this.collapsedEl.slideIn(anchor, {duration:.3});
38926                 this.afterSlide();
38927                 this.el.setLocation(-10000,-10000);
38928                 this.el.hide();
38929                 this.fireEvent("collapsed", this);
38930             },
38931             scope: this,
38932             block: true
38933         });
38934     },
38935
38936     animateExpand : function(){
38937         this.beforeSlide();
38938         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38939         this.el.setStyle("z-index", 20000);
38940         this.collapsedEl.hide({
38941             duration:.1
38942         });
38943         this.el.slideIn(this.getSlideAnchor(), {
38944             callback : function(){
38945                 this.el.setStyle("z-index", "");
38946                 this.afterSlide();
38947                 if(this.split){
38948                     this.split.el.show();
38949                 }
38950                 this.fireEvent("invalidated", this);
38951                 this.fireEvent("expanded", this);
38952             },
38953             scope: this,
38954             block: true
38955         });
38956     },
38957
38958     anchors : {
38959         "west" : "left",
38960         "east" : "right",
38961         "north" : "top",
38962         "south" : "bottom"
38963     },
38964
38965     sanchors : {
38966         "west" : "l",
38967         "east" : "r",
38968         "north" : "t",
38969         "south" : "b"
38970     },
38971
38972     canchors : {
38973         "west" : "tl-tr",
38974         "east" : "tr-tl",
38975         "north" : "tl-bl",
38976         "south" : "bl-tl"
38977     },
38978
38979     getAnchor : function(){
38980         return this.anchors[this.position];
38981     },
38982
38983     getCollapseAnchor : function(){
38984         return this.canchors[this.position];
38985     },
38986
38987     getSlideAnchor : function(){
38988         return this.sanchors[this.position];
38989     },
38990
38991     getAlignAdj : function(){
38992         var cm = this.cmargins;
38993         switch(this.position){
38994             case "west":
38995                 return [0, 0];
38996             break;
38997             case "east":
38998                 return [0, 0];
38999             break;
39000             case "north":
39001                 return [0, 0];
39002             break;
39003             case "south":
39004                 return [0, 0];
39005             break;
39006         }
39007     },
39008
39009     getExpandAdj : function(){
39010         var c = this.collapsedEl, cm = this.cmargins;
39011         switch(this.position){
39012             case "west":
39013                 return [-(cm.right+c.getWidth()+cm.left), 0];
39014             break;
39015             case "east":
39016                 return [cm.right+c.getWidth()+cm.left, 0];
39017             break;
39018             case "north":
39019                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39020             break;
39021             case "south":
39022                 return [0, cm.top+cm.bottom+c.getHeight()];
39023             break;
39024         }
39025     }
39026 });/*
39027  * Based on:
39028  * Ext JS Library 1.1.1
39029  * Copyright(c) 2006-2007, Ext JS, LLC.
39030  *
39031  * Originally Released Under LGPL - original licence link has changed is not relivant.
39032  *
39033  * Fork - LGPL
39034  * <script type="text/javascript">
39035  */
39036 /*
39037  * These classes are private internal classes
39038  */
39039 Roo.bootstrap.layout.Center = function(config){
39040     config.region = "center";
39041     Roo.bootstrap.layout.Region.call(this, config);
39042     this.visible = true;
39043     this.minWidth = config.minWidth || 20;
39044     this.minHeight = config.minHeight || 20;
39045 };
39046
39047 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39048     hide : function(){
39049         // center panel can't be hidden
39050     },
39051     
39052     show : function(){
39053         // center panel can't be hidden
39054     },
39055     
39056     getMinWidth: function(){
39057         return this.minWidth;
39058     },
39059     
39060     getMinHeight: function(){
39061         return this.minHeight;
39062     }
39063 });
39064
39065
39066
39067
39068  
39069
39070
39071
39072
39073
39074
39075 Roo.bootstrap.layout.North = function(config)
39076 {
39077     config.region = 'north';
39078     config.cursor = 'n-resize';
39079     
39080     Roo.bootstrap.layout.Split.call(this, config);
39081     
39082     
39083     if(this.split){
39084         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39085         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39086         this.split.el.addClass("roo-layout-split-v");
39087     }
39088     var size = config.initialSize || config.height;
39089     if(typeof size != "undefined"){
39090         this.el.setHeight(size);
39091     }
39092 };
39093 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39094 {
39095     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39096     
39097     
39098     
39099     getBox : function(){
39100         if(this.collapsed){
39101             return this.collapsedEl.getBox();
39102         }
39103         var box = this.el.getBox();
39104         if(this.split){
39105             box.height += this.split.el.getHeight();
39106         }
39107         return box;
39108     },
39109     
39110     updateBox : function(box){
39111         if(this.split && !this.collapsed){
39112             box.height -= this.split.el.getHeight();
39113             this.split.el.setLeft(box.x);
39114             this.split.el.setTop(box.y+box.height);
39115             this.split.el.setWidth(box.width);
39116         }
39117         if(this.collapsed){
39118             this.updateBody(box.width, null);
39119         }
39120         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39121     }
39122 });
39123
39124
39125
39126
39127
39128 Roo.bootstrap.layout.South = function(config){
39129     config.region = 'south';
39130     config.cursor = 's-resize';
39131     Roo.bootstrap.layout.Split.call(this, config);
39132     if(this.split){
39133         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39134         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39135         this.split.el.addClass("roo-layout-split-v");
39136     }
39137     var size = config.initialSize || config.height;
39138     if(typeof size != "undefined"){
39139         this.el.setHeight(size);
39140     }
39141 };
39142
39143 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39144     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39145     getBox : function(){
39146         if(this.collapsed){
39147             return this.collapsedEl.getBox();
39148         }
39149         var box = this.el.getBox();
39150         if(this.split){
39151             var sh = this.split.el.getHeight();
39152             box.height += sh;
39153             box.y -= sh;
39154         }
39155         return box;
39156     },
39157     
39158     updateBox : function(box){
39159         if(this.split && !this.collapsed){
39160             var sh = this.split.el.getHeight();
39161             box.height -= sh;
39162             box.y += sh;
39163             this.split.el.setLeft(box.x);
39164             this.split.el.setTop(box.y-sh);
39165             this.split.el.setWidth(box.width);
39166         }
39167         if(this.collapsed){
39168             this.updateBody(box.width, null);
39169         }
39170         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39171     }
39172 });
39173
39174 Roo.bootstrap.layout.East = function(config){
39175     config.region = "east";
39176     config.cursor = "e-resize";
39177     Roo.bootstrap.layout.Split.call(this, config);
39178     if(this.split){
39179         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39180         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39181         this.split.el.addClass("roo-layout-split-h");
39182     }
39183     var size = config.initialSize || config.width;
39184     if(typeof size != "undefined"){
39185         this.el.setWidth(size);
39186     }
39187 };
39188 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39189     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39190     getBox : function(){
39191         if(this.collapsed){
39192             return this.collapsedEl.getBox();
39193         }
39194         var box = this.el.getBox();
39195         if(this.split){
39196             var sw = this.split.el.getWidth();
39197             box.width += sw;
39198             box.x -= sw;
39199         }
39200         return box;
39201     },
39202
39203     updateBox : function(box){
39204         if(this.split && !this.collapsed){
39205             var sw = this.split.el.getWidth();
39206             box.width -= sw;
39207             this.split.el.setLeft(box.x);
39208             this.split.el.setTop(box.y);
39209             this.split.el.setHeight(box.height);
39210             box.x += sw;
39211         }
39212         if(this.collapsed){
39213             this.updateBody(null, box.height);
39214         }
39215         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39216     }
39217 });
39218
39219 Roo.bootstrap.layout.West = function(config){
39220     config.region = "west";
39221     config.cursor = "w-resize";
39222     
39223     Roo.bootstrap.layout.Split.call(this, config);
39224     if(this.split){
39225         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39226         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39227         this.split.el.addClass("roo-layout-split-h");
39228     }
39229     
39230 };
39231 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39232     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39233     
39234     onRender: function(ctr, pos)
39235     {
39236         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39237         var size = this.config.initialSize || this.config.width;
39238         if(typeof size != "undefined"){
39239             this.el.setWidth(size);
39240         }
39241     },
39242     
39243     getBox : function(){
39244         if(this.collapsed){
39245             return this.collapsedEl.getBox();
39246         }
39247         var box = this.el.getBox();
39248         if(this.split){
39249             box.width += this.split.el.getWidth();
39250         }
39251         return box;
39252     },
39253     
39254     updateBox : function(box){
39255         if(this.split && !this.collapsed){
39256             var sw = this.split.el.getWidth();
39257             box.width -= sw;
39258             this.split.el.setLeft(box.x+box.width);
39259             this.split.el.setTop(box.y);
39260             this.split.el.setHeight(box.height);
39261         }
39262         if(this.collapsed){
39263             this.updateBody(null, box.height);
39264         }
39265         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39266     }
39267 });Roo.namespace("Roo.bootstrap.panel");/*
39268  * Based on:
39269  * Ext JS Library 1.1.1
39270  * Copyright(c) 2006-2007, Ext JS, LLC.
39271  *
39272  * Originally Released Under LGPL - original licence link has changed is not relivant.
39273  *
39274  * Fork - LGPL
39275  * <script type="text/javascript">
39276  */
39277 /**
39278  * @class Roo.ContentPanel
39279  * @extends Roo.util.Observable
39280  * A basic ContentPanel element.
39281  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39282  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39283  * @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
39284  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39285  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39286  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39287  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39288  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39289  * @cfg {String} title          The title for this panel
39290  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39291  * @cfg {String} url            Calls {@link #setUrl} with this value
39292  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39293  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39294  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39295  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39296  * @cfg {Boolean} badges render the badges
39297  * @cfg {String} cls  extra classes to use  
39298  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39299
39300  * @constructor
39301  * Create a new ContentPanel.
39302  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39303  * @param {String/Object} config A string to set only the title or a config object
39304  * @param {String} content (optional) Set the HTML content for this panel
39305  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39306  */
39307 Roo.bootstrap.panel.Content = function( config){
39308     
39309     this.tpl = config.tpl || false;
39310     
39311     var el = config.el;
39312     var content = config.content;
39313
39314     if(config.autoCreate){ // xtype is available if this is called from factory
39315         el = Roo.id();
39316     }
39317     this.el = Roo.get(el);
39318     if(!this.el && config && config.autoCreate){
39319         if(typeof config.autoCreate == "object"){
39320             if(!config.autoCreate.id){
39321                 config.autoCreate.id = config.id||el;
39322             }
39323             this.el = Roo.DomHelper.append(document.body,
39324                         config.autoCreate, true);
39325         }else{
39326             var elcfg =  {
39327                 tag: "div",
39328                 cls: (config.cls || '') +
39329                     (config.background ? ' bg-' + config.background : '') +
39330                     " roo-layout-inactive-content",
39331                 id: config.id||el
39332             };
39333             if (config.html) {
39334                 elcfg.html = config.html;
39335                 
39336             }
39337                         
39338             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39339         }
39340     } 
39341     this.closable = false;
39342     this.loaded = false;
39343     this.active = false;
39344    
39345       
39346     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39347         
39348         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39349         
39350         this.wrapEl = this.el; //this.el.wrap();
39351         var ti = [];
39352         if (config.toolbar.items) {
39353             ti = config.toolbar.items ;
39354             delete config.toolbar.items ;
39355         }
39356         
39357         var nitems = [];
39358         this.toolbar.render(this.wrapEl, 'before');
39359         for(var i =0;i < ti.length;i++) {
39360           //  Roo.log(['add child', items[i]]);
39361             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39362         }
39363         this.toolbar.items = nitems;
39364         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39365         delete config.toolbar;
39366         
39367     }
39368     /*
39369     // xtype created footer. - not sure if will work as we normally have to render first..
39370     if (this.footer && !this.footer.el && this.footer.xtype) {
39371         if (!this.wrapEl) {
39372             this.wrapEl = this.el.wrap();
39373         }
39374     
39375         this.footer.container = this.wrapEl.createChild();
39376          
39377         this.footer = Roo.factory(this.footer, Roo);
39378         
39379     }
39380     */
39381     
39382      if(typeof config == "string"){
39383         this.title = config;
39384     }else{
39385         Roo.apply(this, config);
39386     }
39387     
39388     if(this.resizeEl){
39389         this.resizeEl = Roo.get(this.resizeEl, true);
39390     }else{
39391         this.resizeEl = this.el;
39392     }
39393     // handle view.xtype
39394     
39395  
39396     
39397     
39398     this.addEvents({
39399         /**
39400          * @event activate
39401          * Fires when this panel is activated. 
39402          * @param {Roo.ContentPanel} this
39403          */
39404         "activate" : true,
39405         /**
39406          * @event deactivate
39407          * Fires when this panel is activated. 
39408          * @param {Roo.ContentPanel} this
39409          */
39410         "deactivate" : true,
39411
39412         /**
39413          * @event resize
39414          * Fires when this panel is resized if fitToFrame is true.
39415          * @param {Roo.ContentPanel} this
39416          * @param {Number} width The width after any component adjustments
39417          * @param {Number} height The height after any component adjustments
39418          */
39419         "resize" : true,
39420         
39421          /**
39422          * @event render
39423          * Fires when this tab is created
39424          * @param {Roo.ContentPanel} this
39425          */
39426         "render" : true
39427         
39428         
39429         
39430     });
39431     
39432
39433     
39434     
39435     if(this.autoScroll){
39436         this.resizeEl.setStyle("overflow", "auto");
39437     } else {
39438         // fix randome scrolling
39439         //this.el.on('scroll', function() {
39440         //    Roo.log('fix random scolling');
39441         //    this.scrollTo('top',0); 
39442         //});
39443     }
39444     content = content || this.content;
39445     if(content){
39446         this.setContent(content);
39447     }
39448     if(config && config.url){
39449         this.setUrl(this.url, this.params, this.loadOnce);
39450     }
39451     
39452     
39453     
39454     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39455     
39456     if (this.view && typeof(this.view.xtype) != 'undefined') {
39457         this.view.el = this.el.appendChild(document.createElement("div"));
39458         this.view = Roo.factory(this.view); 
39459         this.view.render  &&  this.view.render(false, '');  
39460     }
39461     
39462     
39463     this.fireEvent('render', this);
39464 };
39465
39466 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39467     
39468     cls : '',
39469     background : '',
39470     
39471     tabTip : '',
39472     
39473     setRegion : function(region){
39474         this.region = region;
39475         this.setActiveClass(region && !this.background);
39476     },
39477     
39478     
39479     setActiveClass: function(state)
39480     {
39481         if(state){
39482            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39483            this.el.setStyle('position','relative');
39484         }else{
39485            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39486            this.el.setStyle('position', 'absolute');
39487         } 
39488     },
39489     
39490     /**
39491      * Returns the toolbar for this Panel if one was configured. 
39492      * @return {Roo.Toolbar} 
39493      */
39494     getToolbar : function(){
39495         return this.toolbar;
39496     },
39497     
39498     setActiveState : function(active)
39499     {
39500         this.active = active;
39501         this.setActiveClass(active);
39502         if(!active){
39503             if(this.fireEvent("deactivate", this) === false){
39504                 return false;
39505             }
39506             return true;
39507         }
39508         this.fireEvent("activate", this);
39509         return true;
39510     },
39511     /**
39512      * Updates this panel's element
39513      * @param {String} content The new content
39514      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39515     */
39516     setContent : function(content, loadScripts){
39517         this.el.update(content, loadScripts);
39518     },
39519
39520     ignoreResize : function(w, h){
39521         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39522             return true;
39523         }else{
39524             this.lastSize = {width: w, height: h};
39525             return false;
39526         }
39527     },
39528     /**
39529      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39530      * @return {Roo.UpdateManager} The UpdateManager
39531      */
39532     getUpdateManager : function(){
39533         return this.el.getUpdateManager();
39534     },
39535      /**
39536      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39537      * @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:
39538 <pre><code>
39539 panel.load({
39540     url: "your-url.php",
39541     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39542     callback: yourFunction,
39543     scope: yourObject, //(optional scope)
39544     discardUrl: false,
39545     nocache: false,
39546     text: "Loading...",
39547     timeout: 30,
39548     scripts: false
39549 });
39550 </code></pre>
39551      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39552      * 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.
39553      * @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}
39554      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39555      * @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.
39556      * @return {Roo.ContentPanel} this
39557      */
39558     load : function(){
39559         var um = this.el.getUpdateManager();
39560         um.update.apply(um, arguments);
39561         return this;
39562     },
39563
39564
39565     /**
39566      * 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.
39567      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39568      * @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)
39569      * @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)
39570      * @return {Roo.UpdateManager} The UpdateManager
39571      */
39572     setUrl : function(url, params, loadOnce){
39573         if(this.refreshDelegate){
39574             this.removeListener("activate", this.refreshDelegate);
39575         }
39576         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39577         this.on("activate", this.refreshDelegate);
39578         return this.el.getUpdateManager();
39579     },
39580     
39581     _handleRefresh : function(url, params, loadOnce){
39582         if(!loadOnce || !this.loaded){
39583             var updater = this.el.getUpdateManager();
39584             updater.update(url, params, this._setLoaded.createDelegate(this));
39585         }
39586     },
39587     
39588     _setLoaded : function(){
39589         this.loaded = true;
39590     }, 
39591     
39592     /**
39593      * Returns this panel's id
39594      * @return {String} 
39595      */
39596     getId : function(){
39597         return this.el.id;
39598     },
39599     
39600     /** 
39601      * Returns this panel's element - used by regiosn to add.
39602      * @return {Roo.Element} 
39603      */
39604     getEl : function(){
39605         return this.wrapEl || this.el;
39606     },
39607     
39608    
39609     
39610     adjustForComponents : function(width, height)
39611     {
39612         //Roo.log('adjustForComponents ');
39613         if(this.resizeEl != this.el){
39614             width -= this.el.getFrameWidth('lr');
39615             height -= this.el.getFrameWidth('tb');
39616         }
39617         if(this.toolbar){
39618             var te = this.toolbar.getEl();
39619             te.setWidth(width);
39620             height -= te.getHeight();
39621         }
39622         if(this.footer){
39623             var te = this.footer.getEl();
39624             te.setWidth(width);
39625             height -= te.getHeight();
39626         }
39627         
39628         
39629         if(this.adjustments){
39630             width += this.adjustments[0];
39631             height += this.adjustments[1];
39632         }
39633         return {"width": width, "height": height};
39634     },
39635     
39636     setSize : function(width, height){
39637         if(this.fitToFrame && !this.ignoreResize(width, height)){
39638             if(this.fitContainer && this.resizeEl != this.el){
39639                 this.el.setSize(width, height);
39640             }
39641             var size = this.adjustForComponents(width, height);
39642             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39643             this.fireEvent('resize', this, size.width, size.height);
39644         }
39645     },
39646     
39647     /**
39648      * Returns this panel's title
39649      * @return {String} 
39650      */
39651     getTitle : function(){
39652         
39653         if (typeof(this.title) != 'object') {
39654             return this.title;
39655         }
39656         
39657         var t = '';
39658         for (var k in this.title) {
39659             if (!this.title.hasOwnProperty(k)) {
39660                 continue;
39661             }
39662             
39663             if (k.indexOf('-') >= 0) {
39664                 var s = k.split('-');
39665                 for (var i = 0; i<s.length; i++) {
39666                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39667                 }
39668             } else {
39669                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39670             }
39671         }
39672         return t;
39673     },
39674     
39675     /**
39676      * Set this panel's title
39677      * @param {String} title
39678      */
39679     setTitle : function(title){
39680         this.title = title;
39681         if(this.region){
39682             this.region.updatePanelTitle(this, title);
39683         }
39684     },
39685     
39686     /**
39687      * Returns true is this panel was configured to be closable
39688      * @return {Boolean} 
39689      */
39690     isClosable : function(){
39691         return this.closable;
39692     },
39693     
39694     beforeSlide : function(){
39695         this.el.clip();
39696         this.resizeEl.clip();
39697     },
39698     
39699     afterSlide : function(){
39700         this.el.unclip();
39701         this.resizeEl.unclip();
39702     },
39703     
39704     /**
39705      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39706      *   Will fail silently if the {@link #setUrl} method has not been called.
39707      *   This does not activate the panel, just updates its content.
39708      */
39709     refresh : function(){
39710         if(this.refreshDelegate){
39711            this.loaded = false;
39712            this.refreshDelegate();
39713         }
39714     },
39715     
39716     /**
39717      * Destroys this panel
39718      */
39719     destroy : function(){
39720         this.el.removeAllListeners();
39721         var tempEl = document.createElement("span");
39722         tempEl.appendChild(this.el.dom);
39723         tempEl.innerHTML = "";
39724         this.el.remove();
39725         this.el = null;
39726     },
39727     
39728     /**
39729      * form - if the content panel contains a form - this is a reference to it.
39730      * @type {Roo.form.Form}
39731      */
39732     form : false,
39733     /**
39734      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39735      *    This contains a reference to it.
39736      * @type {Roo.View}
39737      */
39738     view : false,
39739     
39740       /**
39741      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39742      * <pre><code>
39743
39744 layout.addxtype({
39745        xtype : 'Form',
39746        items: [ .... ]
39747    }
39748 );
39749
39750 </code></pre>
39751      * @param {Object} cfg Xtype definition of item to add.
39752      */
39753     
39754     
39755     getChildContainer: function () {
39756         return this.getEl();
39757     }
39758     
39759     
39760     /*
39761         var  ret = new Roo.factory(cfg);
39762         return ret;
39763         
39764         
39765         // add form..
39766         if (cfg.xtype.match(/^Form$/)) {
39767             
39768             var el;
39769             //if (this.footer) {
39770             //    el = this.footer.container.insertSibling(false, 'before');
39771             //} else {
39772                 el = this.el.createChild();
39773             //}
39774
39775             this.form = new  Roo.form.Form(cfg);
39776             
39777             
39778             if ( this.form.allItems.length) {
39779                 this.form.render(el.dom);
39780             }
39781             return this.form;
39782         }
39783         // should only have one of theses..
39784         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39785             // views.. should not be just added - used named prop 'view''
39786             
39787             cfg.el = this.el.appendChild(document.createElement("div"));
39788             // factory?
39789             
39790             var ret = new Roo.factory(cfg);
39791              
39792              ret.render && ret.render(false, ''); // render blank..
39793             this.view = ret;
39794             return ret;
39795         }
39796         return false;
39797     }
39798     \*/
39799 });
39800  
39801 /**
39802  * @class Roo.bootstrap.panel.Grid
39803  * @extends Roo.bootstrap.panel.Content
39804  * @constructor
39805  * Create a new GridPanel.
39806  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39807  * @param {Object} config A the config object
39808   
39809  */
39810
39811
39812
39813 Roo.bootstrap.panel.Grid = function(config)
39814 {
39815     
39816       
39817     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39818         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39819
39820     config.el = this.wrapper;
39821     //this.el = this.wrapper;
39822     
39823       if (config.container) {
39824         // ctor'ed from a Border/panel.grid
39825         
39826         
39827         this.wrapper.setStyle("overflow", "hidden");
39828         this.wrapper.addClass('roo-grid-container');
39829
39830     }
39831     
39832     
39833     if(config.toolbar){
39834         var tool_el = this.wrapper.createChild();    
39835         this.toolbar = Roo.factory(config.toolbar);
39836         var ti = [];
39837         if (config.toolbar.items) {
39838             ti = config.toolbar.items ;
39839             delete config.toolbar.items ;
39840         }
39841         
39842         var nitems = [];
39843         this.toolbar.render(tool_el);
39844         for(var i =0;i < ti.length;i++) {
39845           //  Roo.log(['add child', items[i]]);
39846             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39847         }
39848         this.toolbar.items = nitems;
39849         
39850         delete config.toolbar;
39851     }
39852     
39853     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39854     config.grid.scrollBody = true;;
39855     config.grid.monitorWindowResize = false; // turn off autosizing
39856     config.grid.autoHeight = false;
39857     config.grid.autoWidth = false;
39858     
39859     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39860     
39861     if (config.background) {
39862         // render grid on panel activation (if panel background)
39863         this.on('activate', function(gp) {
39864             if (!gp.grid.rendered) {
39865                 gp.grid.render(this.wrapper);
39866                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39867             }
39868         });
39869             
39870     } else {
39871         this.grid.render(this.wrapper);
39872         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39873
39874     }
39875     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39876     // ??? needed ??? config.el = this.wrapper;
39877     
39878     
39879     
39880   
39881     // xtype created footer. - not sure if will work as we normally have to render first..
39882     if (this.footer && !this.footer.el && this.footer.xtype) {
39883         
39884         var ctr = this.grid.getView().getFooterPanel(true);
39885         this.footer.dataSource = this.grid.dataSource;
39886         this.footer = Roo.factory(this.footer, Roo);
39887         this.footer.render(ctr);
39888         
39889     }
39890     
39891     
39892     
39893     
39894      
39895 };
39896
39897 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39898     getId : function(){
39899         return this.grid.id;
39900     },
39901     
39902     /**
39903      * Returns the grid for this panel
39904      * @return {Roo.bootstrap.Table} 
39905      */
39906     getGrid : function(){
39907         return this.grid;    
39908     },
39909     
39910     setSize : function(width, height){
39911         if(!this.ignoreResize(width, height)){
39912             var grid = this.grid;
39913             var size = this.adjustForComponents(width, height);
39914             // tfoot is not a footer?
39915           
39916             
39917             var gridel = grid.getGridEl();
39918             gridel.setSize(size.width, size.height);
39919             
39920             var tbd = grid.getGridEl().select('tbody', true).first();
39921             var thd = grid.getGridEl().select('thead',true).first();
39922             var tbf= grid.getGridEl().select('tfoot', true).first();
39923
39924             if (tbf) {
39925                 size.height -= thd.getHeight();
39926             }
39927             if (thd) {
39928                 size.height -= thd.getHeight();
39929             }
39930             
39931             tbd.setSize(size.width, size.height );
39932             // this is for the account management tab -seems to work there.
39933             var thd = grid.getGridEl().select('thead',true).first();
39934             //if (tbd) {
39935             //    tbd.setSize(size.width, size.height - thd.getHeight());
39936             //}
39937              
39938             grid.autoSize();
39939         }
39940     },
39941      
39942     
39943     
39944     beforeSlide : function(){
39945         this.grid.getView().scroller.clip();
39946     },
39947     
39948     afterSlide : function(){
39949         this.grid.getView().scroller.unclip();
39950     },
39951     
39952     destroy : function(){
39953         this.grid.destroy();
39954         delete this.grid;
39955         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39956     }
39957 });
39958
39959 /**
39960  * @class Roo.bootstrap.panel.Nest
39961  * @extends Roo.bootstrap.panel.Content
39962  * @constructor
39963  * Create a new Panel, that can contain a layout.Border.
39964  * 
39965  * 
39966  * @param {Roo.BorderLayout} layout The layout for this panel
39967  * @param {String/Object} config A string to set only the title or a config object
39968  */
39969 Roo.bootstrap.panel.Nest = function(config)
39970 {
39971     // construct with only one argument..
39972     /* FIXME - implement nicer consturctors
39973     if (layout.layout) {
39974         config = layout;
39975         layout = config.layout;
39976         delete config.layout;
39977     }
39978     if (layout.xtype && !layout.getEl) {
39979         // then layout needs constructing..
39980         layout = Roo.factory(layout, Roo);
39981     }
39982     */
39983     
39984     config.el =  config.layout.getEl();
39985     
39986     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39987     
39988     config.layout.monitorWindowResize = false; // turn off autosizing
39989     this.layout = config.layout;
39990     this.layout.getEl().addClass("roo-layout-nested-layout");
39991     this.layout.parent = this;
39992     
39993     
39994     
39995     
39996 };
39997
39998 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39999
40000     setSize : function(width, height){
40001         if(!this.ignoreResize(width, height)){
40002             var size = this.adjustForComponents(width, height);
40003             var el = this.layout.getEl();
40004             if (size.height < 1) {
40005                 el.setWidth(size.width);   
40006             } else {
40007                 el.setSize(size.width, size.height);
40008             }
40009             var touch = el.dom.offsetWidth;
40010             this.layout.layout();
40011             // ie requires a double layout on the first pass
40012             if(Roo.isIE && !this.initialized){
40013                 this.initialized = true;
40014                 this.layout.layout();
40015             }
40016         }
40017     },
40018     
40019     // activate all subpanels if not currently active..
40020     
40021     setActiveState : function(active){
40022         this.active = active;
40023         this.setActiveClass(active);
40024         
40025         if(!active){
40026             this.fireEvent("deactivate", this);
40027             return;
40028         }
40029         
40030         this.fireEvent("activate", this);
40031         // not sure if this should happen before or after..
40032         if (!this.layout) {
40033             return; // should not happen..
40034         }
40035         var reg = false;
40036         for (var r in this.layout.regions) {
40037             reg = this.layout.getRegion(r);
40038             if (reg.getActivePanel()) {
40039                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40040                 reg.setActivePanel(reg.getActivePanel());
40041                 continue;
40042             }
40043             if (!reg.panels.length) {
40044                 continue;
40045             }
40046             reg.showPanel(reg.getPanel(0));
40047         }
40048         
40049         
40050         
40051         
40052     },
40053     
40054     /**
40055      * Returns the nested BorderLayout for this panel
40056      * @return {Roo.BorderLayout} 
40057      */
40058     getLayout : function(){
40059         return this.layout;
40060     },
40061     
40062      /**
40063      * Adds a xtype elements to the layout of the nested panel
40064      * <pre><code>
40065
40066 panel.addxtype({
40067        xtype : 'ContentPanel',
40068        region: 'west',
40069        items: [ .... ]
40070    }
40071 );
40072
40073 panel.addxtype({
40074         xtype : 'NestedLayoutPanel',
40075         region: 'west',
40076         layout: {
40077            center: { },
40078            west: { }   
40079         },
40080         items : [ ... list of content panels or nested layout panels.. ]
40081    }
40082 );
40083 </code></pre>
40084      * @param {Object} cfg Xtype definition of item to add.
40085      */
40086     addxtype : function(cfg) {
40087         return this.layout.addxtype(cfg);
40088     
40089     }
40090 });/*
40091  * Based on:
40092  * Ext JS Library 1.1.1
40093  * Copyright(c) 2006-2007, Ext JS, LLC.
40094  *
40095  * Originally Released Under LGPL - original licence link has changed is not relivant.
40096  *
40097  * Fork - LGPL
40098  * <script type="text/javascript">
40099  */
40100 /**
40101  * @class Roo.TabPanel
40102  * @extends Roo.util.Observable
40103  * A lightweight tab container.
40104  * <br><br>
40105  * Usage:
40106  * <pre><code>
40107 // basic tabs 1, built from existing content
40108 var tabs = new Roo.TabPanel("tabs1");
40109 tabs.addTab("script", "View Script");
40110 tabs.addTab("markup", "View Markup");
40111 tabs.activate("script");
40112
40113 // more advanced tabs, built from javascript
40114 var jtabs = new Roo.TabPanel("jtabs");
40115 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40116
40117 // set up the UpdateManager
40118 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40119 var updater = tab2.getUpdateManager();
40120 updater.setDefaultUrl("ajax1.htm");
40121 tab2.on('activate', updater.refresh, updater, true);
40122
40123 // Use setUrl for Ajax loading
40124 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40125 tab3.setUrl("ajax2.htm", null, true);
40126
40127 // Disabled tab
40128 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40129 tab4.disable();
40130
40131 jtabs.activate("jtabs-1");
40132  * </code></pre>
40133  * @constructor
40134  * Create a new TabPanel.
40135  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40136  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40137  */
40138 Roo.bootstrap.panel.Tabs = function(config){
40139     /**
40140     * The container element for this TabPanel.
40141     * @type Roo.Element
40142     */
40143     this.el = Roo.get(config.el);
40144     delete config.el;
40145     if(config){
40146         if(typeof config == "boolean"){
40147             this.tabPosition = config ? "bottom" : "top";
40148         }else{
40149             Roo.apply(this, config);
40150         }
40151     }
40152     
40153     if(this.tabPosition == "bottom"){
40154         // if tabs are at the bottom = create the body first.
40155         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40156         this.el.addClass("roo-tabs-bottom");
40157     }
40158     // next create the tabs holders
40159     
40160     if (this.tabPosition == "west"){
40161         
40162         var reg = this.region; // fake it..
40163         while (reg) {
40164             if (!reg.mgr.parent) {
40165                 break;
40166             }
40167             reg = reg.mgr.parent.region;
40168         }
40169         Roo.log("got nest?");
40170         Roo.log(reg);
40171         if (reg.mgr.getRegion('west')) {
40172             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40173             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40174             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40175             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40176             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40177         
40178             
40179         }
40180         
40181         
40182     } else {
40183      
40184         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40185         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40186         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40187         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40188     }
40189     
40190     
40191     if(Roo.isIE){
40192         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40193     }
40194     
40195     // finally - if tabs are at the top, then create the body last..
40196     if(this.tabPosition != "bottom"){
40197         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40198          * @type Roo.Element
40199          */
40200         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40201         this.el.addClass("roo-tabs-top");
40202     }
40203     this.items = [];
40204
40205     this.bodyEl.setStyle("position", "relative");
40206
40207     this.active = null;
40208     this.activateDelegate = this.activate.createDelegate(this);
40209
40210     this.addEvents({
40211         /**
40212          * @event tabchange
40213          * Fires when the active tab changes
40214          * @param {Roo.TabPanel} this
40215          * @param {Roo.TabPanelItem} activePanel The new active tab
40216          */
40217         "tabchange": true,
40218         /**
40219          * @event beforetabchange
40220          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40221          * @param {Roo.TabPanel} this
40222          * @param {Object} e Set cancel to true on this object to cancel the tab change
40223          * @param {Roo.TabPanelItem} tab The tab being changed to
40224          */
40225         "beforetabchange" : true
40226     });
40227
40228     Roo.EventManager.onWindowResize(this.onResize, this);
40229     this.cpad = this.el.getPadding("lr");
40230     this.hiddenCount = 0;
40231
40232
40233     // toolbar on the tabbar support...
40234     if (this.toolbar) {
40235         alert("no toolbar support yet");
40236         this.toolbar  = false;
40237         /*
40238         var tcfg = this.toolbar;
40239         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40240         this.toolbar = new Roo.Toolbar(tcfg);
40241         if (Roo.isSafari) {
40242             var tbl = tcfg.container.child('table', true);
40243             tbl.setAttribute('width', '100%');
40244         }
40245         */
40246         
40247     }
40248    
40249
40250
40251     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40252 };
40253
40254 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40255     /*
40256      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40257      */
40258     tabPosition : "top",
40259     /*
40260      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40261      */
40262     currentTabWidth : 0,
40263     /*
40264      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40265      */
40266     minTabWidth : 40,
40267     /*
40268      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40269      */
40270     maxTabWidth : 250,
40271     /*
40272      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40273      */
40274     preferredTabWidth : 175,
40275     /*
40276      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40277      */
40278     resizeTabs : false,
40279     /*
40280      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40281      */
40282     monitorResize : true,
40283     /*
40284      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40285      */
40286     toolbar : false,  // set by caller..
40287     
40288     region : false, /// set by caller
40289     
40290     disableTooltips : true, // not used yet...
40291
40292     /**
40293      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40294      * @param {String} id The id of the div to use <b>or create</b>
40295      * @param {String} text The text for the tab
40296      * @param {String} content (optional) Content to put in the TabPanelItem body
40297      * @param {Boolean} closable (optional) True to create a close icon on the tab
40298      * @return {Roo.TabPanelItem} The created TabPanelItem
40299      */
40300     addTab : function(id, text, content, closable, tpl)
40301     {
40302         var item = new Roo.bootstrap.panel.TabItem({
40303             panel: this,
40304             id : id,
40305             text : text,
40306             closable : closable,
40307             tpl : tpl
40308         });
40309         this.addTabItem(item);
40310         if(content){
40311             item.setContent(content);
40312         }
40313         return item;
40314     },
40315
40316     /**
40317      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40318      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40319      * @return {Roo.TabPanelItem}
40320      */
40321     getTab : function(id){
40322         return this.items[id];
40323     },
40324
40325     /**
40326      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40327      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40328      */
40329     hideTab : function(id){
40330         var t = this.items[id];
40331         if(!t.isHidden()){
40332            t.setHidden(true);
40333            this.hiddenCount++;
40334            this.autoSizeTabs();
40335         }
40336     },
40337
40338     /**
40339      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40340      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40341      */
40342     unhideTab : function(id){
40343         var t = this.items[id];
40344         if(t.isHidden()){
40345            t.setHidden(false);
40346            this.hiddenCount--;
40347            this.autoSizeTabs();
40348         }
40349     },
40350
40351     /**
40352      * Adds an existing {@link Roo.TabPanelItem}.
40353      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40354      */
40355     addTabItem : function(item)
40356     {
40357         this.items[item.id] = item;
40358         this.items.push(item);
40359         this.autoSizeTabs();
40360       //  if(this.resizeTabs){
40361     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40362   //         this.autoSizeTabs();
40363 //        }else{
40364 //            item.autoSize();
40365        // }
40366     },
40367
40368     /**
40369      * Removes a {@link Roo.TabPanelItem}.
40370      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40371      */
40372     removeTab : function(id){
40373         var items = this.items;
40374         var tab = items[id];
40375         if(!tab) { return; }
40376         var index = items.indexOf(tab);
40377         if(this.active == tab && items.length > 1){
40378             var newTab = this.getNextAvailable(index);
40379             if(newTab) {
40380                 newTab.activate();
40381             }
40382         }
40383         this.stripEl.dom.removeChild(tab.pnode.dom);
40384         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40385             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40386         }
40387         items.splice(index, 1);
40388         delete this.items[tab.id];
40389         tab.fireEvent("close", tab);
40390         tab.purgeListeners();
40391         this.autoSizeTabs();
40392     },
40393
40394     getNextAvailable : function(start){
40395         var items = this.items;
40396         var index = start;
40397         // look for a next tab that will slide over to
40398         // replace the one being removed
40399         while(index < items.length){
40400             var item = items[++index];
40401             if(item && !item.isHidden()){
40402                 return item;
40403             }
40404         }
40405         // if one isn't found select the previous tab (on the left)
40406         index = start;
40407         while(index >= 0){
40408             var item = items[--index];
40409             if(item && !item.isHidden()){
40410                 return item;
40411             }
40412         }
40413         return null;
40414     },
40415
40416     /**
40417      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40418      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40419      */
40420     disableTab : function(id){
40421         var tab = this.items[id];
40422         if(tab && this.active != tab){
40423             tab.disable();
40424         }
40425     },
40426
40427     /**
40428      * Enables a {@link Roo.TabPanelItem} that is disabled.
40429      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40430      */
40431     enableTab : function(id){
40432         var tab = this.items[id];
40433         tab.enable();
40434     },
40435
40436     /**
40437      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40438      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40439      * @return {Roo.TabPanelItem} The TabPanelItem.
40440      */
40441     activate : function(id)
40442     {
40443         //Roo.log('activite:'  + id);
40444         
40445         var tab = this.items[id];
40446         if(!tab){
40447             return null;
40448         }
40449         if(tab == this.active || tab.disabled){
40450             return tab;
40451         }
40452         var e = {};
40453         this.fireEvent("beforetabchange", this, e, tab);
40454         if(e.cancel !== true && !tab.disabled){
40455             if(this.active){
40456                 this.active.hide();
40457             }
40458             this.active = this.items[id];
40459             this.active.show();
40460             this.fireEvent("tabchange", this, this.active);
40461         }
40462         return tab;
40463     },
40464
40465     /**
40466      * Gets the active {@link Roo.TabPanelItem}.
40467      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40468      */
40469     getActiveTab : function(){
40470         return this.active;
40471     },
40472
40473     /**
40474      * Updates the tab body element to fit the height of the container element
40475      * for overflow scrolling
40476      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40477      */
40478     syncHeight : function(targetHeight){
40479         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40480         var bm = this.bodyEl.getMargins();
40481         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40482         this.bodyEl.setHeight(newHeight);
40483         return newHeight;
40484     },
40485
40486     onResize : function(){
40487         if(this.monitorResize){
40488             this.autoSizeTabs();
40489         }
40490     },
40491
40492     /**
40493      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40494      */
40495     beginUpdate : function(){
40496         this.updating = true;
40497     },
40498
40499     /**
40500      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40501      */
40502     endUpdate : function(){
40503         this.updating = false;
40504         this.autoSizeTabs();
40505     },
40506
40507     /**
40508      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40509      */
40510     autoSizeTabs : function()
40511     {
40512         var count = this.items.length;
40513         var vcount = count - this.hiddenCount;
40514         
40515         if (vcount < 2) {
40516             this.stripEl.hide();
40517         } else {
40518             this.stripEl.show();
40519         }
40520         
40521         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40522             return;
40523         }
40524         
40525         
40526         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40527         var availWidth = Math.floor(w / vcount);
40528         var b = this.stripBody;
40529         if(b.getWidth() > w){
40530             var tabs = this.items;
40531             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40532             if(availWidth < this.minTabWidth){
40533                 /*if(!this.sleft){    // incomplete scrolling code
40534                     this.createScrollButtons();
40535                 }
40536                 this.showScroll();
40537                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40538             }
40539         }else{
40540             if(this.currentTabWidth < this.preferredTabWidth){
40541                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40542             }
40543         }
40544     },
40545
40546     /**
40547      * Returns the number of tabs in this TabPanel.
40548      * @return {Number}
40549      */
40550      getCount : function(){
40551          return this.items.length;
40552      },
40553
40554     /**
40555      * Resizes all the tabs to the passed width
40556      * @param {Number} The new width
40557      */
40558     setTabWidth : function(width){
40559         this.currentTabWidth = width;
40560         for(var i = 0, len = this.items.length; i < len; i++) {
40561                 if(!this.items[i].isHidden()) {
40562                 this.items[i].setWidth(width);
40563             }
40564         }
40565     },
40566
40567     /**
40568      * Destroys this TabPanel
40569      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40570      */
40571     destroy : function(removeEl){
40572         Roo.EventManager.removeResizeListener(this.onResize, this);
40573         for(var i = 0, len = this.items.length; i < len; i++){
40574             this.items[i].purgeListeners();
40575         }
40576         if(removeEl === true){
40577             this.el.update("");
40578             this.el.remove();
40579         }
40580     },
40581     
40582     createStrip : function(container)
40583     {
40584         var strip = document.createElement("nav");
40585         strip.className = Roo.bootstrap.version == 4 ?
40586             "navbar-light bg-light" : 
40587             "navbar navbar-default"; //"x-tabs-wrap";
40588         container.appendChild(strip);
40589         return strip;
40590     },
40591     
40592     createStripList : function(strip)
40593     {
40594         // div wrapper for retard IE
40595         // returns the "tr" element.
40596         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40597         //'<div class="x-tabs-strip-wrap">'+
40598           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40599           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40600         return strip.firstChild; //.firstChild.firstChild.firstChild;
40601     },
40602     createBody : function(container)
40603     {
40604         var body = document.createElement("div");
40605         Roo.id(body, "tab-body");
40606         //Roo.fly(body).addClass("x-tabs-body");
40607         Roo.fly(body).addClass("tab-content");
40608         container.appendChild(body);
40609         return body;
40610     },
40611     createItemBody :function(bodyEl, id){
40612         var body = Roo.getDom(id);
40613         if(!body){
40614             body = document.createElement("div");
40615             body.id = id;
40616         }
40617         //Roo.fly(body).addClass("x-tabs-item-body");
40618         Roo.fly(body).addClass("tab-pane");
40619          bodyEl.insertBefore(body, bodyEl.firstChild);
40620         return body;
40621     },
40622     /** @private */
40623     createStripElements :  function(stripEl, text, closable, tpl)
40624     {
40625         var td = document.createElement("li"); // was td..
40626         td.className = 'nav-item';
40627         
40628         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40629         
40630         
40631         stripEl.appendChild(td);
40632         /*if(closable){
40633             td.className = "x-tabs-closable";
40634             if(!this.closeTpl){
40635                 this.closeTpl = new Roo.Template(
40636                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40637                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40638                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40639                 );
40640             }
40641             var el = this.closeTpl.overwrite(td, {"text": text});
40642             var close = el.getElementsByTagName("div")[0];
40643             var inner = el.getElementsByTagName("em")[0];
40644             return {"el": el, "close": close, "inner": inner};
40645         } else {
40646         */
40647         // not sure what this is..
40648 //            if(!this.tabTpl){
40649                 //this.tabTpl = new Roo.Template(
40650                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40651                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40652                 //);
40653 //                this.tabTpl = new Roo.Template(
40654 //                   '<a href="#">' +
40655 //                   '<span unselectable="on"' +
40656 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40657 //                            ' >{text}</span></a>'
40658 //                );
40659 //                
40660 //            }
40661
40662
40663             var template = tpl || this.tabTpl || false;
40664             
40665             if(!template){
40666                 template =  new Roo.Template(
40667                         Roo.bootstrap.version == 4 ? 
40668                             (
40669                                 '<a class="nav-link" href="#" unselectable="on"' +
40670                                      (this.disableTooltips ? '' : ' title="{text}"') +
40671                                      ' >{text}</a>'
40672                             ) : (
40673                                 '<a class="nav-link" href="#">' +
40674                                 '<span unselectable="on"' +
40675                                          (this.disableTooltips ? '' : ' title="{text}"') +
40676                                     ' >{text}</span></a>'
40677                             )
40678                 );
40679             }
40680             
40681             switch (typeof(template)) {
40682                 case 'object' :
40683                     break;
40684                 case 'string' :
40685                     template = new Roo.Template(template);
40686                     break;
40687                 default :
40688                     break;
40689             }
40690             
40691             var el = template.overwrite(td, {"text": text});
40692             
40693             var inner = el.getElementsByTagName("span")[0];
40694             
40695             return {"el": el, "inner": inner};
40696             
40697     }
40698         
40699     
40700 });
40701
40702 /**
40703  * @class Roo.TabPanelItem
40704  * @extends Roo.util.Observable
40705  * Represents an individual item (tab plus body) in a TabPanel.
40706  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40707  * @param {String} id The id of this TabPanelItem
40708  * @param {String} text The text for the tab of this TabPanelItem
40709  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40710  */
40711 Roo.bootstrap.panel.TabItem = function(config){
40712     /**
40713      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40714      * @type Roo.TabPanel
40715      */
40716     this.tabPanel = config.panel;
40717     /**
40718      * The id for this TabPanelItem
40719      * @type String
40720      */
40721     this.id = config.id;
40722     /** @private */
40723     this.disabled = false;
40724     /** @private */
40725     this.text = config.text;
40726     /** @private */
40727     this.loaded = false;
40728     this.closable = config.closable;
40729
40730     /**
40731      * The body element for this TabPanelItem.
40732      * @type Roo.Element
40733      */
40734     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40735     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40736     this.bodyEl.setStyle("display", "block");
40737     this.bodyEl.setStyle("zoom", "1");
40738     //this.hideAction();
40739
40740     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40741     /** @private */
40742     this.el = Roo.get(els.el);
40743     this.inner = Roo.get(els.inner, true);
40744      this.textEl = Roo.bootstrap.version == 4 ?
40745         this.el : Roo.get(this.el.dom.firstChild, true);
40746
40747     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40748     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40749
40750     
40751 //    this.el.on("mousedown", this.onTabMouseDown, this);
40752     this.el.on("click", this.onTabClick, this);
40753     /** @private */
40754     if(config.closable){
40755         var c = Roo.get(els.close, true);
40756         c.dom.title = this.closeText;
40757         c.addClassOnOver("close-over");
40758         c.on("click", this.closeClick, this);
40759      }
40760
40761     this.addEvents({
40762          /**
40763          * @event activate
40764          * Fires when this tab becomes the active tab.
40765          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40766          * @param {Roo.TabPanelItem} this
40767          */
40768         "activate": true,
40769         /**
40770          * @event beforeclose
40771          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40772          * @param {Roo.TabPanelItem} this
40773          * @param {Object} e Set cancel to true on this object to cancel the close.
40774          */
40775         "beforeclose": true,
40776         /**
40777          * @event close
40778          * Fires when this tab is closed.
40779          * @param {Roo.TabPanelItem} this
40780          */
40781          "close": true,
40782         /**
40783          * @event deactivate
40784          * Fires when this tab is no longer the active tab.
40785          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40786          * @param {Roo.TabPanelItem} this
40787          */
40788          "deactivate" : true
40789     });
40790     this.hidden = false;
40791
40792     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40793 };
40794
40795 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40796            {
40797     purgeListeners : function(){
40798        Roo.util.Observable.prototype.purgeListeners.call(this);
40799        this.el.removeAllListeners();
40800     },
40801     /**
40802      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40803      */
40804     show : function(){
40805         this.status_node.addClass("active");
40806         this.showAction();
40807         if(Roo.isOpera){
40808             this.tabPanel.stripWrap.repaint();
40809         }
40810         this.fireEvent("activate", this.tabPanel, this);
40811     },
40812
40813     /**
40814      * Returns true if this tab is the active tab.
40815      * @return {Boolean}
40816      */
40817     isActive : function(){
40818         return this.tabPanel.getActiveTab() == this;
40819     },
40820
40821     /**
40822      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40823      */
40824     hide : function(){
40825         this.status_node.removeClass("active");
40826         this.hideAction();
40827         this.fireEvent("deactivate", this.tabPanel, this);
40828     },
40829
40830     hideAction : function(){
40831         this.bodyEl.hide();
40832         this.bodyEl.setStyle("position", "absolute");
40833         this.bodyEl.setLeft("-20000px");
40834         this.bodyEl.setTop("-20000px");
40835     },
40836
40837     showAction : function(){
40838         this.bodyEl.setStyle("position", "relative");
40839         this.bodyEl.setTop("");
40840         this.bodyEl.setLeft("");
40841         this.bodyEl.show();
40842     },
40843
40844     /**
40845      * Set the tooltip for the tab.
40846      * @param {String} tooltip The tab's tooltip
40847      */
40848     setTooltip : function(text){
40849         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40850             this.textEl.dom.qtip = text;
40851             this.textEl.dom.removeAttribute('title');
40852         }else{
40853             this.textEl.dom.title = text;
40854         }
40855     },
40856
40857     onTabClick : function(e){
40858         e.preventDefault();
40859         this.tabPanel.activate(this.id);
40860     },
40861
40862     onTabMouseDown : function(e){
40863         e.preventDefault();
40864         this.tabPanel.activate(this.id);
40865     },
40866 /*
40867     getWidth : function(){
40868         return this.inner.getWidth();
40869     },
40870
40871     setWidth : function(width){
40872         var iwidth = width - this.linode.getPadding("lr");
40873         this.inner.setWidth(iwidth);
40874         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40875         this.linode.setWidth(width);
40876     },
40877 */
40878     /**
40879      * Show or hide the tab
40880      * @param {Boolean} hidden True to hide or false to show.
40881      */
40882     setHidden : function(hidden){
40883         this.hidden = hidden;
40884         this.linode.setStyle("display", hidden ? "none" : "");
40885     },
40886
40887     /**
40888      * Returns true if this tab is "hidden"
40889      * @return {Boolean}
40890      */
40891     isHidden : function(){
40892         return this.hidden;
40893     },
40894
40895     /**
40896      * Returns the text for this tab
40897      * @return {String}
40898      */
40899     getText : function(){
40900         return this.text;
40901     },
40902     /*
40903     autoSize : function(){
40904         //this.el.beginMeasure();
40905         this.textEl.setWidth(1);
40906         /*
40907          *  #2804 [new] Tabs in Roojs
40908          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40909          */
40910         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40911         //this.el.endMeasure();
40912     //},
40913
40914     /**
40915      * Sets the text for the tab (Note: this also sets the tooltip text)
40916      * @param {String} text The tab's text and tooltip
40917      */
40918     setText : function(text){
40919         this.text = text;
40920         this.textEl.update(text);
40921         this.setTooltip(text);
40922         //if(!this.tabPanel.resizeTabs){
40923         //    this.autoSize();
40924         //}
40925     },
40926     /**
40927      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40928      */
40929     activate : function(){
40930         this.tabPanel.activate(this.id);
40931     },
40932
40933     /**
40934      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40935      */
40936     disable : function(){
40937         if(this.tabPanel.active != this){
40938             this.disabled = true;
40939             this.status_node.addClass("disabled");
40940         }
40941     },
40942
40943     /**
40944      * Enables this TabPanelItem if it was previously disabled.
40945      */
40946     enable : function(){
40947         this.disabled = false;
40948         this.status_node.removeClass("disabled");
40949     },
40950
40951     /**
40952      * Sets the content for this TabPanelItem.
40953      * @param {String} content The content
40954      * @param {Boolean} loadScripts true to look for and load scripts
40955      */
40956     setContent : function(content, loadScripts){
40957         this.bodyEl.update(content, loadScripts);
40958     },
40959
40960     /**
40961      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40962      * @return {Roo.UpdateManager} The UpdateManager
40963      */
40964     getUpdateManager : function(){
40965         return this.bodyEl.getUpdateManager();
40966     },
40967
40968     /**
40969      * Set a URL to be used to load the content for this TabPanelItem.
40970      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40971      * @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)
40972      * @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)
40973      * @return {Roo.UpdateManager} The UpdateManager
40974      */
40975     setUrl : function(url, params, loadOnce){
40976         if(this.refreshDelegate){
40977             this.un('activate', this.refreshDelegate);
40978         }
40979         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40980         this.on("activate", this.refreshDelegate);
40981         return this.bodyEl.getUpdateManager();
40982     },
40983
40984     /** @private */
40985     _handleRefresh : function(url, params, loadOnce){
40986         if(!loadOnce || !this.loaded){
40987             var updater = this.bodyEl.getUpdateManager();
40988             updater.update(url, params, this._setLoaded.createDelegate(this));
40989         }
40990     },
40991
40992     /**
40993      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40994      *   Will fail silently if the setUrl method has not been called.
40995      *   This does not activate the panel, just updates its content.
40996      */
40997     refresh : function(){
40998         if(this.refreshDelegate){
40999            this.loaded = false;
41000            this.refreshDelegate();
41001         }
41002     },
41003
41004     /** @private */
41005     _setLoaded : function(){
41006         this.loaded = true;
41007     },
41008
41009     /** @private */
41010     closeClick : function(e){
41011         var o = {};
41012         e.stopEvent();
41013         this.fireEvent("beforeclose", this, o);
41014         if(o.cancel !== true){
41015             this.tabPanel.removeTab(this.id);
41016         }
41017     },
41018     /**
41019      * The text displayed in the tooltip for the close icon.
41020      * @type String
41021      */
41022     closeText : "Close this tab"
41023 });
41024 /**
41025 *    This script refer to:
41026 *    Title: International Telephone Input
41027 *    Author: Jack O'Connor
41028 *    Code version:  v12.1.12
41029 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41030 **/
41031
41032 Roo.bootstrap.PhoneInputData = function() {
41033     var d = [
41034       [
41035         "Afghanistan (‫افغانستان‬‎)",
41036         "af",
41037         "93"
41038       ],
41039       [
41040         "Albania (Shqipëri)",
41041         "al",
41042         "355"
41043       ],
41044       [
41045         "Algeria (‫الجزائر‬‎)",
41046         "dz",
41047         "213"
41048       ],
41049       [
41050         "American Samoa",
41051         "as",
41052         "1684"
41053       ],
41054       [
41055         "Andorra",
41056         "ad",
41057         "376"
41058       ],
41059       [
41060         "Angola",
41061         "ao",
41062         "244"
41063       ],
41064       [
41065         "Anguilla",
41066         "ai",
41067         "1264"
41068       ],
41069       [
41070         "Antigua and Barbuda",
41071         "ag",
41072         "1268"
41073       ],
41074       [
41075         "Argentina",
41076         "ar",
41077         "54"
41078       ],
41079       [
41080         "Armenia (Հայաստան)",
41081         "am",
41082         "374"
41083       ],
41084       [
41085         "Aruba",
41086         "aw",
41087         "297"
41088       ],
41089       [
41090         "Australia",
41091         "au",
41092         "61",
41093         0
41094       ],
41095       [
41096         "Austria (Österreich)",
41097         "at",
41098         "43"
41099       ],
41100       [
41101         "Azerbaijan (Azərbaycan)",
41102         "az",
41103         "994"
41104       ],
41105       [
41106         "Bahamas",
41107         "bs",
41108         "1242"
41109       ],
41110       [
41111         "Bahrain (‫البحرين‬‎)",
41112         "bh",
41113         "973"
41114       ],
41115       [
41116         "Bangladesh (বাংলাদেশ)",
41117         "bd",
41118         "880"
41119       ],
41120       [
41121         "Barbados",
41122         "bb",
41123         "1246"
41124       ],
41125       [
41126         "Belarus (Беларусь)",
41127         "by",
41128         "375"
41129       ],
41130       [
41131         "Belgium (België)",
41132         "be",
41133         "32"
41134       ],
41135       [
41136         "Belize",
41137         "bz",
41138         "501"
41139       ],
41140       [
41141         "Benin (Bénin)",
41142         "bj",
41143         "229"
41144       ],
41145       [
41146         "Bermuda",
41147         "bm",
41148         "1441"
41149       ],
41150       [
41151         "Bhutan (འབྲུག)",
41152         "bt",
41153         "975"
41154       ],
41155       [
41156         "Bolivia",
41157         "bo",
41158         "591"
41159       ],
41160       [
41161         "Bosnia and Herzegovina (Босна и Херцеговина)",
41162         "ba",
41163         "387"
41164       ],
41165       [
41166         "Botswana",
41167         "bw",
41168         "267"
41169       ],
41170       [
41171         "Brazil (Brasil)",
41172         "br",
41173         "55"
41174       ],
41175       [
41176         "British Indian Ocean Territory",
41177         "io",
41178         "246"
41179       ],
41180       [
41181         "British Virgin Islands",
41182         "vg",
41183         "1284"
41184       ],
41185       [
41186         "Brunei",
41187         "bn",
41188         "673"
41189       ],
41190       [
41191         "Bulgaria (България)",
41192         "bg",
41193         "359"
41194       ],
41195       [
41196         "Burkina Faso",
41197         "bf",
41198         "226"
41199       ],
41200       [
41201         "Burundi (Uburundi)",
41202         "bi",
41203         "257"
41204       ],
41205       [
41206         "Cambodia (កម្ពុជា)",
41207         "kh",
41208         "855"
41209       ],
41210       [
41211         "Cameroon (Cameroun)",
41212         "cm",
41213         "237"
41214       ],
41215       [
41216         "Canada",
41217         "ca",
41218         "1",
41219         1,
41220         ["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"]
41221       ],
41222       [
41223         "Cape Verde (Kabu Verdi)",
41224         "cv",
41225         "238"
41226       ],
41227       [
41228         "Caribbean Netherlands",
41229         "bq",
41230         "599",
41231         1
41232       ],
41233       [
41234         "Cayman Islands",
41235         "ky",
41236         "1345"
41237       ],
41238       [
41239         "Central African Republic (République centrafricaine)",
41240         "cf",
41241         "236"
41242       ],
41243       [
41244         "Chad (Tchad)",
41245         "td",
41246         "235"
41247       ],
41248       [
41249         "Chile",
41250         "cl",
41251         "56"
41252       ],
41253       [
41254         "China (中国)",
41255         "cn",
41256         "86"
41257       ],
41258       [
41259         "Christmas Island",
41260         "cx",
41261         "61",
41262         2
41263       ],
41264       [
41265         "Cocos (Keeling) Islands",
41266         "cc",
41267         "61",
41268         1
41269       ],
41270       [
41271         "Colombia",
41272         "co",
41273         "57"
41274       ],
41275       [
41276         "Comoros (‫جزر القمر‬‎)",
41277         "km",
41278         "269"
41279       ],
41280       [
41281         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41282         "cd",
41283         "243"
41284       ],
41285       [
41286         "Congo (Republic) (Congo-Brazzaville)",
41287         "cg",
41288         "242"
41289       ],
41290       [
41291         "Cook Islands",
41292         "ck",
41293         "682"
41294       ],
41295       [
41296         "Costa Rica",
41297         "cr",
41298         "506"
41299       ],
41300       [
41301         "Côte d’Ivoire",
41302         "ci",
41303         "225"
41304       ],
41305       [
41306         "Croatia (Hrvatska)",
41307         "hr",
41308         "385"
41309       ],
41310       [
41311         "Cuba",
41312         "cu",
41313         "53"
41314       ],
41315       [
41316         "Curaçao",
41317         "cw",
41318         "599",
41319         0
41320       ],
41321       [
41322         "Cyprus (Κύπρος)",
41323         "cy",
41324         "357"
41325       ],
41326       [
41327         "Czech Republic (Česká republika)",
41328         "cz",
41329         "420"
41330       ],
41331       [
41332         "Denmark (Danmark)",
41333         "dk",
41334         "45"
41335       ],
41336       [
41337         "Djibouti",
41338         "dj",
41339         "253"
41340       ],
41341       [
41342         "Dominica",
41343         "dm",
41344         "1767"
41345       ],
41346       [
41347         "Dominican Republic (República Dominicana)",
41348         "do",
41349         "1",
41350         2,
41351         ["809", "829", "849"]
41352       ],
41353       [
41354         "Ecuador",
41355         "ec",
41356         "593"
41357       ],
41358       [
41359         "Egypt (‫مصر‬‎)",
41360         "eg",
41361         "20"
41362       ],
41363       [
41364         "El Salvador",
41365         "sv",
41366         "503"
41367       ],
41368       [
41369         "Equatorial Guinea (Guinea Ecuatorial)",
41370         "gq",
41371         "240"
41372       ],
41373       [
41374         "Eritrea",
41375         "er",
41376         "291"
41377       ],
41378       [
41379         "Estonia (Eesti)",
41380         "ee",
41381         "372"
41382       ],
41383       [
41384         "Ethiopia",
41385         "et",
41386         "251"
41387       ],
41388       [
41389         "Falkland Islands (Islas Malvinas)",
41390         "fk",
41391         "500"
41392       ],
41393       [
41394         "Faroe Islands (Føroyar)",
41395         "fo",
41396         "298"
41397       ],
41398       [
41399         "Fiji",
41400         "fj",
41401         "679"
41402       ],
41403       [
41404         "Finland (Suomi)",
41405         "fi",
41406         "358",
41407         0
41408       ],
41409       [
41410         "France",
41411         "fr",
41412         "33"
41413       ],
41414       [
41415         "French Guiana (Guyane française)",
41416         "gf",
41417         "594"
41418       ],
41419       [
41420         "French Polynesia (Polynésie française)",
41421         "pf",
41422         "689"
41423       ],
41424       [
41425         "Gabon",
41426         "ga",
41427         "241"
41428       ],
41429       [
41430         "Gambia",
41431         "gm",
41432         "220"
41433       ],
41434       [
41435         "Georgia (საქართველო)",
41436         "ge",
41437         "995"
41438       ],
41439       [
41440         "Germany (Deutschland)",
41441         "de",
41442         "49"
41443       ],
41444       [
41445         "Ghana (Gaana)",
41446         "gh",
41447         "233"
41448       ],
41449       [
41450         "Gibraltar",
41451         "gi",
41452         "350"
41453       ],
41454       [
41455         "Greece (Ελλάδα)",
41456         "gr",
41457         "30"
41458       ],
41459       [
41460         "Greenland (Kalaallit Nunaat)",
41461         "gl",
41462         "299"
41463       ],
41464       [
41465         "Grenada",
41466         "gd",
41467         "1473"
41468       ],
41469       [
41470         "Guadeloupe",
41471         "gp",
41472         "590",
41473         0
41474       ],
41475       [
41476         "Guam",
41477         "gu",
41478         "1671"
41479       ],
41480       [
41481         "Guatemala",
41482         "gt",
41483         "502"
41484       ],
41485       [
41486         "Guernsey",
41487         "gg",
41488         "44",
41489         1
41490       ],
41491       [
41492         "Guinea (Guinée)",
41493         "gn",
41494         "224"
41495       ],
41496       [
41497         "Guinea-Bissau (Guiné Bissau)",
41498         "gw",
41499         "245"
41500       ],
41501       [
41502         "Guyana",
41503         "gy",
41504         "592"
41505       ],
41506       [
41507         "Haiti",
41508         "ht",
41509         "509"
41510       ],
41511       [
41512         "Honduras",
41513         "hn",
41514         "504"
41515       ],
41516       [
41517         "Hong Kong (香港)",
41518         "hk",
41519         "852"
41520       ],
41521       [
41522         "Hungary (Magyarország)",
41523         "hu",
41524         "36"
41525       ],
41526       [
41527         "Iceland (Ísland)",
41528         "is",
41529         "354"
41530       ],
41531       [
41532         "India (भारत)",
41533         "in",
41534         "91"
41535       ],
41536       [
41537         "Indonesia",
41538         "id",
41539         "62"
41540       ],
41541       [
41542         "Iran (‫ایران‬‎)",
41543         "ir",
41544         "98"
41545       ],
41546       [
41547         "Iraq (‫العراق‬‎)",
41548         "iq",
41549         "964"
41550       ],
41551       [
41552         "Ireland",
41553         "ie",
41554         "353"
41555       ],
41556       [
41557         "Isle of Man",
41558         "im",
41559         "44",
41560         2
41561       ],
41562       [
41563         "Israel (‫ישראל‬‎)",
41564         "il",
41565         "972"
41566       ],
41567       [
41568         "Italy (Italia)",
41569         "it",
41570         "39",
41571         0
41572       ],
41573       [
41574         "Jamaica",
41575         "jm",
41576         "1876"
41577       ],
41578       [
41579         "Japan (日本)",
41580         "jp",
41581         "81"
41582       ],
41583       [
41584         "Jersey",
41585         "je",
41586         "44",
41587         3
41588       ],
41589       [
41590         "Jordan (‫الأردن‬‎)",
41591         "jo",
41592         "962"
41593       ],
41594       [
41595         "Kazakhstan (Казахстан)",
41596         "kz",
41597         "7",
41598         1
41599       ],
41600       [
41601         "Kenya",
41602         "ke",
41603         "254"
41604       ],
41605       [
41606         "Kiribati",
41607         "ki",
41608         "686"
41609       ],
41610       [
41611         "Kosovo",
41612         "xk",
41613         "383"
41614       ],
41615       [
41616         "Kuwait (‫الكويت‬‎)",
41617         "kw",
41618         "965"
41619       ],
41620       [
41621         "Kyrgyzstan (Кыргызстан)",
41622         "kg",
41623         "996"
41624       ],
41625       [
41626         "Laos (ລາວ)",
41627         "la",
41628         "856"
41629       ],
41630       [
41631         "Latvia (Latvija)",
41632         "lv",
41633         "371"
41634       ],
41635       [
41636         "Lebanon (‫لبنان‬‎)",
41637         "lb",
41638         "961"
41639       ],
41640       [
41641         "Lesotho",
41642         "ls",
41643         "266"
41644       ],
41645       [
41646         "Liberia",
41647         "lr",
41648         "231"
41649       ],
41650       [
41651         "Libya (‫ليبيا‬‎)",
41652         "ly",
41653         "218"
41654       ],
41655       [
41656         "Liechtenstein",
41657         "li",
41658         "423"
41659       ],
41660       [
41661         "Lithuania (Lietuva)",
41662         "lt",
41663         "370"
41664       ],
41665       [
41666         "Luxembourg",
41667         "lu",
41668         "352"
41669       ],
41670       [
41671         "Macau (澳門)",
41672         "mo",
41673         "853"
41674       ],
41675       [
41676         "Macedonia (FYROM) (Македонија)",
41677         "mk",
41678         "389"
41679       ],
41680       [
41681         "Madagascar (Madagasikara)",
41682         "mg",
41683         "261"
41684       ],
41685       [
41686         "Malawi",
41687         "mw",
41688         "265"
41689       ],
41690       [
41691         "Malaysia",
41692         "my",
41693         "60"
41694       ],
41695       [
41696         "Maldives",
41697         "mv",
41698         "960"
41699       ],
41700       [
41701         "Mali",
41702         "ml",
41703         "223"
41704       ],
41705       [
41706         "Malta",
41707         "mt",
41708         "356"
41709       ],
41710       [
41711         "Marshall Islands",
41712         "mh",
41713         "692"
41714       ],
41715       [
41716         "Martinique",
41717         "mq",
41718         "596"
41719       ],
41720       [
41721         "Mauritania (‫موريتانيا‬‎)",
41722         "mr",
41723         "222"
41724       ],
41725       [
41726         "Mauritius (Moris)",
41727         "mu",
41728         "230"
41729       ],
41730       [
41731         "Mayotte",
41732         "yt",
41733         "262",
41734         1
41735       ],
41736       [
41737         "Mexico (México)",
41738         "mx",
41739         "52"
41740       ],
41741       [
41742         "Micronesia",
41743         "fm",
41744         "691"
41745       ],
41746       [
41747         "Moldova (Republica Moldova)",
41748         "md",
41749         "373"
41750       ],
41751       [
41752         "Monaco",
41753         "mc",
41754         "377"
41755       ],
41756       [
41757         "Mongolia (Монгол)",
41758         "mn",
41759         "976"
41760       ],
41761       [
41762         "Montenegro (Crna Gora)",
41763         "me",
41764         "382"
41765       ],
41766       [
41767         "Montserrat",
41768         "ms",
41769         "1664"
41770       ],
41771       [
41772         "Morocco (‫المغرب‬‎)",
41773         "ma",
41774         "212",
41775         0
41776       ],
41777       [
41778         "Mozambique (Moçambique)",
41779         "mz",
41780         "258"
41781       ],
41782       [
41783         "Myanmar (Burma) (မြန်မာ)",
41784         "mm",
41785         "95"
41786       ],
41787       [
41788         "Namibia (Namibië)",
41789         "na",
41790         "264"
41791       ],
41792       [
41793         "Nauru",
41794         "nr",
41795         "674"
41796       ],
41797       [
41798         "Nepal (नेपाल)",
41799         "np",
41800         "977"
41801       ],
41802       [
41803         "Netherlands (Nederland)",
41804         "nl",
41805         "31"
41806       ],
41807       [
41808         "New Caledonia (Nouvelle-Calédonie)",
41809         "nc",
41810         "687"
41811       ],
41812       [
41813         "New Zealand",
41814         "nz",
41815         "64"
41816       ],
41817       [
41818         "Nicaragua",
41819         "ni",
41820         "505"
41821       ],
41822       [
41823         "Niger (Nijar)",
41824         "ne",
41825         "227"
41826       ],
41827       [
41828         "Nigeria",
41829         "ng",
41830         "234"
41831       ],
41832       [
41833         "Niue",
41834         "nu",
41835         "683"
41836       ],
41837       [
41838         "Norfolk Island",
41839         "nf",
41840         "672"
41841       ],
41842       [
41843         "North Korea (조선 민주주의 인민 공화국)",
41844         "kp",
41845         "850"
41846       ],
41847       [
41848         "Northern Mariana Islands",
41849         "mp",
41850         "1670"
41851       ],
41852       [
41853         "Norway (Norge)",
41854         "no",
41855         "47",
41856         0
41857       ],
41858       [
41859         "Oman (‫عُمان‬‎)",
41860         "om",
41861         "968"
41862       ],
41863       [
41864         "Pakistan (‫پاکستان‬‎)",
41865         "pk",
41866         "92"
41867       ],
41868       [
41869         "Palau",
41870         "pw",
41871         "680"
41872       ],
41873       [
41874         "Palestine (‫فلسطين‬‎)",
41875         "ps",
41876         "970"
41877       ],
41878       [
41879         "Panama (Panamá)",
41880         "pa",
41881         "507"
41882       ],
41883       [
41884         "Papua New Guinea",
41885         "pg",
41886         "675"
41887       ],
41888       [
41889         "Paraguay",
41890         "py",
41891         "595"
41892       ],
41893       [
41894         "Peru (Perú)",
41895         "pe",
41896         "51"
41897       ],
41898       [
41899         "Philippines",
41900         "ph",
41901         "63"
41902       ],
41903       [
41904         "Poland (Polska)",
41905         "pl",
41906         "48"
41907       ],
41908       [
41909         "Portugal",
41910         "pt",
41911         "351"
41912       ],
41913       [
41914         "Puerto Rico",
41915         "pr",
41916         "1",
41917         3,
41918         ["787", "939"]
41919       ],
41920       [
41921         "Qatar (‫قطر‬‎)",
41922         "qa",
41923         "974"
41924       ],
41925       [
41926         "Réunion (La Réunion)",
41927         "re",
41928         "262",
41929         0
41930       ],
41931       [
41932         "Romania (România)",
41933         "ro",
41934         "40"
41935       ],
41936       [
41937         "Russia (Россия)",
41938         "ru",
41939         "7",
41940         0
41941       ],
41942       [
41943         "Rwanda",
41944         "rw",
41945         "250"
41946       ],
41947       [
41948         "Saint Barthélemy",
41949         "bl",
41950         "590",
41951         1
41952       ],
41953       [
41954         "Saint Helena",
41955         "sh",
41956         "290"
41957       ],
41958       [
41959         "Saint Kitts and Nevis",
41960         "kn",
41961         "1869"
41962       ],
41963       [
41964         "Saint Lucia",
41965         "lc",
41966         "1758"
41967       ],
41968       [
41969         "Saint Martin (Saint-Martin (partie française))",
41970         "mf",
41971         "590",
41972         2
41973       ],
41974       [
41975         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41976         "pm",
41977         "508"
41978       ],
41979       [
41980         "Saint Vincent and the Grenadines",
41981         "vc",
41982         "1784"
41983       ],
41984       [
41985         "Samoa",
41986         "ws",
41987         "685"
41988       ],
41989       [
41990         "San Marino",
41991         "sm",
41992         "378"
41993       ],
41994       [
41995         "São Tomé and Príncipe (São Tomé e Príncipe)",
41996         "st",
41997         "239"
41998       ],
41999       [
42000         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42001         "sa",
42002         "966"
42003       ],
42004       [
42005         "Senegal (Sénégal)",
42006         "sn",
42007         "221"
42008       ],
42009       [
42010         "Serbia (Србија)",
42011         "rs",
42012         "381"
42013       ],
42014       [
42015         "Seychelles",
42016         "sc",
42017         "248"
42018       ],
42019       [
42020         "Sierra Leone",
42021         "sl",
42022         "232"
42023       ],
42024       [
42025         "Singapore",
42026         "sg",
42027         "65"
42028       ],
42029       [
42030         "Sint Maarten",
42031         "sx",
42032         "1721"
42033       ],
42034       [
42035         "Slovakia (Slovensko)",
42036         "sk",
42037         "421"
42038       ],
42039       [
42040         "Slovenia (Slovenija)",
42041         "si",
42042         "386"
42043       ],
42044       [
42045         "Solomon Islands",
42046         "sb",
42047         "677"
42048       ],
42049       [
42050         "Somalia (Soomaaliya)",
42051         "so",
42052         "252"
42053       ],
42054       [
42055         "South Africa",
42056         "za",
42057         "27"
42058       ],
42059       [
42060         "South Korea (대한민국)",
42061         "kr",
42062         "82"
42063       ],
42064       [
42065         "South Sudan (‫جنوب السودان‬‎)",
42066         "ss",
42067         "211"
42068       ],
42069       [
42070         "Spain (España)",
42071         "es",
42072         "34"
42073       ],
42074       [
42075         "Sri Lanka (ශ්‍රී ලංකාව)",
42076         "lk",
42077         "94"
42078       ],
42079       [
42080         "Sudan (‫السودان‬‎)",
42081         "sd",
42082         "249"
42083       ],
42084       [
42085         "Suriname",
42086         "sr",
42087         "597"
42088       ],
42089       [
42090         "Svalbard and Jan Mayen",
42091         "sj",
42092         "47",
42093         1
42094       ],
42095       [
42096         "Swaziland",
42097         "sz",
42098         "268"
42099       ],
42100       [
42101         "Sweden (Sverige)",
42102         "se",
42103         "46"
42104       ],
42105       [
42106         "Switzerland (Schweiz)",
42107         "ch",
42108         "41"
42109       ],
42110       [
42111         "Syria (‫سوريا‬‎)",
42112         "sy",
42113         "963"
42114       ],
42115       [
42116         "Taiwan (台灣)",
42117         "tw",
42118         "886"
42119       ],
42120       [
42121         "Tajikistan",
42122         "tj",
42123         "992"
42124       ],
42125       [
42126         "Tanzania",
42127         "tz",
42128         "255"
42129       ],
42130       [
42131         "Thailand (ไทย)",
42132         "th",
42133         "66"
42134       ],
42135       [
42136         "Timor-Leste",
42137         "tl",
42138         "670"
42139       ],
42140       [
42141         "Togo",
42142         "tg",
42143         "228"
42144       ],
42145       [
42146         "Tokelau",
42147         "tk",
42148         "690"
42149       ],
42150       [
42151         "Tonga",
42152         "to",
42153         "676"
42154       ],
42155       [
42156         "Trinidad and Tobago",
42157         "tt",
42158         "1868"
42159       ],
42160       [
42161         "Tunisia (‫تونس‬‎)",
42162         "tn",
42163         "216"
42164       ],
42165       [
42166         "Turkey (Türkiye)",
42167         "tr",
42168         "90"
42169       ],
42170       [
42171         "Turkmenistan",
42172         "tm",
42173         "993"
42174       ],
42175       [
42176         "Turks and Caicos Islands",
42177         "tc",
42178         "1649"
42179       ],
42180       [
42181         "Tuvalu",
42182         "tv",
42183         "688"
42184       ],
42185       [
42186         "U.S. Virgin Islands",
42187         "vi",
42188         "1340"
42189       ],
42190       [
42191         "Uganda",
42192         "ug",
42193         "256"
42194       ],
42195       [
42196         "Ukraine (Україна)",
42197         "ua",
42198         "380"
42199       ],
42200       [
42201         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42202         "ae",
42203         "971"
42204       ],
42205       [
42206         "United Kingdom",
42207         "gb",
42208         "44",
42209         0
42210       ],
42211       [
42212         "United States",
42213         "us",
42214         "1",
42215         0
42216       ],
42217       [
42218         "Uruguay",
42219         "uy",
42220         "598"
42221       ],
42222       [
42223         "Uzbekistan (Oʻzbekiston)",
42224         "uz",
42225         "998"
42226       ],
42227       [
42228         "Vanuatu",
42229         "vu",
42230         "678"
42231       ],
42232       [
42233         "Vatican City (Città del Vaticano)",
42234         "va",
42235         "39",
42236         1
42237       ],
42238       [
42239         "Venezuela",
42240         "ve",
42241         "58"
42242       ],
42243       [
42244         "Vietnam (Việt Nam)",
42245         "vn",
42246         "84"
42247       ],
42248       [
42249         "Wallis and Futuna (Wallis-et-Futuna)",
42250         "wf",
42251         "681"
42252       ],
42253       [
42254         "Western Sahara (‫الصحراء الغربية‬‎)",
42255         "eh",
42256         "212",
42257         1
42258       ],
42259       [
42260         "Yemen (‫اليمن‬‎)",
42261         "ye",
42262         "967"
42263       ],
42264       [
42265         "Zambia",
42266         "zm",
42267         "260"
42268       ],
42269       [
42270         "Zimbabwe",
42271         "zw",
42272         "263"
42273       ],
42274       [
42275         "Åland Islands",
42276         "ax",
42277         "358",
42278         1
42279       ]
42280   ];
42281   
42282   return d;
42283 }/**
42284 *    This script refer to:
42285 *    Title: International Telephone Input
42286 *    Author: Jack O'Connor
42287 *    Code version:  v12.1.12
42288 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42289 **/
42290
42291 /**
42292  * @class Roo.bootstrap.PhoneInput
42293  * @extends Roo.bootstrap.TriggerField
42294  * An input with International dial-code selection
42295  
42296  * @cfg {String} defaultDialCode default '+852'
42297  * @cfg {Array} preferedCountries default []
42298   
42299  * @constructor
42300  * Create a new PhoneInput.
42301  * @param {Object} config Configuration options
42302  */
42303
42304 Roo.bootstrap.PhoneInput = function(config) {
42305     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42306 };
42307
42308 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42309         
42310         listWidth: undefined,
42311         
42312         selectedClass: 'active',
42313         
42314         invalidClass : "has-warning",
42315         
42316         validClass: 'has-success',
42317         
42318         allowed: '0123456789',
42319         
42320         max_length: 15,
42321         
42322         /**
42323          * @cfg {String} defaultDialCode The default dial code when initializing the input
42324          */
42325         defaultDialCode: '+852',
42326         
42327         /**
42328          * @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
42329          */
42330         preferedCountries: false,
42331         
42332         getAutoCreate : function()
42333         {
42334             var data = Roo.bootstrap.PhoneInputData();
42335             var align = this.labelAlign || this.parentLabelAlign();
42336             var id = Roo.id();
42337             
42338             this.allCountries = [];
42339             this.dialCodeMapping = [];
42340             
42341             for (var i = 0; i < data.length; i++) {
42342               var c = data[i];
42343               this.allCountries[i] = {
42344                 name: c[0],
42345                 iso2: c[1],
42346                 dialCode: c[2],
42347                 priority: c[3] || 0,
42348                 areaCodes: c[4] || null
42349               };
42350               this.dialCodeMapping[c[2]] = {
42351                   name: c[0],
42352                   iso2: c[1],
42353                   priority: c[3] || 0,
42354                   areaCodes: c[4] || null
42355               };
42356             }
42357             
42358             var cfg = {
42359                 cls: 'form-group',
42360                 cn: []
42361             };
42362             
42363             var input =  {
42364                 tag: 'input',
42365                 id : id,
42366                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42367                 maxlength: this.max_length,
42368                 cls : 'form-control tel-input',
42369                 autocomplete: 'new-password'
42370             };
42371             
42372             var hiddenInput = {
42373                 tag: 'input',
42374                 type: 'hidden',
42375                 cls: 'hidden-tel-input'
42376             };
42377             
42378             if (this.name) {
42379                 hiddenInput.name = this.name;
42380             }
42381             
42382             if (this.disabled) {
42383                 input.disabled = true;
42384             }
42385             
42386             var flag_container = {
42387                 tag: 'div',
42388                 cls: 'flag-box',
42389                 cn: [
42390                     {
42391                         tag: 'div',
42392                         cls: 'flag'
42393                     },
42394                     {
42395                         tag: 'div',
42396                         cls: 'caret'
42397                     }
42398                 ]
42399             };
42400             
42401             var box = {
42402                 tag: 'div',
42403                 cls: this.hasFeedback ? 'has-feedback' : '',
42404                 cn: [
42405                     hiddenInput,
42406                     input,
42407                     {
42408                         tag: 'input',
42409                         cls: 'dial-code-holder',
42410                         disabled: true
42411                     }
42412                 ]
42413             };
42414             
42415             var container = {
42416                 cls: 'roo-select2-container input-group',
42417                 cn: [
42418                     flag_container,
42419                     box
42420                 ]
42421             };
42422             
42423             if (this.fieldLabel.length) {
42424                 var indicator = {
42425                     tag: 'i',
42426                     tooltip: 'This field is required'
42427                 };
42428                 
42429                 var label = {
42430                     tag: 'label',
42431                     'for':  id,
42432                     cls: 'control-label',
42433                     cn: []
42434                 };
42435                 
42436                 var label_text = {
42437                     tag: 'span',
42438                     html: this.fieldLabel
42439                 };
42440                 
42441                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42442                 label.cn = [
42443                     indicator,
42444                     label_text
42445                 ];
42446                 
42447                 if(this.indicatorpos == 'right') {
42448                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42449                     label.cn = [
42450                         label_text,
42451                         indicator
42452                     ];
42453                 }
42454                 
42455                 if(align == 'left') {
42456                     container = {
42457                         tag: 'div',
42458                         cn: [
42459                             container
42460                         ]
42461                     };
42462                     
42463                     if(this.labelWidth > 12){
42464                         label.style = "width: " + this.labelWidth + 'px';
42465                     }
42466                     if(this.labelWidth < 13 && this.labelmd == 0){
42467                         this.labelmd = this.labelWidth;
42468                     }
42469                     if(this.labellg > 0){
42470                         label.cls += ' col-lg-' + this.labellg;
42471                         input.cls += ' col-lg-' + (12 - this.labellg);
42472                     }
42473                     if(this.labelmd > 0){
42474                         label.cls += ' col-md-' + this.labelmd;
42475                         container.cls += ' col-md-' + (12 - this.labelmd);
42476                     }
42477                     if(this.labelsm > 0){
42478                         label.cls += ' col-sm-' + this.labelsm;
42479                         container.cls += ' col-sm-' + (12 - this.labelsm);
42480                     }
42481                     if(this.labelxs > 0){
42482                         label.cls += ' col-xs-' + this.labelxs;
42483                         container.cls += ' col-xs-' + (12 - this.labelxs);
42484                     }
42485                 }
42486             }
42487             
42488             cfg.cn = [
42489                 label,
42490                 container
42491             ];
42492             
42493             var settings = this;
42494             
42495             ['xs','sm','md','lg'].map(function(size){
42496                 if (settings[size]) {
42497                     cfg.cls += ' col-' + size + '-' + settings[size];
42498                 }
42499             });
42500             
42501             this.store = new Roo.data.Store({
42502                 proxy : new Roo.data.MemoryProxy({}),
42503                 reader : new Roo.data.JsonReader({
42504                     fields : [
42505                         {
42506                             'name' : 'name',
42507                             'type' : 'string'
42508                         },
42509                         {
42510                             'name' : 'iso2',
42511                             'type' : 'string'
42512                         },
42513                         {
42514                             'name' : 'dialCode',
42515                             'type' : 'string'
42516                         },
42517                         {
42518                             'name' : 'priority',
42519                             'type' : 'string'
42520                         },
42521                         {
42522                             'name' : 'areaCodes',
42523                             'type' : 'string'
42524                         }
42525                     ]
42526                 })
42527             });
42528             
42529             if(!this.preferedCountries) {
42530                 this.preferedCountries = [
42531                     'hk',
42532                     'gb',
42533                     'us'
42534                 ];
42535             }
42536             
42537             var p = this.preferedCountries.reverse();
42538             
42539             if(p) {
42540                 for (var i = 0; i < p.length; i++) {
42541                     for (var j = 0; j < this.allCountries.length; j++) {
42542                         if(this.allCountries[j].iso2 == p[i]) {
42543                             var t = this.allCountries[j];
42544                             this.allCountries.splice(j,1);
42545                             this.allCountries.unshift(t);
42546                         }
42547                     } 
42548                 }
42549             }
42550             
42551             this.store.proxy.data = {
42552                 success: true,
42553                 data: this.allCountries
42554             };
42555             
42556             return cfg;
42557         },
42558         
42559         initEvents : function()
42560         {
42561             this.createList();
42562             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42563             
42564             this.indicator = this.indicatorEl();
42565             this.flag = this.flagEl();
42566             this.dialCodeHolder = this.dialCodeHolderEl();
42567             
42568             this.trigger = this.el.select('div.flag-box',true).first();
42569             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42570             
42571             var _this = this;
42572             
42573             (function(){
42574                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42575                 _this.list.setWidth(lw);
42576             }).defer(100);
42577             
42578             this.list.on('mouseover', this.onViewOver, this);
42579             this.list.on('mousemove', this.onViewMove, this);
42580             this.inputEl().on("keyup", this.onKeyUp, this);
42581             this.inputEl().on("keypress", this.onKeyPress, this);
42582             
42583             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42584
42585             this.view = new Roo.View(this.list, this.tpl, {
42586                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42587             });
42588             
42589             this.view.on('click', this.onViewClick, this);
42590             this.setValue(this.defaultDialCode);
42591         },
42592         
42593         onTriggerClick : function(e)
42594         {
42595             Roo.log('trigger click');
42596             if(this.disabled){
42597                 return;
42598             }
42599             
42600             if(this.isExpanded()){
42601                 this.collapse();
42602                 this.hasFocus = false;
42603             }else {
42604                 this.store.load({});
42605                 this.hasFocus = true;
42606                 this.expand();
42607             }
42608         },
42609         
42610         isExpanded : function()
42611         {
42612             return this.list.isVisible();
42613         },
42614         
42615         collapse : function()
42616         {
42617             if(!this.isExpanded()){
42618                 return;
42619             }
42620             this.list.hide();
42621             Roo.get(document).un('mousedown', this.collapseIf, this);
42622             Roo.get(document).un('mousewheel', this.collapseIf, this);
42623             this.fireEvent('collapse', this);
42624             this.validate();
42625         },
42626         
42627         expand : function()
42628         {
42629             Roo.log('expand');
42630
42631             if(this.isExpanded() || !this.hasFocus){
42632                 return;
42633             }
42634             
42635             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42636             this.list.setWidth(lw);
42637             
42638             this.list.show();
42639             this.restrictHeight();
42640             
42641             Roo.get(document).on('mousedown', this.collapseIf, this);
42642             Roo.get(document).on('mousewheel', this.collapseIf, this);
42643             
42644             this.fireEvent('expand', this);
42645         },
42646         
42647         restrictHeight : function()
42648         {
42649             this.list.alignTo(this.inputEl(), this.listAlign);
42650             this.list.alignTo(this.inputEl(), this.listAlign);
42651         },
42652         
42653         onViewOver : function(e, t)
42654         {
42655             if(this.inKeyMode){
42656                 return;
42657             }
42658             var item = this.view.findItemFromChild(t);
42659             
42660             if(item){
42661                 var index = this.view.indexOf(item);
42662                 this.select(index, false);
42663             }
42664         },
42665
42666         // private
42667         onViewClick : function(view, doFocus, el, e)
42668         {
42669             var index = this.view.getSelectedIndexes()[0];
42670             
42671             var r = this.store.getAt(index);
42672             
42673             if(r){
42674                 this.onSelect(r, index);
42675             }
42676             if(doFocus !== false && !this.blockFocus){
42677                 this.inputEl().focus();
42678             }
42679         },
42680         
42681         onViewMove : function(e, t)
42682         {
42683             this.inKeyMode = false;
42684         },
42685         
42686         select : function(index, scrollIntoView)
42687         {
42688             this.selectedIndex = index;
42689             this.view.select(index);
42690             if(scrollIntoView !== false){
42691                 var el = this.view.getNode(index);
42692                 if(el){
42693                     this.list.scrollChildIntoView(el, false);
42694                 }
42695             }
42696         },
42697         
42698         createList : function()
42699         {
42700             this.list = Roo.get(document.body).createChild({
42701                 tag: 'ul',
42702                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42703                 style: 'display:none'
42704             });
42705             
42706             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42707         },
42708         
42709         collapseIf : function(e)
42710         {
42711             var in_combo  = e.within(this.el);
42712             var in_list =  e.within(this.list);
42713             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42714             
42715             if (in_combo || in_list || is_list) {
42716                 return;
42717             }
42718             this.collapse();
42719         },
42720         
42721         onSelect : function(record, index)
42722         {
42723             if(this.fireEvent('beforeselect', this, record, index) !== false){
42724                 
42725                 this.setFlagClass(record.data.iso2);
42726                 this.setDialCode(record.data.dialCode);
42727                 this.hasFocus = false;
42728                 this.collapse();
42729                 this.fireEvent('select', this, record, index);
42730             }
42731         },
42732         
42733         flagEl : function()
42734         {
42735             var flag = this.el.select('div.flag',true).first();
42736             if(!flag){
42737                 return false;
42738             }
42739             return flag;
42740         },
42741         
42742         dialCodeHolderEl : function()
42743         {
42744             var d = this.el.select('input.dial-code-holder',true).first();
42745             if(!d){
42746                 return false;
42747             }
42748             return d;
42749         },
42750         
42751         setDialCode : function(v)
42752         {
42753             this.dialCodeHolder.dom.value = '+'+v;
42754         },
42755         
42756         setFlagClass : function(n)
42757         {
42758             this.flag.dom.className = 'flag '+n;
42759         },
42760         
42761         getValue : function()
42762         {
42763             var v = this.inputEl().getValue();
42764             if(this.dialCodeHolder) {
42765                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42766             }
42767             return v;
42768         },
42769         
42770         setValue : function(v)
42771         {
42772             var d = this.getDialCode(v);
42773             
42774             //invalid dial code
42775             if(v.length == 0 || !d || d.length == 0) {
42776                 if(this.rendered){
42777                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42778                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42779                 }
42780                 return;
42781             }
42782             
42783             //valid dial code
42784             this.setFlagClass(this.dialCodeMapping[d].iso2);
42785             this.setDialCode(d);
42786             this.inputEl().dom.value = v.replace('+'+d,'');
42787             this.hiddenEl().dom.value = this.getValue();
42788             
42789             this.validate();
42790         },
42791         
42792         getDialCode : function(v)
42793         {
42794             v = v ||  '';
42795             
42796             if (v.length == 0) {
42797                 return this.dialCodeHolder.dom.value;
42798             }
42799             
42800             var dialCode = "";
42801             if (v.charAt(0) != "+") {
42802                 return false;
42803             }
42804             var numericChars = "";
42805             for (var i = 1; i < v.length; i++) {
42806               var c = v.charAt(i);
42807               if (!isNaN(c)) {
42808                 numericChars += c;
42809                 if (this.dialCodeMapping[numericChars]) {
42810                   dialCode = v.substr(1, i);
42811                 }
42812                 if (numericChars.length == 4) {
42813                   break;
42814                 }
42815               }
42816             }
42817             return dialCode;
42818         },
42819         
42820         reset : function()
42821         {
42822             this.setValue(this.defaultDialCode);
42823             this.validate();
42824         },
42825         
42826         hiddenEl : function()
42827         {
42828             return this.el.select('input.hidden-tel-input',true).first();
42829         },
42830         
42831         // after setting val
42832         onKeyUp : function(e){
42833             this.setValue(this.getValue());
42834         },
42835         
42836         onKeyPress : function(e){
42837             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42838                 e.stopEvent();
42839             }
42840         }
42841         
42842 });
42843 /**
42844  * @class Roo.bootstrap.MoneyField
42845  * @extends Roo.bootstrap.ComboBox
42846  * Bootstrap MoneyField class
42847  * 
42848  * @constructor
42849  * Create a new MoneyField.
42850  * @param {Object} config Configuration options
42851  */
42852
42853 Roo.bootstrap.MoneyField = function(config) {
42854     
42855     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42856     
42857 };
42858
42859 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42860     
42861     /**
42862      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42863      */
42864     allowDecimals : true,
42865     /**
42866      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42867      */
42868     decimalSeparator : ".",
42869     /**
42870      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42871      */
42872     decimalPrecision : 0,
42873     /**
42874      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42875      */
42876     allowNegative : true,
42877     /**
42878      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42879      */
42880     allowZero: true,
42881     /**
42882      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42883      */
42884     minValue : Number.NEGATIVE_INFINITY,
42885     /**
42886      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42887      */
42888     maxValue : Number.MAX_VALUE,
42889     /**
42890      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42891      */
42892     minText : "The minimum value for this field is {0}",
42893     /**
42894      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42895      */
42896     maxText : "The maximum value for this field is {0}",
42897     /**
42898      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42899      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42900      */
42901     nanText : "{0} is not a valid number",
42902     /**
42903      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42904      */
42905     castInt : true,
42906     /**
42907      * @cfg {String} defaults currency of the MoneyField
42908      * value should be in lkey
42909      */
42910     defaultCurrency : false,
42911     /**
42912      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42913      */
42914     thousandsDelimiter : false,
42915     /**
42916      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42917      */
42918     max_length: false,
42919     
42920     inputlg : 9,
42921     inputmd : 9,
42922     inputsm : 9,
42923     inputxs : 6,
42924     
42925     store : false,
42926     
42927     getAutoCreate : function()
42928     {
42929         var align = this.labelAlign || this.parentLabelAlign();
42930         
42931         var id = Roo.id();
42932
42933         var cfg = {
42934             cls: 'form-group',
42935             cn: []
42936         };
42937
42938         var input =  {
42939             tag: 'input',
42940             id : id,
42941             cls : 'form-control roo-money-amount-input',
42942             autocomplete: 'new-password'
42943         };
42944         
42945         var hiddenInput = {
42946             tag: 'input',
42947             type: 'hidden',
42948             id: Roo.id(),
42949             cls: 'hidden-number-input'
42950         };
42951         
42952         if(this.max_length) {
42953             input.maxlength = this.max_length; 
42954         }
42955         
42956         if (this.name) {
42957             hiddenInput.name = this.name;
42958         }
42959
42960         if (this.disabled) {
42961             input.disabled = true;
42962         }
42963
42964         var clg = 12 - this.inputlg;
42965         var cmd = 12 - this.inputmd;
42966         var csm = 12 - this.inputsm;
42967         var cxs = 12 - this.inputxs;
42968         
42969         var container = {
42970             tag : 'div',
42971             cls : 'row roo-money-field',
42972             cn : [
42973                 {
42974                     tag : 'div',
42975                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42976                     cn : [
42977                         {
42978                             tag : 'div',
42979                             cls: 'roo-select2-container input-group',
42980                             cn: [
42981                                 {
42982                                     tag : 'input',
42983                                     cls : 'form-control roo-money-currency-input',
42984                                     autocomplete: 'new-password',
42985                                     readOnly : 1,
42986                                     name : this.currencyName
42987                                 },
42988                                 {
42989                                     tag :'span',
42990                                     cls : 'input-group-addon',
42991                                     cn : [
42992                                         {
42993                                             tag: 'span',
42994                                             cls: 'caret'
42995                                         }
42996                                     ]
42997                                 }
42998                             ]
42999                         }
43000                     ]
43001                 },
43002                 {
43003                     tag : 'div',
43004                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43005                     cn : [
43006                         {
43007                             tag: 'div',
43008                             cls: this.hasFeedback ? 'has-feedback' : '',
43009                             cn: [
43010                                 input
43011                             ]
43012                         }
43013                     ]
43014                 }
43015             ]
43016             
43017         };
43018         
43019         if (this.fieldLabel.length) {
43020             var indicator = {
43021                 tag: 'i',
43022                 tooltip: 'This field is required'
43023             };
43024
43025             var label = {
43026                 tag: 'label',
43027                 'for':  id,
43028                 cls: 'control-label',
43029                 cn: []
43030             };
43031
43032             var label_text = {
43033                 tag: 'span',
43034                 html: this.fieldLabel
43035             };
43036
43037             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43038             label.cn = [
43039                 indicator,
43040                 label_text
43041             ];
43042
43043             if(this.indicatorpos == 'right') {
43044                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43045                 label.cn = [
43046                     label_text,
43047                     indicator
43048                 ];
43049             }
43050
43051             if(align == 'left') {
43052                 container = {
43053                     tag: 'div',
43054                     cn: [
43055                         container
43056                     ]
43057                 };
43058
43059                 if(this.labelWidth > 12){
43060                     label.style = "width: " + this.labelWidth + 'px';
43061                 }
43062                 if(this.labelWidth < 13 && this.labelmd == 0){
43063                     this.labelmd = this.labelWidth;
43064                 }
43065                 if(this.labellg > 0){
43066                     label.cls += ' col-lg-' + this.labellg;
43067                     input.cls += ' col-lg-' + (12 - this.labellg);
43068                 }
43069                 if(this.labelmd > 0){
43070                     label.cls += ' col-md-' + this.labelmd;
43071                     container.cls += ' col-md-' + (12 - this.labelmd);
43072                 }
43073                 if(this.labelsm > 0){
43074                     label.cls += ' col-sm-' + this.labelsm;
43075                     container.cls += ' col-sm-' + (12 - this.labelsm);
43076                 }
43077                 if(this.labelxs > 0){
43078                     label.cls += ' col-xs-' + this.labelxs;
43079                     container.cls += ' col-xs-' + (12 - this.labelxs);
43080                 }
43081             }
43082         }
43083
43084         cfg.cn = [
43085             label,
43086             container,
43087             hiddenInput
43088         ];
43089         
43090         var settings = this;
43091
43092         ['xs','sm','md','lg'].map(function(size){
43093             if (settings[size]) {
43094                 cfg.cls += ' col-' + size + '-' + settings[size];
43095             }
43096         });
43097         
43098         return cfg;
43099     },
43100     
43101     initEvents : function()
43102     {
43103         this.indicator = this.indicatorEl();
43104         
43105         this.initCurrencyEvent();
43106         
43107         this.initNumberEvent();
43108     },
43109     
43110     initCurrencyEvent : function()
43111     {
43112         if (!this.store) {
43113             throw "can not find store for combo";
43114         }
43115         
43116         this.store = Roo.factory(this.store, Roo.data);
43117         this.store.parent = this;
43118         
43119         this.createList();
43120         
43121         this.triggerEl = this.el.select('.input-group-addon', true).first();
43122         
43123         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43124         
43125         var _this = this;
43126         
43127         (function(){
43128             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43129             _this.list.setWidth(lw);
43130         }).defer(100);
43131         
43132         this.list.on('mouseover', this.onViewOver, this);
43133         this.list.on('mousemove', this.onViewMove, this);
43134         this.list.on('scroll', this.onViewScroll, this);
43135         
43136         if(!this.tpl){
43137             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43138         }
43139         
43140         this.view = new Roo.View(this.list, this.tpl, {
43141             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43142         });
43143         
43144         this.view.on('click', this.onViewClick, this);
43145         
43146         this.store.on('beforeload', this.onBeforeLoad, this);
43147         this.store.on('load', this.onLoad, this);
43148         this.store.on('loadexception', this.onLoadException, this);
43149         
43150         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43151             "up" : function(e){
43152                 this.inKeyMode = true;
43153                 this.selectPrev();
43154             },
43155
43156             "down" : function(e){
43157                 if(!this.isExpanded()){
43158                     this.onTriggerClick();
43159                 }else{
43160                     this.inKeyMode = true;
43161                     this.selectNext();
43162                 }
43163             },
43164
43165             "enter" : function(e){
43166                 this.collapse();
43167                 
43168                 if(this.fireEvent("specialkey", this, e)){
43169                     this.onViewClick(false);
43170                 }
43171                 
43172                 return true;
43173             },
43174
43175             "esc" : function(e){
43176                 this.collapse();
43177             },
43178
43179             "tab" : function(e){
43180                 this.collapse();
43181                 
43182                 if(this.fireEvent("specialkey", this, e)){
43183                     this.onViewClick(false);
43184                 }
43185                 
43186                 return true;
43187             },
43188
43189             scope : this,
43190
43191             doRelay : function(foo, bar, hname){
43192                 if(hname == 'down' || this.scope.isExpanded()){
43193                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43194                 }
43195                 return true;
43196             },
43197
43198             forceKeyDown: true
43199         });
43200         
43201         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43202         
43203     },
43204     
43205     initNumberEvent : function(e)
43206     {
43207         this.inputEl().on("keydown" , this.fireKey,  this);
43208         this.inputEl().on("focus", this.onFocus,  this);
43209         this.inputEl().on("blur", this.onBlur,  this);
43210         
43211         this.inputEl().relayEvent('keyup', this);
43212         
43213         if(this.indicator){
43214             this.indicator.addClass('invisible');
43215         }
43216  
43217         this.originalValue = this.getValue();
43218         
43219         if(this.validationEvent == 'keyup'){
43220             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43221             this.inputEl().on('keyup', this.filterValidation, this);
43222         }
43223         else if(this.validationEvent !== false){
43224             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43225         }
43226         
43227         if(this.selectOnFocus){
43228             this.on("focus", this.preFocus, this);
43229             
43230         }
43231         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43232             this.inputEl().on("keypress", this.filterKeys, this);
43233         } else {
43234             this.inputEl().relayEvent('keypress', this);
43235         }
43236         
43237         var allowed = "0123456789";
43238         
43239         if(this.allowDecimals){
43240             allowed += this.decimalSeparator;
43241         }
43242         
43243         if(this.allowNegative){
43244             allowed += "-";
43245         }
43246         
43247         if(this.thousandsDelimiter) {
43248             allowed += ",";
43249         }
43250         
43251         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43252         
43253         var keyPress = function(e){
43254             
43255             var k = e.getKey();
43256             
43257             var c = e.getCharCode();
43258             
43259             if(
43260                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43261                     allowed.indexOf(String.fromCharCode(c)) === -1
43262             ){
43263                 e.stopEvent();
43264                 return;
43265             }
43266             
43267             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43268                 return;
43269             }
43270             
43271             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43272                 e.stopEvent();
43273             }
43274         };
43275         
43276         this.inputEl().on("keypress", keyPress, this);
43277         
43278     },
43279     
43280     onTriggerClick : function(e)
43281     {   
43282         if(this.disabled){
43283             return;
43284         }
43285         
43286         this.page = 0;
43287         this.loadNext = false;
43288         
43289         if(this.isExpanded()){
43290             this.collapse();
43291             return;
43292         }
43293         
43294         this.hasFocus = true;
43295         
43296         if(this.triggerAction == 'all') {
43297             this.doQuery(this.allQuery, true);
43298             return;
43299         }
43300         
43301         this.doQuery(this.getRawValue());
43302     },
43303     
43304     getCurrency : function()
43305     {   
43306         var v = this.currencyEl().getValue();
43307         
43308         return v;
43309     },
43310     
43311     restrictHeight : function()
43312     {
43313         this.list.alignTo(this.currencyEl(), this.listAlign);
43314         this.list.alignTo(this.currencyEl(), this.listAlign);
43315     },
43316     
43317     onViewClick : function(view, doFocus, el, e)
43318     {
43319         var index = this.view.getSelectedIndexes()[0];
43320         
43321         var r = this.store.getAt(index);
43322         
43323         if(r){
43324             this.onSelect(r, index);
43325         }
43326     },
43327     
43328     onSelect : function(record, index){
43329         
43330         if(this.fireEvent('beforeselect', this, record, index) !== false){
43331         
43332             this.setFromCurrencyData(index > -1 ? record.data : false);
43333             
43334             this.collapse();
43335             
43336             this.fireEvent('select', this, record, index);
43337         }
43338     },
43339     
43340     setFromCurrencyData : function(o)
43341     {
43342         var currency = '';
43343         
43344         this.lastCurrency = o;
43345         
43346         if (this.currencyField) {
43347             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43348         } else {
43349             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43350         }
43351         
43352         this.lastSelectionText = currency;
43353         
43354         //setting default currency
43355         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43356             this.setCurrency(this.defaultCurrency);
43357             return;
43358         }
43359         
43360         this.setCurrency(currency);
43361     },
43362     
43363     setFromData : function(o)
43364     {
43365         var c = {};
43366         
43367         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43368         
43369         this.setFromCurrencyData(c);
43370         
43371         var value = '';
43372         
43373         if (this.name) {
43374             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43375         } else {
43376             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43377         }
43378         
43379         this.setValue(value);
43380         
43381     },
43382     
43383     setCurrency : function(v)
43384     {   
43385         this.currencyValue = v;
43386         
43387         if(this.rendered){
43388             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43389             this.validate();
43390         }
43391     },
43392     
43393     setValue : function(v)
43394     {
43395         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43396         
43397         this.value = v;
43398         
43399         if(this.rendered){
43400             
43401             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43402             
43403             this.inputEl().dom.value = (v == '') ? '' :
43404                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43405             
43406             if(!this.allowZero && v === '0') {
43407                 this.hiddenEl().dom.value = '';
43408                 this.inputEl().dom.value = '';
43409             }
43410             
43411             this.validate();
43412         }
43413     },
43414     
43415     getRawValue : function()
43416     {
43417         var v = this.inputEl().getValue();
43418         
43419         return v;
43420     },
43421     
43422     getValue : function()
43423     {
43424         return this.fixPrecision(this.parseValue(this.getRawValue()));
43425     },
43426     
43427     parseValue : function(value)
43428     {
43429         if(this.thousandsDelimiter) {
43430             value += "";
43431             r = new RegExp(",", "g");
43432             value = value.replace(r, "");
43433         }
43434         
43435         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43436         return isNaN(value) ? '' : value;
43437         
43438     },
43439     
43440     fixPrecision : function(value)
43441     {
43442         if(this.thousandsDelimiter) {
43443             value += "";
43444             r = new RegExp(",", "g");
43445             value = value.replace(r, "");
43446         }
43447         
43448         var nan = isNaN(value);
43449         
43450         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43451             return nan ? '' : value;
43452         }
43453         return parseFloat(value).toFixed(this.decimalPrecision);
43454     },
43455     
43456     decimalPrecisionFcn : function(v)
43457     {
43458         return Math.floor(v);
43459     },
43460     
43461     validateValue : function(value)
43462     {
43463         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43464             return false;
43465         }
43466         
43467         var num = this.parseValue(value);
43468         
43469         if(isNaN(num)){
43470             this.markInvalid(String.format(this.nanText, value));
43471             return false;
43472         }
43473         
43474         if(num < this.minValue){
43475             this.markInvalid(String.format(this.minText, this.minValue));
43476             return false;
43477         }
43478         
43479         if(num > this.maxValue){
43480             this.markInvalid(String.format(this.maxText, this.maxValue));
43481             return false;
43482         }
43483         
43484         return true;
43485     },
43486     
43487     validate : function()
43488     {
43489         if(this.disabled || this.allowBlank){
43490             this.markValid();
43491             return true;
43492         }
43493         
43494         var currency = this.getCurrency();
43495         
43496         if(this.validateValue(this.getRawValue()) && currency.length){
43497             this.markValid();
43498             return true;
43499         }
43500         
43501         this.markInvalid();
43502         return false;
43503     },
43504     
43505     getName: function()
43506     {
43507         return this.name;
43508     },
43509     
43510     beforeBlur : function()
43511     {
43512         if(!this.castInt){
43513             return;
43514         }
43515         
43516         var v = this.parseValue(this.getRawValue());
43517         
43518         if(v || v == 0){
43519             this.setValue(v);
43520         }
43521     },
43522     
43523     onBlur : function()
43524     {
43525         this.beforeBlur();
43526         
43527         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43528             //this.el.removeClass(this.focusClass);
43529         }
43530         
43531         this.hasFocus = false;
43532         
43533         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43534             this.validate();
43535         }
43536         
43537         var v = this.getValue();
43538         
43539         if(String(v) !== String(this.startValue)){
43540             this.fireEvent('change', this, v, this.startValue);
43541         }
43542         
43543         this.fireEvent("blur", this);
43544     },
43545     
43546     inputEl : function()
43547     {
43548         return this.el.select('.roo-money-amount-input', true).first();
43549     },
43550     
43551     currencyEl : function()
43552     {
43553         return this.el.select('.roo-money-currency-input', true).first();
43554     },
43555     
43556     hiddenEl : function()
43557     {
43558         return this.el.select('input.hidden-number-input',true).first();
43559     }
43560     
43561 });/**
43562  * @class Roo.bootstrap.BezierSignature
43563  * @extends Roo.bootstrap.Component
43564  * Bootstrap BezierSignature class
43565  * This script refer to:
43566  *    Title: Signature Pad
43567  *    Author: szimek
43568  *    Availability: https://github.com/szimek/signature_pad
43569  *
43570  * @constructor
43571  * Create a new BezierSignature
43572  * @param {Object} config The config object
43573  */
43574
43575 Roo.bootstrap.BezierSignature = function(config){
43576     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43577     this.addEvents({
43578         "resize" : true
43579     });
43580 };
43581
43582 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43583 {
43584      
43585     curve_data: [],
43586     
43587     is_empty: true,
43588     
43589     mouse_btn_down: true,
43590     
43591     /**
43592      * @cfg {int} canvas height
43593      */
43594     canvas_height: '200px',
43595     
43596     /**
43597      * @cfg {float|function} Radius of a single dot.
43598      */ 
43599     dot_size: false,
43600     
43601     /**
43602      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43603      */
43604     min_width: 0.5,
43605     
43606     /**
43607      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43608      */
43609     max_width: 2.5,
43610     
43611     /**
43612      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43613      */
43614     throttle: 16,
43615     
43616     /**
43617      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43618      */
43619     min_distance: 5,
43620     
43621     /**
43622      * @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.
43623      */
43624     bg_color: 'rgba(0, 0, 0, 0)',
43625     
43626     /**
43627      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43628      */
43629     dot_color: 'black',
43630     
43631     /**
43632      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43633      */ 
43634     velocity_filter_weight: 0.7,
43635     
43636     /**
43637      * @cfg {function} Callback when stroke begin. 
43638      */
43639     onBegin: false,
43640     
43641     /**
43642      * @cfg {function} Callback when stroke end.
43643      */
43644     onEnd: false,
43645     
43646     getAutoCreate : function()
43647     {
43648         var cls = 'roo-signature column';
43649         
43650         if(this.cls){
43651             cls += ' ' + this.cls;
43652         }
43653         
43654         var col_sizes = [
43655             'lg',
43656             'md',
43657             'sm',
43658             'xs'
43659         ];
43660         
43661         for(var i = 0; i < col_sizes.length; i++) {
43662             if(this[col_sizes[i]]) {
43663                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43664             }
43665         }
43666         
43667         var cfg = {
43668             tag: 'div',
43669             cls: cls,
43670             cn: [
43671                 {
43672                     tag: 'div',
43673                     cls: 'roo-signature-body',
43674                     cn: [
43675                         {
43676                             tag: 'canvas',
43677                             cls: 'roo-signature-body-canvas',
43678                             height: this.canvas_height,
43679                             width: this.canvas_width
43680                         }
43681                     ]
43682                 },
43683                 {
43684                     tag: 'input',
43685                     type: 'file',
43686                     style: 'display: none'
43687                 }
43688             ]
43689         };
43690         
43691         return cfg;
43692     },
43693     
43694     initEvents: function() 
43695     {
43696         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43697         
43698         var canvas = this.canvasEl();
43699         
43700         // mouse && touch event swapping...
43701         canvas.dom.style.touchAction = 'none';
43702         canvas.dom.style.msTouchAction = 'none';
43703         
43704         this.mouse_btn_down = false;
43705         canvas.on('mousedown', this._handleMouseDown, this);
43706         canvas.on('mousemove', this._handleMouseMove, this);
43707         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43708         
43709         if (window.PointerEvent) {
43710             canvas.on('pointerdown', this._handleMouseDown, this);
43711             canvas.on('pointermove', this._handleMouseMove, this);
43712             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43713         }
43714         
43715         if ('ontouchstart' in window) {
43716             canvas.on('touchstart', this._handleTouchStart, this);
43717             canvas.on('touchmove', this._handleTouchMove, this);
43718             canvas.on('touchend', this._handleTouchEnd, this);
43719         }
43720         
43721         Roo.EventManager.onWindowResize(this.resize, this, true);
43722         
43723         // file input event
43724         this.fileEl().on('change', this.uploadImage, this);
43725         
43726         this.clear();
43727         
43728         this.resize();
43729     },
43730     
43731     resize: function(){
43732         
43733         var canvas = this.canvasEl().dom;
43734         var ctx = this.canvasElCtx();
43735         var img_data = false;
43736         
43737         if(canvas.width > 0) {
43738             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43739         }
43740         // setting canvas width will clean img data
43741         canvas.width = 0;
43742         
43743         var style = window.getComputedStyle ? 
43744             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43745             
43746         var padding_left = parseInt(style.paddingLeft) || 0;
43747         var padding_right = parseInt(style.paddingRight) || 0;
43748         
43749         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43750         
43751         if(img_data) {
43752             ctx.putImageData(img_data, 0, 0);
43753         }
43754     },
43755     
43756     _handleMouseDown: function(e)
43757     {
43758         if (e.browserEvent.which === 1) {
43759             this.mouse_btn_down = true;
43760             this.strokeBegin(e);
43761         }
43762     },
43763     
43764     _handleMouseMove: function (e)
43765     {
43766         if (this.mouse_btn_down) {
43767             this.strokeMoveUpdate(e);
43768         }
43769     },
43770     
43771     _handleMouseUp: function (e)
43772     {
43773         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43774             this.mouse_btn_down = false;
43775             this.strokeEnd(e);
43776         }
43777     },
43778     
43779     _handleTouchStart: function (e) {
43780         
43781         e.preventDefault();
43782         if (e.browserEvent.targetTouches.length === 1) {
43783             // var touch = e.browserEvent.changedTouches[0];
43784             // this.strokeBegin(touch);
43785             
43786              this.strokeBegin(e); // assume e catching the correct xy...
43787         }
43788     },
43789     
43790     _handleTouchMove: function (e) {
43791         e.preventDefault();
43792         // var touch = event.targetTouches[0];
43793         // _this._strokeMoveUpdate(touch);
43794         this.strokeMoveUpdate(e);
43795     },
43796     
43797     _handleTouchEnd: function (e) {
43798         var wasCanvasTouched = e.target === this.canvasEl().dom;
43799         if (wasCanvasTouched) {
43800             e.preventDefault();
43801             // var touch = event.changedTouches[0];
43802             // _this._strokeEnd(touch);
43803             this.strokeEnd(e);
43804         }
43805     },
43806     
43807     reset: function () {
43808         this._lastPoints = [];
43809         this._lastVelocity = 0;
43810         this._lastWidth = (this.min_width + this.max_width) / 2;
43811         this.canvasElCtx().fillStyle = this.dot_color;
43812     },
43813     
43814     strokeMoveUpdate: function(e)
43815     {
43816         this.strokeUpdate(e);
43817         
43818         if (this.throttle) {
43819             this.throttleStroke(this.strokeUpdate, this.throttle);
43820         }
43821         else {
43822             this.strokeUpdate(e);
43823         }
43824     },
43825     
43826     strokeBegin: function(e)
43827     {
43828         var newPointGroup = {
43829             color: this.dot_color,
43830             points: []
43831         };
43832         
43833         if (typeof this.onBegin === 'function') {
43834             this.onBegin(e);
43835         }
43836         
43837         this.curve_data.push(newPointGroup);
43838         this.reset();
43839         this.strokeUpdate(e);
43840     },
43841     
43842     strokeUpdate: function(e)
43843     {
43844         var rect = this.canvasEl().dom.getBoundingClientRect();
43845         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43846         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43847         var lastPoints = lastPointGroup.points;
43848         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43849         var isLastPointTooClose = lastPoint
43850             ? point.distanceTo(lastPoint) <= this.min_distance
43851             : false;
43852         var color = lastPointGroup.color;
43853         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43854             var curve = this.addPoint(point);
43855             if (!lastPoint) {
43856                 this.drawDot({color: color, point: point});
43857             }
43858             else if (curve) {
43859                 this.drawCurve({color: color, curve: curve});
43860             }
43861             lastPoints.push({
43862                 time: point.time,
43863                 x: point.x,
43864                 y: point.y
43865             });
43866         }
43867     },
43868     
43869     strokeEnd: function(e)
43870     {
43871         this.strokeUpdate(e);
43872         if (typeof this.onEnd === 'function') {
43873             this.onEnd(e);
43874         }
43875     },
43876     
43877     addPoint:  function (point) {
43878         var _lastPoints = this._lastPoints;
43879         _lastPoints.push(point);
43880         if (_lastPoints.length > 2) {
43881             if (_lastPoints.length === 3) {
43882                 _lastPoints.unshift(_lastPoints[0]);
43883             }
43884             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43885             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43886             _lastPoints.shift();
43887             return curve;
43888         }
43889         return null;
43890     },
43891     
43892     calculateCurveWidths: function (startPoint, endPoint) {
43893         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43894             (1 - this.velocity_filter_weight) * this._lastVelocity;
43895
43896         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43897         var widths = {
43898             end: newWidth,
43899             start: this._lastWidth
43900         };
43901         
43902         this._lastVelocity = velocity;
43903         this._lastWidth = newWidth;
43904         return widths;
43905     },
43906     
43907     drawDot: function (_a) {
43908         var color = _a.color, point = _a.point;
43909         var ctx = this.canvasElCtx();
43910         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43911         ctx.beginPath();
43912         this.drawCurveSegment(point.x, point.y, width);
43913         ctx.closePath();
43914         ctx.fillStyle = color;
43915         ctx.fill();
43916     },
43917     
43918     drawCurve: function (_a) {
43919         var color = _a.color, curve = _a.curve;
43920         var ctx = this.canvasElCtx();
43921         var widthDelta = curve.endWidth - curve.startWidth;
43922         var drawSteps = Math.floor(curve.length()) * 2;
43923         ctx.beginPath();
43924         ctx.fillStyle = color;
43925         for (var i = 0; i < drawSteps; i += 1) {
43926         var t = i / drawSteps;
43927         var tt = t * t;
43928         var ttt = tt * t;
43929         var u = 1 - t;
43930         var uu = u * u;
43931         var uuu = uu * u;
43932         var x = uuu * curve.startPoint.x;
43933         x += 3 * uu * t * curve.control1.x;
43934         x += 3 * u * tt * curve.control2.x;
43935         x += ttt * curve.endPoint.x;
43936         var y = uuu * curve.startPoint.y;
43937         y += 3 * uu * t * curve.control1.y;
43938         y += 3 * u * tt * curve.control2.y;
43939         y += ttt * curve.endPoint.y;
43940         var width = curve.startWidth + ttt * widthDelta;
43941         this.drawCurveSegment(x, y, width);
43942         }
43943         ctx.closePath();
43944         ctx.fill();
43945     },
43946     
43947     drawCurveSegment: function (x, y, width) {
43948         var ctx = this.canvasElCtx();
43949         ctx.moveTo(x, y);
43950         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43951         this.is_empty = false;
43952     },
43953     
43954     clear: function()
43955     {
43956         var ctx = this.canvasElCtx();
43957         var canvas = this.canvasEl().dom;
43958         ctx.fillStyle = this.bg_color;
43959         ctx.clearRect(0, 0, canvas.width, canvas.height);
43960         ctx.fillRect(0, 0, canvas.width, canvas.height);
43961         this.curve_data = [];
43962         this.reset();
43963         this.is_empty = true;
43964     },
43965     
43966     fileEl: function()
43967     {
43968         return  this.el.select('input',true).first();
43969     },
43970     
43971     canvasEl: function()
43972     {
43973         return this.el.select('canvas',true).first();
43974     },
43975     
43976     canvasElCtx: function()
43977     {
43978         return this.el.select('canvas',true).first().dom.getContext('2d');
43979     },
43980     
43981     getImage: function(type)
43982     {
43983         if(this.is_empty) {
43984             return false;
43985         }
43986         
43987         // encryption ?
43988         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43989     },
43990     
43991     drawFromImage: function(img_src)
43992     {
43993         var img = new Image();
43994         
43995         img.onload = function(){
43996             this.canvasElCtx().drawImage(img, 0, 0);
43997         }.bind(this);
43998         
43999         img.src = img_src;
44000         
44001         this.is_empty = false;
44002     },
44003     
44004     selectImage: function()
44005     {
44006         this.fileEl().dom.click();
44007     },
44008     
44009     uploadImage: function(e)
44010     {
44011         var reader = new FileReader();
44012         
44013         reader.onload = function(e){
44014             var img = new Image();
44015             img.onload = function(){
44016                 this.reset();
44017                 this.canvasElCtx().drawImage(img, 0, 0);
44018             }.bind(this);
44019             img.src = e.target.result;
44020         }.bind(this);
44021         
44022         reader.readAsDataURL(e.target.files[0]);
44023     },
44024     
44025     // Bezier Point Constructor
44026     Point: (function () {
44027         function Point(x, y, time) {
44028             this.x = x;
44029             this.y = y;
44030             this.time = time || Date.now();
44031         }
44032         Point.prototype.distanceTo = function (start) {
44033             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44034         };
44035         Point.prototype.equals = function (other) {
44036             return this.x === other.x && this.y === other.y && this.time === other.time;
44037         };
44038         Point.prototype.velocityFrom = function (start) {
44039             return this.time !== start.time
44040             ? this.distanceTo(start) / (this.time - start.time)
44041             : 0;
44042         };
44043         return Point;
44044     }()),
44045     
44046     
44047     // Bezier Constructor
44048     Bezier: (function () {
44049         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44050             this.startPoint = startPoint;
44051             this.control2 = control2;
44052             this.control1 = control1;
44053             this.endPoint = endPoint;
44054             this.startWidth = startWidth;
44055             this.endWidth = endWidth;
44056         }
44057         Bezier.fromPoints = function (points, widths, scope) {
44058             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44059             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44060             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44061         };
44062         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44063             var dx1 = s1.x - s2.x;
44064             var dy1 = s1.y - s2.y;
44065             var dx2 = s2.x - s3.x;
44066             var dy2 = s2.y - s3.y;
44067             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44068             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44069             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44070             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44071             var dxm = m1.x - m2.x;
44072             var dym = m1.y - m2.y;
44073             var k = l2 / (l1 + l2);
44074             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44075             var tx = s2.x - cm.x;
44076             var ty = s2.y - cm.y;
44077             return {
44078                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44079                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44080             };
44081         };
44082         Bezier.prototype.length = function () {
44083             var steps = 10;
44084             var length = 0;
44085             var px;
44086             var py;
44087             for (var i = 0; i <= steps; i += 1) {
44088                 var t = i / steps;
44089                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44090                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44091                 if (i > 0) {
44092                     var xdiff = cx - px;
44093                     var ydiff = cy - py;
44094                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44095                 }
44096                 px = cx;
44097                 py = cy;
44098             }
44099             return length;
44100         };
44101         Bezier.prototype.point = function (t, start, c1, c2, end) {
44102             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44103             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44104             + (3.0 * c2 * (1.0 - t) * t * t)
44105             + (end * t * t * t);
44106         };
44107         return Bezier;
44108     }()),
44109     
44110     throttleStroke: function(fn, wait) {
44111       if (wait === void 0) { wait = 250; }
44112       var previous = 0;
44113       var timeout = null;
44114       var result;
44115       var storedContext;
44116       var storedArgs;
44117       var later = function () {
44118           previous = Date.now();
44119           timeout = null;
44120           result = fn.apply(storedContext, storedArgs);
44121           if (!timeout) {
44122               storedContext = null;
44123               storedArgs = [];
44124           }
44125       };
44126       return function wrapper() {
44127           var args = [];
44128           for (var _i = 0; _i < arguments.length; _i++) {
44129               args[_i] = arguments[_i];
44130           }
44131           var now = Date.now();
44132           var remaining = wait - (now - previous);
44133           storedContext = this;
44134           storedArgs = args;
44135           if (remaining <= 0 || remaining > wait) {
44136               if (timeout) {
44137                   clearTimeout(timeout);
44138                   timeout = null;
44139               }
44140               previous = now;
44141               result = fn.apply(storedContext, storedArgs);
44142               if (!timeout) {
44143                   storedContext = null;
44144                   storedArgs = [];
44145               }
44146           }
44147           else if (!timeout) {
44148               timeout = window.setTimeout(later, remaining);
44149           }
44150           return result;
44151       };
44152   }
44153   
44154 });
44155
44156  
44157
44158