4013cad9890abdd40aca73d8baae03d71f8fc93b
[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
6677  * 
6678  * @constructor
6679  * Create a new breadcrumb.Nav
6680  * @param {Object} config The config object
6681  */
6682
6683 Roo.bootstrap.breadcrumb.Item = function(config){
6684     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6685     this.addEvents({
6686         // img events
6687         /**
6688          * @event click
6689          * The img click event for the img.
6690          * @param {Roo.EventObject} e
6691          */
6692         "click" : true
6693     });
6694     
6695 };
6696
6697 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6698     
6699     href: false,
6700     html : '',
6701     
6702     getAutoCreate : function()
6703     {
6704
6705         var cfg = {
6706             tag: 'li'
6707         };
6708         if (this.href !== false) {
6709             cfg.cn = [{
6710                 tag : 'a',
6711                 href : this.href,
6712                 html : this.html
6713             }];
6714         } else {
6715             cfg.html = this.html;
6716         }
6717         
6718         return cfg;
6719     },
6720     
6721     initEvents: function()
6722     {
6723         if (this.href) {
6724             this.el.select('a', true).first().onClick(this.onClick, this)
6725         }
6726         
6727     },
6728     onClick : function(e)
6729     {
6730         e.preventDefault();
6731         this.fireEvent('click',this,  e);
6732     }
6733     
6734 });
6735
6736  /*
6737  * - LGPL
6738  *
6739  * row
6740  * 
6741  */
6742
6743 /**
6744  * @class Roo.bootstrap.Row
6745  * @extends Roo.bootstrap.Component
6746  * Bootstrap Row class (contains columns...)
6747  * 
6748  * @constructor
6749  * Create a new Row
6750  * @param {Object} config The config object
6751  */
6752
6753 Roo.bootstrap.Row = function(config){
6754     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6755 };
6756
6757 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6758     
6759     getAutoCreate : function(){
6760        return {
6761             cls: 'row clearfix'
6762        };
6763     }
6764     
6765     
6766 });
6767
6768  
6769
6770  /*
6771  * - LGPL
6772  *
6773  * pagination
6774  * 
6775  */
6776
6777 /**
6778  * @class Roo.bootstrap.Pagination
6779  * @extends Roo.bootstrap.Component
6780  * Bootstrap Pagination class
6781  * @cfg {String} size xs | sm | md | lg
6782  * @cfg {Boolean} inverse false | true
6783  * 
6784  * @constructor
6785  * Create a new Pagination
6786  * @param {Object} config The config object
6787  */
6788
6789 Roo.bootstrap.Pagination = function(config){
6790     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6791 };
6792
6793 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6794     
6795     cls: false,
6796     size: false,
6797     inverse: false,
6798     
6799     getAutoCreate : function(){
6800         var cfg = {
6801             tag: 'ul',
6802                 cls: 'pagination'
6803         };
6804         if (this.inverse) {
6805             cfg.cls += ' inverse';
6806         }
6807         if (this.html) {
6808             cfg.html=this.html;
6809         }
6810         if (this.cls) {
6811             cfg.cls += " " + this.cls;
6812         }
6813         return cfg;
6814     }
6815    
6816 });
6817
6818  
6819
6820  /*
6821  * - LGPL
6822  *
6823  * Pagination item
6824  * 
6825  */
6826
6827
6828 /**
6829  * @class Roo.bootstrap.PaginationItem
6830  * @extends Roo.bootstrap.Component
6831  * Bootstrap PaginationItem class
6832  * @cfg {String} html text
6833  * @cfg {String} href the link
6834  * @cfg {Boolean} preventDefault (true | false) default true
6835  * @cfg {Boolean} active (true | false) default false
6836  * @cfg {Boolean} disabled default false
6837  * 
6838  * 
6839  * @constructor
6840  * Create a new PaginationItem
6841  * @param {Object} config The config object
6842  */
6843
6844
6845 Roo.bootstrap.PaginationItem = function(config){
6846     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6847     this.addEvents({
6848         // raw events
6849         /**
6850          * @event click
6851          * The raw click event for the entire grid.
6852          * @param {Roo.EventObject} e
6853          */
6854         "click" : true
6855     });
6856 };
6857
6858 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6859     
6860     href : false,
6861     html : false,
6862     preventDefault: true,
6863     active : false,
6864     cls : false,
6865     disabled: false,
6866     
6867     getAutoCreate : function(){
6868         var cfg= {
6869             tag: 'li',
6870             cn: [
6871                 {
6872                     tag : 'a',
6873                     href : this.href ? this.href : '#',
6874                     html : this.html ? this.html : ''
6875                 }
6876             ]
6877         };
6878         
6879         if(this.cls){
6880             cfg.cls = this.cls;
6881         }
6882         
6883         if(this.disabled){
6884             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6885         }
6886         
6887         if(this.active){
6888             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6889         }
6890         
6891         return cfg;
6892     },
6893     
6894     initEvents: function() {
6895         
6896         this.el.on('click', this.onClick, this);
6897         
6898     },
6899     onClick : function(e)
6900     {
6901         Roo.log('PaginationItem on click ');
6902         if(this.preventDefault){
6903             e.preventDefault();
6904         }
6905         
6906         if(this.disabled){
6907             return;
6908         }
6909         
6910         this.fireEvent('click', this, e);
6911     }
6912    
6913 });
6914
6915  
6916
6917  /*
6918  * - LGPL
6919  *
6920  * slider
6921  * 
6922  */
6923
6924
6925 /**
6926  * @class Roo.bootstrap.Slider
6927  * @extends Roo.bootstrap.Component
6928  * Bootstrap Slider class
6929  *    
6930  * @constructor
6931  * Create a new Slider
6932  * @param {Object} config The config object
6933  */
6934
6935 Roo.bootstrap.Slider = function(config){
6936     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6937 };
6938
6939 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6940     
6941     getAutoCreate : function(){
6942         
6943         var cfg = {
6944             tag: 'div',
6945             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6946             cn: [
6947                 {
6948                     tag: 'a',
6949                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6950                 }
6951             ]
6952         };
6953         
6954         return cfg;
6955     }
6956    
6957 });
6958
6959  /*
6960  * Based on:
6961  * Ext JS Library 1.1.1
6962  * Copyright(c) 2006-2007, Ext JS, LLC.
6963  *
6964  * Originally Released Under LGPL - original licence link has changed is not relivant.
6965  *
6966  * Fork - LGPL
6967  * <script type="text/javascript">
6968  */
6969  
6970
6971 /**
6972  * @class Roo.grid.ColumnModel
6973  * @extends Roo.util.Observable
6974  * This is the default implementation of a ColumnModel used by the Grid. It defines
6975  * the columns in the grid.
6976  * <br>Usage:<br>
6977  <pre><code>
6978  var colModel = new Roo.grid.ColumnModel([
6979         {header: "Ticker", width: 60, sortable: true, locked: true},
6980         {header: "Company Name", width: 150, sortable: true},
6981         {header: "Market Cap.", width: 100, sortable: true},
6982         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6983         {header: "Employees", width: 100, sortable: true, resizable: false}
6984  ]);
6985  </code></pre>
6986  * <p>
6987  
6988  * The config options listed for this class are options which may appear in each
6989  * individual column definition.
6990  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6991  * @constructor
6992  * @param {Object} config An Array of column config objects. See this class's
6993  * config objects for details.
6994 */
6995 Roo.grid.ColumnModel = function(config){
6996         /**
6997      * The config passed into the constructor
6998      */
6999     this.config = config;
7000     this.lookup = {};
7001
7002     // if no id, create one
7003     // if the column does not have a dataIndex mapping,
7004     // map it to the order it is in the config
7005     for(var i = 0, len = config.length; i < len; i++){
7006         var c = config[i];
7007         if(typeof c.dataIndex == "undefined"){
7008             c.dataIndex = i;
7009         }
7010         if(typeof c.renderer == "string"){
7011             c.renderer = Roo.util.Format[c.renderer];
7012         }
7013         if(typeof c.id == "undefined"){
7014             c.id = Roo.id();
7015         }
7016         if(c.editor && c.editor.xtype){
7017             c.editor  = Roo.factory(c.editor, Roo.grid);
7018         }
7019         if(c.editor && c.editor.isFormField){
7020             c.editor = new Roo.grid.GridEditor(c.editor);
7021         }
7022         this.lookup[c.id] = c;
7023     }
7024
7025     /**
7026      * The width of columns which have no width specified (defaults to 100)
7027      * @type Number
7028      */
7029     this.defaultWidth = 100;
7030
7031     /**
7032      * Default sortable of columns which have no sortable specified (defaults to false)
7033      * @type Boolean
7034      */
7035     this.defaultSortable = false;
7036
7037     this.addEvents({
7038         /**
7039              * @event widthchange
7040              * Fires when the width of a column changes.
7041              * @param {ColumnModel} this
7042              * @param {Number} columnIndex The column index
7043              * @param {Number} newWidth The new width
7044              */
7045             "widthchange": true,
7046         /**
7047              * @event headerchange
7048              * Fires when the text of a header changes.
7049              * @param {ColumnModel} this
7050              * @param {Number} columnIndex The column index
7051              * @param {Number} newText The new header text
7052              */
7053             "headerchange": true,
7054         /**
7055              * @event hiddenchange
7056              * Fires when a column is hidden or "unhidden".
7057              * @param {ColumnModel} this
7058              * @param {Number} columnIndex The column index
7059              * @param {Boolean} hidden true if hidden, false otherwise
7060              */
7061             "hiddenchange": true,
7062             /**
7063          * @event columnmoved
7064          * Fires when a column is moved.
7065          * @param {ColumnModel} this
7066          * @param {Number} oldIndex
7067          * @param {Number} newIndex
7068          */
7069         "columnmoved" : true,
7070         /**
7071          * @event columlockchange
7072          * Fires when a column's locked state is changed
7073          * @param {ColumnModel} this
7074          * @param {Number} colIndex
7075          * @param {Boolean} locked true if locked
7076          */
7077         "columnlockchange" : true
7078     });
7079     Roo.grid.ColumnModel.superclass.constructor.call(this);
7080 };
7081 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7082     /**
7083      * @cfg {String} header The header text to display in the Grid view.
7084      */
7085     /**
7086      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7087      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7088      * specified, the column's index is used as an index into the Record's data Array.
7089      */
7090     /**
7091      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7092      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7093      */
7094     /**
7095      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7096      * Defaults to the value of the {@link #defaultSortable} property.
7097      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7098      */
7099     /**
7100      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7101      */
7102     /**
7103      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7104      */
7105     /**
7106      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7107      */
7108     /**
7109      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7110      */
7111     /**
7112      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7113      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7114      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7115      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7116      */
7117        /**
7118      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7119      */
7120     /**
7121      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7122      */
7123     /**
7124      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7125      */
7126     /**
7127      * @cfg {String} cursor (Optional)
7128      */
7129     /**
7130      * @cfg {String} tooltip (Optional)
7131      */
7132     /**
7133      * @cfg {Number} xs (Optional)
7134      */
7135     /**
7136      * @cfg {Number} sm (Optional)
7137      */
7138     /**
7139      * @cfg {Number} md (Optional)
7140      */
7141     /**
7142      * @cfg {Number} lg (Optional)
7143      */
7144     /**
7145      * Returns the id of the column at the specified index.
7146      * @param {Number} index The column index
7147      * @return {String} the id
7148      */
7149     getColumnId : function(index){
7150         return this.config[index].id;
7151     },
7152
7153     /**
7154      * Returns the column for a specified id.
7155      * @param {String} id The column id
7156      * @return {Object} the column
7157      */
7158     getColumnById : function(id){
7159         return this.lookup[id];
7160     },
7161
7162     
7163     /**
7164      * Returns the column for a specified dataIndex.
7165      * @param {String} dataIndex The column dataIndex
7166      * @return {Object|Boolean} the column or false if not found
7167      */
7168     getColumnByDataIndex: function(dataIndex){
7169         var index = this.findColumnIndex(dataIndex);
7170         return index > -1 ? this.config[index] : false;
7171     },
7172     
7173     /**
7174      * Returns the index for a specified column id.
7175      * @param {String} id The column id
7176      * @return {Number} the index, or -1 if not found
7177      */
7178     getIndexById : function(id){
7179         for(var i = 0, len = this.config.length; i < len; i++){
7180             if(this.config[i].id == id){
7181                 return i;
7182             }
7183         }
7184         return -1;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column dataIndex.
7189      * @param {String} dataIndex The column dataIndex
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     
7193     findColumnIndex : function(dataIndex){
7194         for(var i = 0, len = this.config.length; i < len; i++){
7195             if(this.config[i].dataIndex == dataIndex){
7196                 return i;
7197             }
7198         }
7199         return -1;
7200     },
7201     
7202     
7203     moveColumn : function(oldIndex, newIndex){
7204         var c = this.config[oldIndex];
7205         this.config.splice(oldIndex, 1);
7206         this.config.splice(newIndex, 0, c);
7207         this.dataMap = null;
7208         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7209     },
7210
7211     isLocked : function(colIndex){
7212         return this.config[colIndex].locked === true;
7213     },
7214
7215     setLocked : function(colIndex, value, suppressEvent){
7216         if(this.isLocked(colIndex) == value){
7217             return;
7218         }
7219         this.config[colIndex].locked = value;
7220         if(!suppressEvent){
7221             this.fireEvent("columnlockchange", this, colIndex, value);
7222         }
7223     },
7224
7225     getTotalLockedWidth : function(){
7226         var totalWidth = 0;
7227         for(var i = 0; i < this.config.length; i++){
7228             if(this.isLocked(i) && !this.isHidden(i)){
7229                 this.totalWidth += this.getColumnWidth(i);
7230             }
7231         }
7232         return totalWidth;
7233     },
7234
7235     getLockedCount : function(){
7236         for(var i = 0, len = this.config.length; i < len; i++){
7237             if(!this.isLocked(i)){
7238                 return i;
7239             }
7240         }
7241         
7242         return this.config.length;
7243     },
7244
7245     /**
7246      * Returns the number of columns.
7247      * @return {Number}
7248      */
7249     getColumnCount : function(visibleOnly){
7250         if(visibleOnly === true){
7251             var c = 0;
7252             for(var i = 0, len = this.config.length; i < len; i++){
7253                 if(!this.isHidden(i)){
7254                     c++;
7255                 }
7256             }
7257             return c;
7258         }
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7264      * @param {Function} fn
7265      * @param {Object} scope (optional)
7266      * @return {Array} result
7267      */
7268     getColumnsBy : function(fn, scope){
7269         var r = [];
7270         for(var i = 0, len = this.config.length; i < len; i++){
7271             var c = this.config[i];
7272             if(fn.call(scope||this, c, i) === true){
7273                 r[r.length] = c;
7274             }
7275         }
7276         return r;
7277     },
7278
7279     /**
7280      * Returns true if the specified column is sortable.
7281      * @param {Number} col The column index
7282      * @return {Boolean}
7283      */
7284     isSortable : function(col){
7285         if(typeof this.config[col].sortable == "undefined"){
7286             return this.defaultSortable;
7287         }
7288         return this.config[col].sortable;
7289     },
7290
7291     /**
7292      * Returns the rendering (formatting) function defined for the column.
7293      * @param {Number} col The column index.
7294      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7295      */
7296     getRenderer : function(col){
7297         if(!this.config[col].renderer){
7298             return Roo.grid.ColumnModel.defaultRenderer;
7299         }
7300         return this.config[col].renderer;
7301     },
7302
7303     /**
7304      * Sets the rendering (formatting) function for a column.
7305      * @param {Number} col The column index
7306      * @param {Function} fn The function to use to process the cell's raw data
7307      * to return HTML markup for the grid view. The render function is called with
7308      * the following parameters:<ul>
7309      * <li>Data value.</li>
7310      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7311      * <li>css A CSS style string to apply to the table cell.</li>
7312      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7313      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7314      * <li>Row index</li>
7315      * <li>Column index</li>
7316      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7317      */
7318     setRenderer : function(col, fn){
7319         this.config[col].renderer = fn;
7320     },
7321
7322     /**
7323      * Returns the width for the specified column.
7324      * @param {Number} col The column index
7325      * @return {Number}
7326      */
7327     getColumnWidth : function(col){
7328         return this.config[col].width * 1 || this.defaultWidth;
7329     },
7330
7331     /**
7332      * Sets the width for a column.
7333      * @param {Number} col The column index
7334      * @param {Number} width The new width
7335      */
7336     setColumnWidth : function(col, width, suppressEvent){
7337         this.config[col].width = width;
7338         this.totalWidth = null;
7339         if(!suppressEvent){
7340              this.fireEvent("widthchange", this, col, width);
7341         }
7342     },
7343
7344     /**
7345      * Returns the total width of all columns.
7346      * @param {Boolean} includeHidden True to include hidden column widths
7347      * @return {Number}
7348      */
7349     getTotalWidth : function(includeHidden){
7350         if(!this.totalWidth){
7351             this.totalWidth = 0;
7352             for(var i = 0, len = this.config.length; i < len; i++){
7353                 if(includeHidden || !this.isHidden(i)){
7354                     this.totalWidth += this.getColumnWidth(i);
7355                 }
7356             }
7357         }
7358         return this.totalWidth;
7359     },
7360
7361     /**
7362      * Returns the header for the specified column.
7363      * @param {Number} col The column index
7364      * @return {String}
7365      */
7366     getColumnHeader : function(col){
7367         return this.config[col].header;
7368     },
7369
7370     /**
7371      * Sets the header for a column.
7372      * @param {Number} col The column index
7373      * @param {String} header The new header
7374      */
7375     setColumnHeader : function(col, header){
7376         this.config[col].header = header;
7377         this.fireEvent("headerchange", this, col, header);
7378     },
7379
7380     /**
7381      * Returns the tooltip for the specified column.
7382      * @param {Number} col The column index
7383      * @return {String}
7384      */
7385     getColumnTooltip : function(col){
7386             return this.config[col].tooltip;
7387     },
7388     /**
7389      * Sets the tooltip for a column.
7390      * @param {Number} col The column index
7391      * @param {String} tooltip The new tooltip
7392      */
7393     setColumnTooltip : function(col, tooltip){
7394             this.config[col].tooltip = tooltip;
7395     },
7396
7397     /**
7398      * Returns the dataIndex for the specified column.
7399      * @param {Number} col The column index
7400      * @return {Number}
7401      */
7402     getDataIndex : function(col){
7403         return this.config[col].dataIndex;
7404     },
7405
7406     /**
7407      * Sets the dataIndex for a column.
7408      * @param {Number} col The column index
7409      * @param {Number} dataIndex The new dataIndex
7410      */
7411     setDataIndex : function(col, dataIndex){
7412         this.config[col].dataIndex = dataIndex;
7413     },
7414
7415     
7416     
7417     /**
7418      * Returns true if the cell is editable.
7419      * @param {Number} colIndex The column index
7420      * @param {Number} rowIndex The row index - this is nto actually used..?
7421      * @return {Boolean}
7422      */
7423     isCellEditable : function(colIndex, rowIndex){
7424         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7425     },
7426
7427     /**
7428      * Returns the editor defined for the cell/column.
7429      * return false or null to disable editing.
7430      * @param {Number} colIndex The column index
7431      * @param {Number} rowIndex The row index
7432      * @return {Object}
7433      */
7434     getCellEditor : function(colIndex, rowIndex){
7435         return this.config[colIndex].editor;
7436     },
7437
7438     /**
7439      * Sets if a column is editable.
7440      * @param {Number} col The column index
7441      * @param {Boolean} editable True if the column is editable
7442      */
7443     setEditable : function(col, editable){
7444         this.config[col].editable = editable;
7445     },
7446
7447
7448     /**
7449      * Returns true if the column is hidden.
7450      * @param {Number} colIndex The column index
7451      * @return {Boolean}
7452      */
7453     isHidden : function(colIndex){
7454         return this.config[colIndex].hidden;
7455     },
7456
7457
7458     /**
7459      * Returns true if the column width cannot be changed
7460      */
7461     isFixed : function(colIndex){
7462         return this.config[colIndex].fixed;
7463     },
7464
7465     /**
7466      * Returns true if the column can be resized
7467      * @return {Boolean}
7468      */
7469     isResizable : function(colIndex){
7470         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7471     },
7472     /**
7473      * Sets if a column is hidden.
7474      * @param {Number} colIndex The column index
7475      * @param {Boolean} hidden True if the column is hidden
7476      */
7477     setHidden : function(colIndex, hidden){
7478         this.config[colIndex].hidden = hidden;
7479         this.totalWidth = null;
7480         this.fireEvent("hiddenchange", this, colIndex, hidden);
7481     },
7482
7483     /**
7484      * Sets the editor for a column.
7485      * @param {Number} col The column index
7486      * @param {Object} editor The editor object
7487      */
7488     setEditor : function(col, editor){
7489         this.config[col].editor = editor;
7490     }
7491 });
7492
7493 Roo.grid.ColumnModel.defaultRenderer = function(value)
7494 {
7495     if(typeof value == "object") {
7496         return value;
7497     }
7498         if(typeof value == "string" && value.length < 1){
7499             return "&#160;";
7500         }
7501     
7502         return String.format("{0}", value);
7503 };
7504
7505 // Alias for backwards compatibility
7506 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7507 /*
7508  * Based on:
7509  * Ext JS Library 1.1.1
7510  * Copyright(c) 2006-2007, Ext JS, LLC.
7511  *
7512  * Originally Released Under LGPL - original licence link has changed is not relivant.
7513  *
7514  * Fork - LGPL
7515  * <script type="text/javascript">
7516  */
7517  
7518 /**
7519  * @class Roo.LoadMask
7520  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7521  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7522  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7523  * element's UpdateManager load indicator and will be destroyed after the initial load.
7524  * @constructor
7525  * Create a new LoadMask
7526  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7527  * @param {Object} config The config object
7528  */
7529 Roo.LoadMask = function(el, config){
7530     this.el = Roo.get(el);
7531     Roo.apply(this, config);
7532     if(this.store){
7533         this.store.on('beforeload', this.onBeforeLoad, this);
7534         this.store.on('load', this.onLoad, this);
7535         this.store.on('loadexception', this.onLoadException, this);
7536         this.removeMask = false;
7537     }else{
7538         var um = this.el.getUpdateManager();
7539         um.showLoadIndicator = false; // disable the default indicator
7540         um.on('beforeupdate', this.onBeforeLoad, this);
7541         um.on('update', this.onLoad, this);
7542         um.on('failure', this.onLoad, this);
7543         this.removeMask = true;
7544     }
7545 };
7546
7547 Roo.LoadMask.prototype = {
7548     /**
7549      * @cfg {Boolean} removeMask
7550      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7551      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7552      */
7553     /**
7554      * @cfg {String} msg
7555      * The text to display in a centered loading message box (defaults to 'Loading...')
7556      */
7557     msg : 'Loading...',
7558     /**
7559      * @cfg {String} msgCls
7560      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7561      */
7562     msgCls : 'x-mask-loading',
7563
7564     /**
7565      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7566      * @type Boolean
7567      */
7568     disabled: false,
7569
7570     /**
7571      * Disables the mask to prevent it from being displayed
7572      */
7573     disable : function(){
7574        this.disabled = true;
7575     },
7576
7577     /**
7578      * Enables the mask so that it can be displayed
7579      */
7580     enable : function(){
7581         this.disabled = false;
7582     },
7583     
7584     onLoadException : function()
7585     {
7586         Roo.log(arguments);
7587         
7588         if (typeof(arguments[3]) != 'undefined') {
7589             Roo.MessageBox.alert("Error loading",arguments[3]);
7590         } 
7591         /*
7592         try {
7593             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7594                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7595             }   
7596         } catch(e) {
7597             
7598         }
7599         */
7600     
7601         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7602     },
7603     // private
7604     onLoad : function()
7605     {
7606         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7607     },
7608
7609     // private
7610     onBeforeLoad : function(){
7611         if(!this.disabled){
7612             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7613         }
7614     },
7615
7616     // private
7617     destroy : function(){
7618         if(this.store){
7619             this.store.un('beforeload', this.onBeforeLoad, this);
7620             this.store.un('load', this.onLoad, this);
7621             this.store.un('loadexception', this.onLoadException, this);
7622         }else{
7623             var um = this.el.getUpdateManager();
7624             um.un('beforeupdate', this.onBeforeLoad, this);
7625             um.un('update', this.onLoad, this);
7626             um.un('failure', this.onLoad, this);
7627         }
7628     }
7629 };/*
7630  * - LGPL
7631  *
7632  * table
7633  * 
7634  */
7635
7636 /**
7637  * @class Roo.bootstrap.Table
7638  * @extends Roo.bootstrap.Component
7639  * Bootstrap Table class
7640  * @cfg {String} cls table class
7641  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7642  * @cfg {String} bgcolor Specifies the background color for a table
7643  * @cfg {Number} border Specifies whether the table cells should have borders or not
7644  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7645  * @cfg {Number} cellspacing Specifies the space between cells
7646  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7647  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7648  * @cfg {String} sortable Specifies that the table should be sortable
7649  * @cfg {String} summary Specifies a summary of the content of a table
7650  * @cfg {Number} width Specifies the width of a table
7651  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7652  * 
7653  * @cfg {boolean} striped Should the rows be alternative striped
7654  * @cfg {boolean} bordered Add borders to the table
7655  * @cfg {boolean} hover Add hover highlighting
7656  * @cfg {boolean} condensed Format condensed
7657  * @cfg {boolean} responsive Format condensed
7658  * @cfg {Boolean} loadMask (true|false) default false
7659  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7660  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7661  * @cfg {Boolean} rowSelection (true|false) default false
7662  * @cfg {Boolean} cellSelection (true|false) default false
7663  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7664  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7665  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7666  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7667  
7668  * 
7669  * @constructor
7670  * Create a new Table
7671  * @param {Object} config The config object
7672  */
7673
7674 Roo.bootstrap.Table = function(config){
7675     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7676     
7677   
7678     
7679     // BC...
7680     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7681     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7682     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7683     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7684     
7685     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7686     if (this.sm) {
7687         this.sm.grid = this;
7688         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7689         this.sm = this.selModel;
7690         this.sm.xmodule = this.xmodule || false;
7691     }
7692     
7693     if (this.cm && typeof(this.cm.config) == 'undefined') {
7694         this.colModel = new Roo.grid.ColumnModel(this.cm);
7695         this.cm = this.colModel;
7696         this.cm.xmodule = this.xmodule || false;
7697     }
7698     if (this.store) {
7699         this.store= Roo.factory(this.store, Roo.data);
7700         this.ds = this.store;
7701         this.ds.xmodule = this.xmodule || false;
7702          
7703     }
7704     if (this.footer && this.store) {
7705         this.footer.dataSource = this.ds;
7706         this.footer = Roo.factory(this.footer);
7707     }
7708     
7709     /** @private */
7710     this.addEvents({
7711         /**
7712          * @event cellclick
7713          * Fires when a cell is clicked
7714          * @param {Roo.bootstrap.Table} this
7715          * @param {Roo.Element} el
7716          * @param {Number} rowIndex
7717          * @param {Number} columnIndex
7718          * @param {Roo.EventObject} e
7719          */
7720         "cellclick" : true,
7721         /**
7722          * @event celldblclick
7723          * Fires when a cell is double clicked
7724          * @param {Roo.bootstrap.Table} this
7725          * @param {Roo.Element} el
7726          * @param {Number} rowIndex
7727          * @param {Number} columnIndex
7728          * @param {Roo.EventObject} e
7729          */
7730         "celldblclick" : true,
7731         /**
7732          * @event rowclick
7733          * Fires when a row is clicked
7734          * @param {Roo.bootstrap.Table} this
7735          * @param {Roo.Element} el
7736          * @param {Number} rowIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "rowclick" : true,
7740         /**
7741          * @event rowdblclick
7742          * Fires when a row is double clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowdblclick" : true,
7749         /**
7750          * @event mouseover
7751          * Fires when a mouseover occur
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Number} columnIndex
7756          * @param {Roo.EventObject} e
7757          */
7758         "mouseover" : true,
7759         /**
7760          * @event mouseout
7761          * Fires when a mouseout occur
7762          * @param {Roo.bootstrap.Table} this
7763          * @param {Roo.Element} el
7764          * @param {Number} rowIndex
7765          * @param {Number} columnIndex
7766          * @param {Roo.EventObject} e
7767          */
7768         "mouseout" : true,
7769         /**
7770          * @event rowclass
7771          * Fires when a row is rendered, so you can change add a style to it.
7772          * @param {Roo.bootstrap.Table} this
7773          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7774          */
7775         'rowclass' : true,
7776           /**
7777          * @event rowsrendered
7778          * Fires when all the  rows have been rendered
7779          * @param {Roo.bootstrap.Table} this
7780          */
7781         'rowsrendered' : true,
7782         /**
7783          * @event contextmenu
7784          * The raw contextmenu event for the entire grid.
7785          * @param {Roo.EventObject} e
7786          */
7787         "contextmenu" : true,
7788         /**
7789          * @event rowcontextmenu
7790          * Fires when a row is right clicked
7791          * @param {Roo.bootstrap.Table} this
7792          * @param {Number} rowIndex
7793          * @param {Roo.EventObject} e
7794          */
7795         "rowcontextmenu" : true,
7796         /**
7797          * @event cellcontextmenu
7798          * Fires when a cell is right clicked
7799          * @param {Roo.bootstrap.Table} this
7800          * @param {Number} rowIndex
7801          * @param {Number} cellIndex
7802          * @param {Roo.EventObject} e
7803          */
7804          "cellcontextmenu" : true,
7805          /**
7806          * @event headercontextmenu
7807          * Fires when a header is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} columnIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "headercontextmenu" : true
7813     });
7814 };
7815
7816 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7817     
7818     cls: false,
7819     align: false,
7820     bgcolor: false,
7821     border: false,
7822     cellpadding: false,
7823     cellspacing: false,
7824     frame: false,
7825     rules: false,
7826     sortable: false,
7827     summary: false,
7828     width: false,
7829     striped : false,
7830     scrollBody : false,
7831     bordered: false,
7832     hover:  false,
7833     condensed : false,
7834     responsive : false,
7835     sm : false,
7836     cm : false,
7837     store : false,
7838     loadMask : false,
7839     footerShow : true,
7840     headerShow : true,
7841   
7842     rowSelection : false,
7843     cellSelection : false,
7844     layout : false,
7845     
7846     // Roo.Element - the tbody
7847     mainBody: false,
7848     // Roo.Element - thead element
7849     mainHead: false,
7850     
7851     container: false, // used by gridpanel...
7852     
7853     lazyLoad : false,
7854     
7855     CSS : Roo.util.CSS,
7856     
7857     auto_hide_footer : false,
7858     
7859     getAutoCreate : function()
7860     {
7861         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7862         
7863         cfg = {
7864             tag: 'table',
7865             cls : 'table',
7866             cn : []
7867         };
7868         if (this.scrollBody) {
7869             cfg.cls += ' table-body-fixed';
7870         }    
7871         if (this.striped) {
7872             cfg.cls += ' table-striped';
7873         }
7874         
7875         if (this.hover) {
7876             cfg.cls += ' table-hover';
7877         }
7878         if (this.bordered) {
7879             cfg.cls += ' table-bordered';
7880         }
7881         if (this.condensed) {
7882             cfg.cls += ' table-condensed';
7883         }
7884         if (this.responsive) {
7885             cfg.cls += ' table-responsive';
7886         }
7887         
7888         if (this.cls) {
7889             cfg.cls+=  ' ' +this.cls;
7890         }
7891         
7892         // this lot should be simplifed...
7893         var _t = this;
7894         var cp = [
7895             'align',
7896             'bgcolor',
7897             'border',
7898             'cellpadding',
7899             'cellspacing',
7900             'frame',
7901             'rules',
7902             'sortable',
7903             'summary',
7904             'width'
7905         ].forEach(function(k) {
7906             if (_t[k]) {
7907                 cfg[k] = _t[k];
7908             }
7909         });
7910         
7911         
7912         if (this.layout) {
7913             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7914         }
7915         
7916         if(this.store || this.cm){
7917             if(this.headerShow){
7918                 cfg.cn.push(this.renderHeader());
7919             }
7920             
7921             cfg.cn.push(this.renderBody());
7922             
7923             if(this.footerShow){
7924                 cfg.cn.push(this.renderFooter());
7925             }
7926             // where does this come from?
7927             //cfg.cls+=  ' TableGrid';
7928         }
7929         
7930         return { cn : [ cfg ] };
7931     },
7932     
7933     initEvents : function()
7934     {   
7935         if(!this.store || !this.cm){
7936             return;
7937         }
7938         if (this.selModel) {
7939             this.selModel.initEvents();
7940         }
7941         
7942         
7943         //Roo.log('initEvents with ds!!!!');
7944         
7945         this.mainBody = this.el.select('tbody', true).first();
7946         this.mainHead = this.el.select('thead', true).first();
7947         this.mainFoot = this.el.select('tfoot', true).first();
7948         
7949         
7950         
7951         var _this = this;
7952         
7953         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7954             e.on('click', _this.sort, _this);
7955         });
7956         
7957         this.mainBody.on("click", this.onClick, this);
7958         this.mainBody.on("dblclick", this.onDblClick, this);
7959         
7960         // why is this done????? = it breaks dialogs??
7961         //this.parent().el.setStyle('position', 'relative');
7962         
7963         
7964         if (this.footer) {
7965             this.footer.parentId = this.id;
7966             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7967             
7968             if(this.lazyLoad){
7969                 this.el.select('tfoot tr td').first().addClass('hide');
7970             }
7971         } 
7972         
7973         if(this.loadMask) {
7974             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7975         }
7976         
7977         this.store.on('load', this.onLoad, this);
7978         this.store.on('beforeload', this.onBeforeLoad, this);
7979         this.store.on('update', this.onUpdate, this);
7980         this.store.on('add', this.onAdd, this);
7981         this.store.on("clear", this.clear, this);
7982         
7983         this.el.on("contextmenu", this.onContextMenu, this);
7984         
7985         this.mainBody.on('scroll', this.onBodyScroll, this);
7986         
7987         this.cm.on("headerchange", this.onHeaderChange, this);
7988         
7989         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7990         
7991     },
7992     
7993     onContextMenu : function(e, t)
7994     {
7995         this.processEvent("contextmenu", e);
7996     },
7997     
7998     processEvent : function(name, e)
7999     {
8000         if (name != 'touchstart' ) {
8001             this.fireEvent(name, e);    
8002         }
8003         
8004         var t = e.getTarget();
8005         
8006         var cell = Roo.get(t);
8007         
8008         if(!cell){
8009             return;
8010         }
8011         
8012         if(cell.findParent('tfoot', false, true)){
8013             return;
8014         }
8015         
8016         if(cell.findParent('thead', false, true)){
8017             
8018             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8019                 cell = Roo.get(t).findParent('th', false, true);
8020                 if (!cell) {
8021                     Roo.log("failed to find th in thead?");
8022                     Roo.log(e.getTarget());
8023                     return;
8024                 }
8025             }
8026             
8027             var cellIndex = cell.dom.cellIndex;
8028             
8029             var ename = name == 'touchstart' ? 'click' : name;
8030             this.fireEvent("header" + ename, this, cellIndex, e);
8031             
8032             return;
8033         }
8034         
8035         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8036             cell = Roo.get(t).findParent('td', false, true);
8037             if (!cell) {
8038                 Roo.log("failed to find th in tbody?");
8039                 Roo.log(e.getTarget());
8040                 return;
8041             }
8042         }
8043         
8044         var row = cell.findParent('tr', false, true);
8045         var cellIndex = cell.dom.cellIndex;
8046         var rowIndex = row.dom.rowIndex - 1;
8047         
8048         if(row !== false){
8049             
8050             this.fireEvent("row" + name, this, rowIndex, e);
8051             
8052             if(cell !== false){
8053             
8054                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8055             }
8056         }
8057         
8058     },
8059     
8060     onMouseover : function(e, el)
8061     {
8062         var cell = Roo.get(el);
8063         
8064         if(!cell){
8065             return;
8066         }
8067         
8068         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8069             cell = cell.findParent('td', false, true);
8070         }
8071         
8072         var row = cell.findParent('tr', false, true);
8073         var cellIndex = cell.dom.cellIndex;
8074         var rowIndex = row.dom.rowIndex - 1; // start from 0
8075         
8076         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8077         
8078     },
8079     
8080     onMouseout : function(e, el)
8081     {
8082         var cell = Roo.get(el);
8083         
8084         if(!cell){
8085             return;
8086         }
8087         
8088         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8089             cell = cell.findParent('td', false, true);
8090         }
8091         
8092         var row = cell.findParent('tr', false, true);
8093         var cellIndex = cell.dom.cellIndex;
8094         var rowIndex = row.dom.rowIndex - 1; // start from 0
8095         
8096         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8097         
8098     },
8099     
8100     onClick : function(e, el)
8101     {
8102         var cell = Roo.get(el);
8103         
8104         if(!cell || (!this.cellSelection && !this.rowSelection)){
8105             return;
8106         }
8107         
8108         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8109             cell = cell.findParent('td', false, true);
8110         }
8111         
8112         if(!cell || typeof(cell) == 'undefined'){
8113             return;
8114         }
8115         
8116         var row = cell.findParent('tr', false, true);
8117         
8118         if(!row || typeof(row) == 'undefined'){
8119             return;
8120         }
8121         
8122         var cellIndex = cell.dom.cellIndex;
8123         var rowIndex = this.getRowIndex(row);
8124         
8125         // why??? - should these not be based on SelectionModel?
8126         if(this.cellSelection){
8127             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8128         }
8129         
8130         if(this.rowSelection){
8131             this.fireEvent('rowclick', this, row, rowIndex, e);
8132         }
8133         
8134         
8135     },
8136         
8137     onDblClick : function(e,el)
8138     {
8139         var cell = Roo.get(el);
8140         
8141         if(!cell || (!this.cellSelection && !this.rowSelection)){
8142             return;
8143         }
8144         
8145         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8146             cell = cell.findParent('td', false, true);
8147         }
8148         
8149         if(!cell || typeof(cell) == 'undefined'){
8150             return;
8151         }
8152         
8153         var row = cell.findParent('tr', false, true);
8154         
8155         if(!row || typeof(row) == 'undefined'){
8156             return;
8157         }
8158         
8159         var cellIndex = cell.dom.cellIndex;
8160         var rowIndex = this.getRowIndex(row);
8161         
8162         if(this.cellSelection){
8163             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8164         }
8165         
8166         if(this.rowSelection){
8167             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8168         }
8169     },
8170     
8171     sort : function(e,el)
8172     {
8173         var col = Roo.get(el);
8174         
8175         if(!col.hasClass('sortable')){
8176             return;
8177         }
8178         
8179         var sort = col.attr('sort');
8180         var dir = 'ASC';
8181         
8182         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8183             dir = 'DESC';
8184         }
8185         
8186         this.store.sortInfo = {field : sort, direction : dir};
8187         
8188         if (this.footer) {
8189             Roo.log("calling footer first");
8190             this.footer.onClick('first');
8191         } else {
8192         
8193             this.store.load({ params : { start : 0 } });
8194         }
8195     },
8196     
8197     renderHeader : function()
8198     {
8199         var header = {
8200             tag: 'thead',
8201             cn : []
8202         };
8203         
8204         var cm = this.cm;
8205         this.totalWidth = 0;
8206         
8207         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8208             
8209             var config = cm.config[i];
8210             
8211             var c = {
8212                 tag: 'th',
8213                 cls : 'x-hcol-' + i,
8214                 style : '',
8215                 html: cm.getColumnHeader(i)
8216             };
8217             
8218             var hh = '';
8219             
8220             if(typeof(config.sortable) != 'undefined' && config.sortable){
8221                 c.cls = 'sortable';
8222                 c.html = '<i class="glyphicon"></i>' + c.html;
8223             }
8224             
8225             // could use BS4 hidden-..-down 
8226             
8227             if(typeof(config.lgHeader) != 'undefined'){
8228                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8229             }
8230             
8231             if(typeof(config.mdHeader) != 'undefined'){
8232                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8233             }
8234             
8235             if(typeof(config.smHeader) != 'undefined'){
8236                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8237             }
8238             
8239             if(typeof(config.xsHeader) != 'undefined'){
8240                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8241             }
8242             
8243             if(hh.length){
8244                 c.html = hh;
8245             }
8246             
8247             if(typeof(config.tooltip) != 'undefined'){
8248                 c.tooltip = config.tooltip;
8249             }
8250             
8251             if(typeof(config.colspan) != 'undefined'){
8252                 c.colspan = config.colspan;
8253             }
8254             
8255             if(typeof(config.hidden) != 'undefined' && config.hidden){
8256                 c.style += ' display:none;';
8257             }
8258             
8259             if(typeof(config.dataIndex) != 'undefined'){
8260                 c.sort = config.dataIndex;
8261             }
8262             
8263            
8264             
8265             if(typeof(config.align) != 'undefined' && config.align.length){
8266                 c.style += ' text-align:' + config.align + ';';
8267             }
8268             
8269             if(typeof(config.width) != 'undefined'){
8270                 c.style += ' width:' + config.width + 'px;';
8271                 this.totalWidth += config.width;
8272             } else {
8273                 this.totalWidth += 100; // assume minimum of 100 per column?
8274             }
8275             
8276             if(typeof(config.cls) != 'undefined'){
8277                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8278             }
8279             
8280             ['xs','sm','md','lg'].map(function(size){
8281                 
8282                 if(typeof(config[size]) == 'undefined'){
8283                     return;
8284                 }
8285                  
8286                 if (!config[size]) { // 0 = hidden
8287                     // BS 4 '0' is treated as hide that column and below.
8288                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8289                     return;
8290                 }
8291                 
8292                 c.cls += ' col-' + size + '-' + config[size] + (
8293                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8294                 );
8295                 
8296                 
8297             });
8298             
8299             header.cn.push(c)
8300         }
8301         
8302         return header;
8303     },
8304     
8305     renderBody : function()
8306     {
8307         var body = {
8308             tag: 'tbody',
8309             cn : [
8310                 {
8311                     tag: 'tr',
8312                     cn : [
8313                         {
8314                             tag : 'td',
8315                             colspan :  this.cm.getColumnCount()
8316                         }
8317                     ]
8318                 }
8319             ]
8320         };
8321         
8322         return body;
8323     },
8324     
8325     renderFooter : function()
8326     {
8327         var footer = {
8328             tag: 'tfoot',
8329             cn : [
8330                 {
8331                     tag: 'tr',
8332                     cn : [
8333                         {
8334                             tag : 'td',
8335                             colspan :  this.cm.getColumnCount()
8336                         }
8337                     ]
8338                 }
8339             ]
8340         };
8341         
8342         return footer;
8343     },
8344     
8345     
8346     
8347     onLoad : function()
8348     {
8349 //        Roo.log('ds onload');
8350         this.clear();
8351         
8352         var _this = this;
8353         var cm = this.cm;
8354         var ds = this.store;
8355         
8356         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8357             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8358             if (_this.store.sortInfo) {
8359                     
8360                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8361                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8362                 }
8363                 
8364                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8365                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8366                 }
8367             }
8368         });
8369         
8370         var tbody =  this.mainBody;
8371               
8372         if(ds.getCount() > 0){
8373             ds.data.each(function(d,rowIndex){
8374                 var row =  this.renderRow(cm, ds, rowIndex);
8375                 
8376                 tbody.createChild(row);
8377                 
8378                 var _this = this;
8379                 
8380                 if(row.cellObjects.length){
8381                     Roo.each(row.cellObjects, function(r){
8382                         _this.renderCellObject(r);
8383                     })
8384                 }
8385                 
8386             }, this);
8387         }
8388         
8389         var tfoot = this.el.select('tfoot', true).first();
8390         
8391         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8392             
8393             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8394             
8395             var total = this.ds.getTotalCount();
8396             
8397             if(this.footer.pageSize < total){
8398                 this.mainFoot.show();
8399             }
8400         }
8401         
8402         Roo.each(this.el.select('tbody td', true).elements, function(e){
8403             e.on('mouseover', _this.onMouseover, _this);
8404         });
8405         
8406         Roo.each(this.el.select('tbody td', true).elements, function(e){
8407             e.on('mouseout', _this.onMouseout, _this);
8408         });
8409         this.fireEvent('rowsrendered', this);
8410         
8411         this.autoSize();
8412     },
8413     
8414     
8415     onUpdate : function(ds,record)
8416     {
8417         this.refreshRow(record);
8418         this.autoSize();
8419     },
8420     
8421     onRemove : function(ds, record, index, isUpdate){
8422         if(isUpdate !== true){
8423             this.fireEvent("beforerowremoved", this, index, record);
8424         }
8425         var bt = this.mainBody.dom;
8426         
8427         var rows = this.el.select('tbody > tr', true).elements;
8428         
8429         if(typeof(rows[index]) != 'undefined'){
8430             bt.removeChild(rows[index].dom);
8431         }
8432         
8433 //        if(bt.rows[index]){
8434 //            bt.removeChild(bt.rows[index]);
8435 //        }
8436         
8437         if(isUpdate !== true){
8438             //this.stripeRows(index);
8439             //this.syncRowHeights(index, index);
8440             //this.layout();
8441             this.fireEvent("rowremoved", this, index, record);
8442         }
8443     },
8444     
8445     onAdd : function(ds, records, rowIndex)
8446     {
8447         //Roo.log('on Add called');
8448         // - note this does not handle multiple adding very well..
8449         var bt = this.mainBody.dom;
8450         for (var i =0 ; i < records.length;i++) {
8451             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8452             //Roo.log(records[i]);
8453             //Roo.log(this.store.getAt(rowIndex+i));
8454             this.insertRow(this.store, rowIndex + i, false);
8455             return;
8456         }
8457         
8458     },
8459     
8460     
8461     refreshRow : function(record){
8462         var ds = this.store, index;
8463         if(typeof record == 'number'){
8464             index = record;
8465             record = ds.getAt(index);
8466         }else{
8467             index = ds.indexOf(record);
8468             if (index < 0) {
8469                 return; // should not happen - but seems to 
8470             }
8471         }
8472         this.insertRow(ds, index, true);
8473         this.autoSize();
8474         this.onRemove(ds, record, index+1, true);
8475         this.autoSize();
8476         //this.syncRowHeights(index, index);
8477         //this.layout();
8478         this.fireEvent("rowupdated", this, index, record);
8479     },
8480     
8481     insertRow : function(dm, rowIndex, isUpdate){
8482         
8483         if(!isUpdate){
8484             this.fireEvent("beforerowsinserted", this, rowIndex);
8485         }
8486             //var s = this.getScrollState();
8487         var row = this.renderRow(this.cm, this.store, rowIndex);
8488         // insert before rowIndex..
8489         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8490         
8491         var _this = this;
8492                 
8493         if(row.cellObjects.length){
8494             Roo.each(row.cellObjects, function(r){
8495                 _this.renderCellObject(r);
8496             })
8497         }
8498             
8499         if(!isUpdate){
8500             this.fireEvent("rowsinserted", this, rowIndex);
8501             //this.syncRowHeights(firstRow, lastRow);
8502             //this.stripeRows(firstRow);
8503             //this.layout();
8504         }
8505         
8506     },
8507     
8508     
8509     getRowDom : function(rowIndex)
8510     {
8511         var rows = this.el.select('tbody > tr', true).elements;
8512         
8513         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8514         
8515     },
8516     // returns the object tree for a tr..
8517   
8518     
8519     renderRow : function(cm, ds, rowIndex) 
8520     {
8521         var d = ds.getAt(rowIndex);
8522         
8523         var row = {
8524             tag : 'tr',
8525             cls : 'x-row-' + rowIndex,
8526             cn : []
8527         };
8528             
8529         var cellObjects = [];
8530         
8531         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8532             var config = cm.config[i];
8533             
8534             var renderer = cm.getRenderer(i);
8535             var value = '';
8536             var id = false;
8537             
8538             if(typeof(renderer) !== 'undefined'){
8539                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8540             }
8541             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8542             // and are rendered into the cells after the row is rendered - using the id for the element.
8543             
8544             if(typeof(value) === 'object'){
8545                 id = Roo.id();
8546                 cellObjects.push({
8547                     container : id,
8548                     cfg : value 
8549                 })
8550             }
8551             
8552             var rowcfg = {
8553                 record: d,
8554                 rowIndex : rowIndex,
8555                 colIndex : i,
8556                 rowClass : ''
8557             };
8558
8559             this.fireEvent('rowclass', this, rowcfg);
8560             
8561             var td = {
8562                 tag: 'td',
8563                 cls : rowcfg.rowClass + ' x-col-' + i,
8564                 style: '',
8565                 html: (typeof(value) === 'object') ? '' : value
8566             };
8567             
8568             if (id) {
8569                 td.id = id;
8570             }
8571             
8572             if(typeof(config.colspan) != 'undefined'){
8573                 td.colspan = config.colspan;
8574             }
8575             
8576             if(typeof(config.hidden) != 'undefined' && config.hidden){
8577                 td.style += ' display:none;';
8578             }
8579             
8580             if(typeof(config.align) != 'undefined' && config.align.length){
8581                 td.style += ' text-align:' + config.align + ';';
8582             }
8583             if(typeof(config.valign) != 'undefined' && config.valign.length){
8584                 td.style += ' vertical-align:' + config.valign + ';';
8585             }
8586             
8587             if(typeof(config.width) != 'undefined'){
8588                 td.style += ' width:' +  config.width + 'px;';
8589             }
8590             
8591             if(typeof(config.cursor) != 'undefined'){
8592                 td.style += ' cursor:' +  config.cursor + ';';
8593             }
8594             
8595             if(typeof(config.cls) != 'undefined'){
8596                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8597             }
8598             
8599             ['xs','sm','md','lg'].map(function(size){
8600                 
8601                 if(typeof(config[size]) == 'undefined'){
8602                     return;
8603                 }
8604                 
8605                 
8606                   
8607                 if (!config[size]) { // 0 = hidden
8608                     // BS 4 '0' is treated as hide that column and below.
8609                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8610                     return;
8611                 }
8612                 
8613                 td.cls += ' col-' + size + '-' + config[size] + (
8614                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8615                 );
8616                  
8617
8618             });
8619             
8620             row.cn.push(td);
8621            
8622         }
8623         
8624         row.cellObjects = cellObjects;
8625         
8626         return row;
8627           
8628     },
8629     
8630     
8631     
8632     onBeforeLoad : function()
8633     {
8634         
8635     },
8636      /**
8637      * Remove all rows
8638      */
8639     clear : function()
8640     {
8641         this.el.select('tbody', true).first().dom.innerHTML = '';
8642     },
8643     /**
8644      * Show or hide a row.
8645      * @param {Number} rowIndex to show or hide
8646      * @param {Boolean} state hide
8647      */
8648     setRowVisibility : function(rowIndex, state)
8649     {
8650         var bt = this.mainBody.dom;
8651         
8652         var rows = this.el.select('tbody > tr', true).elements;
8653         
8654         if(typeof(rows[rowIndex]) == 'undefined'){
8655             return;
8656         }
8657         rows[rowIndex].dom.style.display = state ? '' : 'none';
8658     },
8659     
8660     
8661     getSelectionModel : function(){
8662         if(!this.selModel){
8663             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8664         }
8665         return this.selModel;
8666     },
8667     /*
8668      * Render the Roo.bootstrap object from renderder
8669      */
8670     renderCellObject : function(r)
8671     {
8672         var _this = this;
8673         
8674         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8675         
8676         var t = r.cfg.render(r.container);
8677         
8678         if(r.cfg.cn){
8679             Roo.each(r.cfg.cn, function(c){
8680                 var child = {
8681                     container: t.getChildContainer(),
8682                     cfg: c
8683                 };
8684                 _this.renderCellObject(child);
8685             })
8686         }
8687     },
8688     
8689     getRowIndex : function(row)
8690     {
8691         var rowIndex = -1;
8692         
8693         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8694             if(el != row){
8695                 return;
8696             }
8697             
8698             rowIndex = index;
8699         });
8700         
8701         return rowIndex;
8702     },
8703      /**
8704      * Returns the grid's underlying element = used by panel.Grid
8705      * @return {Element} The element
8706      */
8707     getGridEl : function(){
8708         return this.el;
8709     },
8710      /**
8711      * Forces a resize - used by panel.Grid
8712      * @return {Element} The element
8713      */
8714     autoSize : function()
8715     {
8716         //var ctr = Roo.get(this.container.dom.parentElement);
8717         var ctr = Roo.get(this.el.dom);
8718         
8719         var thd = this.getGridEl().select('thead',true).first();
8720         var tbd = this.getGridEl().select('tbody', true).first();
8721         var tfd = this.getGridEl().select('tfoot', true).first();
8722         
8723         var cw = ctr.getWidth();
8724         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8725         
8726         if (tbd) {
8727             
8728             tbd.setWidth(ctr.getWidth());
8729             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8730             // this needs fixing for various usage - currently only hydra job advers I think..
8731             //tdb.setHeight(
8732             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8733             //); 
8734             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8735             cw -= barsize;
8736         }
8737         cw = Math.max(cw, this.totalWidth);
8738         this.getGridEl().select('tbody tr',true).setWidth(cw);
8739         
8740         // resize 'expandable coloumn?
8741         
8742         return; // we doe not have a view in this design..
8743         
8744     },
8745     onBodyScroll: function()
8746     {
8747         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8748         if(this.mainHead){
8749             this.mainHead.setStyle({
8750                 'position' : 'relative',
8751                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8752             });
8753         }
8754         
8755         if(this.lazyLoad){
8756             
8757             var scrollHeight = this.mainBody.dom.scrollHeight;
8758             
8759             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8760             
8761             var height = this.mainBody.getHeight();
8762             
8763             if(scrollHeight - height == scrollTop) {
8764                 
8765                 var total = this.ds.getTotalCount();
8766                 
8767                 if(this.footer.cursor + this.footer.pageSize < total){
8768                     
8769                     this.footer.ds.load({
8770                         params : {
8771                             start : this.footer.cursor + this.footer.pageSize,
8772                             limit : this.footer.pageSize
8773                         },
8774                         add : true
8775                     });
8776                 }
8777             }
8778             
8779         }
8780     },
8781     
8782     onHeaderChange : function()
8783     {
8784         var header = this.renderHeader();
8785         var table = this.el.select('table', true).first();
8786         
8787         this.mainHead.remove();
8788         this.mainHead = table.createChild(header, this.mainBody, false);
8789     },
8790     
8791     onHiddenChange : function(colModel, colIndex, hidden)
8792     {
8793         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8794         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8795         
8796         this.CSS.updateRule(thSelector, "display", "");
8797         this.CSS.updateRule(tdSelector, "display", "");
8798         
8799         if(hidden){
8800             this.CSS.updateRule(thSelector, "display", "none");
8801             this.CSS.updateRule(tdSelector, "display", "none");
8802         }
8803         
8804         this.onHeaderChange();
8805         this.onLoad();
8806     },
8807     
8808     setColumnWidth: function(col_index, width)
8809     {
8810         // width = "md-2 xs-2..."
8811         if(!this.colModel.config[col_index]) {
8812             return;
8813         }
8814         
8815         var w = width.split(" ");
8816         
8817         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8818         
8819         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8820         
8821         
8822         for(var j = 0; j < w.length; j++) {
8823             
8824             if(!w[j]) {
8825                 continue;
8826             }
8827             
8828             var size_cls = w[j].split("-");
8829             
8830             if(!Number.isInteger(size_cls[1] * 1)) {
8831                 continue;
8832             }
8833             
8834             if(!this.colModel.config[col_index][size_cls[0]]) {
8835                 continue;
8836             }
8837             
8838             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8839                 continue;
8840             }
8841             
8842             h_row[0].classList.replace(
8843                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8844                 "col-"+size_cls[0]+"-"+size_cls[1]
8845             );
8846             
8847             for(var i = 0; i < rows.length; i++) {
8848                 
8849                 var size_cls = w[j].split("-");
8850                 
8851                 if(!Number.isInteger(size_cls[1] * 1)) {
8852                     continue;
8853                 }
8854                 
8855                 if(!this.colModel.config[col_index][size_cls[0]]) {
8856                     continue;
8857                 }
8858                 
8859                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8860                     continue;
8861                 }
8862                 
8863                 rows[i].classList.replace(
8864                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8865                     "col-"+size_cls[0]+"-"+size_cls[1]
8866                 );
8867             }
8868             
8869             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8870         }
8871     }
8872 });
8873
8874  
8875
8876  /*
8877  * - LGPL
8878  *
8879  * table cell
8880  * 
8881  */
8882
8883 /**
8884  * @class Roo.bootstrap.TableCell
8885  * @extends Roo.bootstrap.Component
8886  * Bootstrap TableCell class
8887  * @cfg {String} html cell contain text
8888  * @cfg {String} cls cell class
8889  * @cfg {String} tag cell tag (td|th) default td
8890  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8891  * @cfg {String} align Aligns the content in a cell
8892  * @cfg {String} axis Categorizes cells
8893  * @cfg {String} bgcolor Specifies the background color of a cell
8894  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8895  * @cfg {Number} colspan Specifies the number of columns a cell should span
8896  * @cfg {String} headers Specifies one or more header cells a cell is related to
8897  * @cfg {Number} height Sets the height of a cell
8898  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8899  * @cfg {Number} rowspan Sets the number of rows a cell should span
8900  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8901  * @cfg {String} valign Vertical aligns the content in a cell
8902  * @cfg {Number} width Specifies the width of a cell
8903  * 
8904  * @constructor
8905  * Create a new TableCell
8906  * @param {Object} config The config object
8907  */
8908
8909 Roo.bootstrap.TableCell = function(config){
8910     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8911 };
8912
8913 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8914     
8915     html: false,
8916     cls: false,
8917     tag: false,
8918     abbr: false,
8919     align: false,
8920     axis: false,
8921     bgcolor: false,
8922     charoff: false,
8923     colspan: false,
8924     headers: false,
8925     height: false,
8926     nowrap: false,
8927     rowspan: false,
8928     scope: false,
8929     valign: false,
8930     width: false,
8931     
8932     
8933     getAutoCreate : function(){
8934         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8935         
8936         cfg = {
8937             tag: 'td'
8938         };
8939         
8940         if(this.tag){
8941             cfg.tag = this.tag;
8942         }
8943         
8944         if (this.html) {
8945             cfg.html=this.html
8946         }
8947         if (this.cls) {
8948             cfg.cls=this.cls
8949         }
8950         if (this.abbr) {
8951             cfg.abbr=this.abbr
8952         }
8953         if (this.align) {
8954             cfg.align=this.align
8955         }
8956         if (this.axis) {
8957             cfg.axis=this.axis
8958         }
8959         if (this.bgcolor) {
8960             cfg.bgcolor=this.bgcolor
8961         }
8962         if (this.charoff) {
8963             cfg.charoff=this.charoff
8964         }
8965         if (this.colspan) {
8966             cfg.colspan=this.colspan
8967         }
8968         if (this.headers) {
8969             cfg.headers=this.headers
8970         }
8971         if (this.height) {
8972             cfg.height=this.height
8973         }
8974         if (this.nowrap) {
8975             cfg.nowrap=this.nowrap
8976         }
8977         if (this.rowspan) {
8978             cfg.rowspan=this.rowspan
8979         }
8980         if (this.scope) {
8981             cfg.scope=this.scope
8982         }
8983         if (this.valign) {
8984             cfg.valign=this.valign
8985         }
8986         if (this.width) {
8987             cfg.width=this.width
8988         }
8989         
8990         
8991         return cfg;
8992     }
8993    
8994 });
8995
8996  
8997
8998  /*
8999  * - LGPL
9000  *
9001  * table row
9002  * 
9003  */
9004
9005 /**
9006  * @class Roo.bootstrap.TableRow
9007  * @extends Roo.bootstrap.Component
9008  * Bootstrap TableRow class
9009  * @cfg {String} cls row class
9010  * @cfg {String} align Aligns the content in a table row
9011  * @cfg {String} bgcolor Specifies a background color for a table row
9012  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9013  * @cfg {String} valign Vertical aligns the content in a table row
9014  * 
9015  * @constructor
9016  * Create a new TableRow
9017  * @param {Object} config The config object
9018  */
9019
9020 Roo.bootstrap.TableRow = function(config){
9021     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9022 };
9023
9024 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9025     
9026     cls: false,
9027     align: false,
9028     bgcolor: false,
9029     charoff: false,
9030     valign: false,
9031     
9032     getAutoCreate : function(){
9033         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9034         
9035         cfg = {
9036             tag: 'tr'
9037         };
9038             
9039         if(this.cls){
9040             cfg.cls = this.cls;
9041         }
9042         if(this.align){
9043             cfg.align = this.align;
9044         }
9045         if(this.bgcolor){
9046             cfg.bgcolor = this.bgcolor;
9047         }
9048         if(this.charoff){
9049             cfg.charoff = this.charoff;
9050         }
9051         if(this.valign){
9052             cfg.valign = this.valign;
9053         }
9054         
9055         return cfg;
9056     }
9057    
9058 });
9059
9060  
9061
9062  /*
9063  * - LGPL
9064  *
9065  * table body
9066  * 
9067  */
9068
9069 /**
9070  * @class Roo.bootstrap.TableBody
9071  * @extends Roo.bootstrap.Component
9072  * Bootstrap TableBody class
9073  * @cfg {String} cls element class
9074  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9075  * @cfg {String} align Aligns the content inside the element
9076  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9077  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9078  * 
9079  * @constructor
9080  * Create a new TableBody
9081  * @param {Object} config The config object
9082  */
9083
9084 Roo.bootstrap.TableBody = function(config){
9085     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9086 };
9087
9088 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9089     
9090     cls: false,
9091     tag: false,
9092     align: false,
9093     charoff: false,
9094     valign: false,
9095     
9096     getAutoCreate : function(){
9097         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9098         
9099         cfg = {
9100             tag: 'tbody'
9101         };
9102             
9103         if (this.cls) {
9104             cfg.cls=this.cls
9105         }
9106         if(this.tag){
9107             cfg.tag = this.tag;
9108         }
9109         
9110         if(this.align){
9111             cfg.align = this.align;
9112         }
9113         if(this.charoff){
9114             cfg.charoff = this.charoff;
9115         }
9116         if(this.valign){
9117             cfg.valign = this.valign;
9118         }
9119         
9120         return cfg;
9121     }
9122     
9123     
9124 //    initEvents : function()
9125 //    {
9126 //        
9127 //        if(!this.store){
9128 //            return;
9129 //        }
9130 //        
9131 //        this.store = Roo.factory(this.store, Roo.data);
9132 //        this.store.on('load', this.onLoad, this);
9133 //        
9134 //        this.store.load();
9135 //        
9136 //    },
9137 //    
9138 //    onLoad: function () 
9139 //    {   
9140 //        this.fireEvent('load', this);
9141 //    }
9142 //    
9143 //   
9144 });
9145
9146  
9147
9148  /*
9149  * Based on:
9150  * Ext JS Library 1.1.1
9151  * Copyright(c) 2006-2007, Ext JS, LLC.
9152  *
9153  * Originally Released Under LGPL - original licence link has changed is not relivant.
9154  *
9155  * Fork - LGPL
9156  * <script type="text/javascript">
9157  */
9158
9159 // as we use this in bootstrap.
9160 Roo.namespace('Roo.form');
9161  /**
9162  * @class Roo.form.Action
9163  * Internal Class used to handle form actions
9164  * @constructor
9165  * @param {Roo.form.BasicForm} el The form element or its id
9166  * @param {Object} config Configuration options
9167  */
9168
9169  
9170  
9171 // define the action interface
9172 Roo.form.Action = function(form, options){
9173     this.form = form;
9174     this.options = options || {};
9175 };
9176 /**
9177  * Client Validation Failed
9178  * @const 
9179  */
9180 Roo.form.Action.CLIENT_INVALID = 'client';
9181 /**
9182  * Server Validation Failed
9183  * @const 
9184  */
9185 Roo.form.Action.SERVER_INVALID = 'server';
9186  /**
9187  * Connect to Server Failed
9188  * @const 
9189  */
9190 Roo.form.Action.CONNECT_FAILURE = 'connect';
9191 /**
9192  * Reading Data from Server Failed
9193  * @const 
9194  */
9195 Roo.form.Action.LOAD_FAILURE = 'load';
9196
9197 Roo.form.Action.prototype = {
9198     type : 'default',
9199     failureType : undefined,
9200     response : undefined,
9201     result : undefined,
9202
9203     // interface method
9204     run : function(options){
9205
9206     },
9207
9208     // interface method
9209     success : function(response){
9210
9211     },
9212
9213     // interface method
9214     handleResponse : function(response){
9215
9216     },
9217
9218     // default connection failure
9219     failure : function(response){
9220         
9221         this.response = response;
9222         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9223         this.form.afterAction(this, false);
9224     },
9225
9226     processResponse : function(response){
9227         this.response = response;
9228         if(!response.responseText){
9229             return true;
9230         }
9231         this.result = this.handleResponse(response);
9232         return this.result;
9233     },
9234
9235     // utility functions used internally
9236     getUrl : function(appendParams){
9237         var url = this.options.url || this.form.url || this.form.el.dom.action;
9238         if(appendParams){
9239             var p = this.getParams();
9240             if(p){
9241                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9242             }
9243         }
9244         return url;
9245     },
9246
9247     getMethod : function(){
9248         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9249     },
9250
9251     getParams : function(){
9252         var bp = this.form.baseParams;
9253         var p = this.options.params;
9254         if(p){
9255             if(typeof p == "object"){
9256                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9257             }else if(typeof p == 'string' && bp){
9258                 p += '&' + Roo.urlEncode(bp);
9259             }
9260         }else if(bp){
9261             p = Roo.urlEncode(bp);
9262         }
9263         return p;
9264     },
9265
9266     createCallback : function(){
9267         return {
9268             success: this.success,
9269             failure: this.failure,
9270             scope: this,
9271             timeout: (this.form.timeout*1000),
9272             upload: this.form.fileUpload ? this.success : undefined
9273         };
9274     }
9275 };
9276
9277 Roo.form.Action.Submit = function(form, options){
9278     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9279 };
9280
9281 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9282     type : 'submit',
9283
9284     haveProgress : false,
9285     uploadComplete : false,
9286     
9287     // uploadProgress indicator.
9288     uploadProgress : function()
9289     {
9290         if (!this.form.progressUrl) {
9291             return;
9292         }
9293         
9294         if (!this.haveProgress) {
9295             Roo.MessageBox.progress("Uploading", "Uploading");
9296         }
9297         if (this.uploadComplete) {
9298            Roo.MessageBox.hide();
9299            return;
9300         }
9301         
9302         this.haveProgress = true;
9303    
9304         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9305         
9306         var c = new Roo.data.Connection();
9307         c.request({
9308             url : this.form.progressUrl,
9309             params: {
9310                 id : uid
9311             },
9312             method: 'GET',
9313             success : function(req){
9314                //console.log(data);
9315                 var rdata = false;
9316                 var edata;
9317                 try  {
9318                    rdata = Roo.decode(req.responseText)
9319                 } catch (e) {
9320                     Roo.log("Invalid data from server..");
9321                     Roo.log(edata);
9322                     return;
9323                 }
9324                 if (!rdata || !rdata.success) {
9325                     Roo.log(rdata);
9326                     Roo.MessageBox.alert(Roo.encode(rdata));
9327                     return;
9328                 }
9329                 var data = rdata.data;
9330                 
9331                 if (this.uploadComplete) {
9332                    Roo.MessageBox.hide();
9333                    return;
9334                 }
9335                    
9336                 if (data){
9337                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9338                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9339                     );
9340                 }
9341                 this.uploadProgress.defer(2000,this);
9342             },
9343        
9344             failure: function(data) {
9345                 Roo.log('progress url failed ');
9346                 Roo.log(data);
9347             },
9348             scope : this
9349         });
9350            
9351     },
9352     
9353     
9354     run : function()
9355     {
9356         // run get Values on the form, so it syncs any secondary forms.
9357         this.form.getValues();
9358         
9359         var o = this.options;
9360         var method = this.getMethod();
9361         var isPost = method == 'POST';
9362         if(o.clientValidation === false || this.form.isValid()){
9363             
9364             if (this.form.progressUrl) {
9365                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9366                     (new Date() * 1) + '' + Math.random());
9367                     
9368             } 
9369             
9370             
9371             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9372                 form:this.form.el.dom,
9373                 url:this.getUrl(!isPost),
9374                 method: method,
9375                 params:isPost ? this.getParams() : null,
9376                 isUpload: this.form.fileUpload,
9377                 formData : this.form.formData
9378             }));
9379             
9380             this.uploadProgress();
9381
9382         }else if (o.clientValidation !== false){ // client validation failed
9383             this.failureType = Roo.form.Action.CLIENT_INVALID;
9384             this.form.afterAction(this, false);
9385         }
9386     },
9387
9388     success : function(response)
9389     {
9390         this.uploadComplete= true;
9391         if (this.haveProgress) {
9392             Roo.MessageBox.hide();
9393         }
9394         
9395         
9396         var result = this.processResponse(response);
9397         if(result === true || result.success){
9398             this.form.afterAction(this, true);
9399             return;
9400         }
9401         if(result.errors){
9402             this.form.markInvalid(result.errors);
9403             this.failureType = Roo.form.Action.SERVER_INVALID;
9404         }
9405         this.form.afterAction(this, false);
9406     },
9407     failure : function(response)
9408     {
9409         this.uploadComplete= true;
9410         if (this.haveProgress) {
9411             Roo.MessageBox.hide();
9412         }
9413         
9414         this.response = response;
9415         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9416         this.form.afterAction(this, false);
9417     },
9418     
9419     handleResponse : function(response){
9420         if(this.form.errorReader){
9421             var rs = this.form.errorReader.read(response);
9422             var errors = [];
9423             if(rs.records){
9424                 for(var i = 0, len = rs.records.length; i < len; i++) {
9425                     var r = rs.records[i];
9426                     errors[i] = r.data;
9427                 }
9428             }
9429             if(errors.length < 1){
9430                 errors = null;
9431             }
9432             return {
9433                 success : rs.success,
9434                 errors : errors
9435             };
9436         }
9437         var ret = false;
9438         try {
9439             ret = Roo.decode(response.responseText);
9440         } catch (e) {
9441             ret = {
9442                 success: false,
9443                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9444                 errors : []
9445             };
9446         }
9447         return ret;
9448         
9449     }
9450 });
9451
9452
9453 Roo.form.Action.Load = function(form, options){
9454     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9455     this.reader = this.form.reader;
9456 };
9457
9458 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9459     type : 'load',
9460
9461     run : function(){
9462         
9463         Roo.Ajax.request(Roo.apply(
9464                 this.createCallback(), {
9465                     method:this.getMethod(),
9466                     url:this.getUrl(false),
9467                     params:this.getParams()
9468         }));
9469     },
9470
9471     success : function(response){
9472         
9473         var result = this.processResponse(response);
9474         if(result === true || !result.success || !result.data){
9475             this.failureType = Roo.form.Action.LOAD_FAILURE;
9476             this.form.afterAction(this, false);
9477             return;
9478         }
9479         this.form.clearInvalid();
9480         this.form.setValues(result.data);
9481         this.form.afterAction(this, true);
9482     },
9483
9484     handleResponse : function(response){
9485         if(this.form.reader){
9486             var rs = this.form.reader.read(response);
9487             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9488             return {
9489                 success : rs.success,
9490                 data : data
9491             };
9492         }
9493         return Roo.decode(response.responseText);
9494     }
9495 });
9496
9497 Roo.form.Action.ACTION_TYPES = {
9498     'load' : Roo.form.Action.Load,
9499     'submit' : Roo.form.Action.Submit
9500 };/*
9501  * - LGPL
9502  *
9503  * form
9504  *
9505  */
9506
9507 /**
9508  * @class Roo.bootstrap.Form
9509  * @extends Roo.bootstrap.Component
9510  * Bootstrap Form class
9511  * @cfg {String} method  GET | POST (default POST)
9512  * @cfg {String} labelAlign top | left (default top)
9513  * @cfg {String} align left  | right - for navbars
9514  * @cfg {Boolean} loadMask load mask when submit (default true)
9515
9516  *
9517  * @constructor
9518  * Create a new Form
9519  * @param {Object} config The config object
9520  */
9521
9522
9523 Roo.bootstrap.Form = function(config){
9524     
9525     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9526     
9527     Roo.bootstrap.Form.popover.apply();
9528     
9529     this.addEvents({
9530         /**
9531          * @event clientvalidation
9532          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9533          * @param {Form} this
9534          * @param {Boolean} valid true if the form has passed client-side validation
9535          */
9536         clientvalidation: true,
9537         /**
9538          * @event beforeaction
9539          * Fires before any action is performed. Return false to cancel the action.
9540          * @param {Form} this
9541          * @param {Action} action The action to be performed
9542          */
9543         beforeaction: true,
9544         /**
9545          * @event actionfailed
9546          * Fires when an action fails.
9547          * @param {Form} this
9548          * @param {Action} action The action that failed
9549          */
9550         actionfailed : true,
9551         /**
9552          * @event actioncomplete
9553          * Fires when an action is completed.
9554          * @param {Form} this
9555          * @param {Action} action The action that completed
9556          */
9557         actioncomplete : true
9558     });
9559 };
9560
9561 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9562
9563      /**
9564      * @cfg {String} method
9565      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9566      */
9567     method : 'POST',
9568     /**
9569      * @cfg {String} url
9570      * The URL to use for form actions if one isn't supplied in the action options.
9571      */
9572     /**
9573      * @cfg {Boolean} fileUpload
9574      * Set to true if this form is a file upload.
9575      */
9576
9577     /**
9578      * @cfg {Object} baseParams
9579      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9580      */
9581
9582     /**
9583      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9584      */
9585     timeout: 30,
9586     /**
9587      * @cfg {Sting} align (left|right) for navbar forms
9588      */
9589     align : 'left',
9590
9591     // private
9592     activeAction : null,
9593
9594     /**
9595      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9596      * element by passing it or its id or mask the form itself by passing in true.
9597      * @type Mixed
9598      */
9599     waitMsgTarget : false,
9600
9601     loadMask : true,
9602     
9603     /**
9604      * @cfg {Boolean} errorMask (true|false) default false
9605      */
9606     errorMask : false,
9607     
9608     /**
9609      * @cfg {Number} maskOffset Default 100
9610      */
9611     maskOffset : 100,
9612     
9613     /**
9614      * @cfg {Boolean} maskBody
9615      */
9616     maskBody : false,
9617
9618     getAutoCreate : function(){
9619
9620         var cfg = {
9621             tag: 'form',
9622             method : this.method || 'POST',
9623             id : this.id || Roo.id(),
9624             cls : ''
9625         };
9626         if (this.parent().xtype.match(/^Nav/)) {
9627             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9628
9629         }
9630
9631         if (this.labelAlign == 'left' ) {
9632             cfg.cls += ' form-horizontal';
9633         }
9634
9635
9636         return cfg;
9637     },
9638     initEvents : function()
9639     {
9640         this.el.on('submit', this.onSubmit, this);
9641         // this was added as random key presses on the form where triggering form submit.
9642         this.el.on('keypress', function(e) {
9643             if (e.getCharCode() != 13) {
9644                 return true;
9645             }
9646             // we might need to allow it for textareas.. and some other items.
9647             // check e.getTarget().
9648
9649             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9650                 return true;
9651             }
9652
9653             Roo.log("keypress blocked");
9654
9655             e.preventDefault();
9656             return false;
9657         });
9658         
9659     },
9660     // private
9661     onSubmit : function(e){
9662         e.stopEvent();
9663     },
9664
9665      /**
9666      * Returns true if client-side validation on the form is successful.
9667      * @return Boolean
9668      */
9669     isValid : function(){
9670         var items = this.getItems();
9671         var valid = true;
9672         var target = false;
9673         
9674         items.each(function(f){
9675             
9676             if(f.validate()){
9677                 return;
9678             }
9679             
9680             Roo.log('invalid field: ' + f.name);
9681             
9682             valid = false;
9683
9684             if(!target && f.el.isVisible(true)){
9685                 target = f;
9686             }
9687            
9688         });
9689         
9690         if(this.errorMask && !valid){
9691             Roo.bootstrap.Form.popover.mask(this, target);
9692         }
9693         
9694         return valid;
9695     },
9696     
9697     /**
9698      * Returns true if any fields in this form have changed since their original load.
9699      * @return Boolean
9700      */
9701     isDirty : function(){
9702         var dirty = false;
9703         var items = this.getItems();
9704         items.each(function(f){
9705            if(f.isDirty()){
9706                dirty = true;
9707                return false;
9708            }
9709            return true;
9710         });
9711         return dirty;
9712     },
9713      /**
9714      * Performs a predefined action (submit or load) or custom actions you define on this form.
9715      * @param {String} actionName The name of the action type
9716      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9717      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9718      * accept other config options):
9719      * <pre>
9720 Property          Type             Description
9721 ----------------  ---------------  ----------------------------------------------------------------------------------
9722 url               String           The url for the action (defaults to the form's url)
9723 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9724 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9725 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9726                                    validate the form on the client (defaults to false)
9727      * </pre>
9728      * @return {BasicForm} this
9729      */
9730     doAction : function(action, options){
9731         if(typeof action == 'string'){
9732             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9733         }
9734         if(this.fireEvent('beforeaction', this, action) !== false){
9735             this.beforeAction(action);
9736             action.run.defer(100, action);
9737         }
9738         return this;
9739     },
9740
9741     // private
9742     beforeAction : function(action){
9743         var o = action.options;
9744         
9745         if(this.loadMask){
9746             
9747             if(this.maskBody){
9748                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9749             } else {
9750                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9751             }
9752         }
9753         // not really supported yet.. ??
9754
9755         //if(this.waitMsgTarget === true){
9756         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9757         //}else if(this.waitMsgTarget){
9758         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9759         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760         //}else {
9761         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9762        // }
9763
9764     },
9765
9766     // private
9767     afterAction : function(action, success){
9768         this.activeAction = null;
9769         var o = action.options;
9770
9771         if(this.loadMask){
9772             
9773             if(this.maskBody){
9774                 Roo.get(document.body).unmask();
9775             } else {
9776                 this.el.unmask();
9777             }
9778         }
9779         
9780         //if(this.waitMsgTarget === true){
9781 //            this.el.unmask();
9782         //}else if(this.waitMsgTarget){
9783         //    this.waitMsgTarget.unmask();
9784         //}else{
9785         //    Roo.MessageBox.updateProgress(1);
9786         //    Roo.MessageBox.hide();
9787        // }
9788         //
9789         if(success){
9790             if(o.reset){
9791                 this.reset();
9792             }
9793             Roo.callback(o.success, o.scope, [this, action]);
9794             this.fireEvent('actioncomplete', this, action);
9795
9796         }else{
9797
9798             // failure condition..
9799             // we have a scenario where updates need confirming.
9800             // eg. if a locking scenario exists..
9801             // we look for { errors : { needs_confirm : true }} in the response.
9802             if (
9803                 (typeof(action.result) != 'undefined')  &&
9804                 (typeof(action.result.errors) != 'undefined')  &&
9805                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9806            ){
9807                 var _t = this;
9808                 Roo.log("not supported yet");
9809                  /*
9810
9811                 Roo.MessageBox.confirm(
9812                     "Change requires confirmation",
9813                     action.result.errorMsg,
9814                     function(r) {
9815                         if (r != 'yes') {
9816                             return;
9817                         }
9818                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9819                     }
9820
9821                 );
9822                 */
9823
9824
9825                 return;
9826             }
9827
9828             Roo.callback(o.failure, o.scope, [this, action]);
9829             // show an error message if no failed handler is set..
9830             if (!this.hasListener('actionfailed')) {
9831                 Roo.log("need to add dialog support");
9832                 /*
9833                 Roo.MessageBox.alert("Error",
9834                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9835                         action.result.errorMsg :
9836                         "Saving Failed, please check your entries or try again"
9837                 );
9838                 */
9839             }
9840
9841             this.fireEvent('actionfailed', this, action);
9842         }
9843
9844     },
9845     /**
9846      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9847      * @param {String} id The value to search for
9848      * @return Field
9849      */
9850     findField : function(id){
9851         var items = this.getItems();
9852         var field = items.get(id);
9853         if(!field){
9854              items.each(function(f){
9855                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9856                     field = f;
9857                     return false;
9858                 }
9859                 return true;
9860             });
9861         }
9862         return field || null;
9863     },
9864      /**
9865      * Mark fields in this form invalid in bulk.
9866      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9867      * @return {BasicForm} this
9868      */
9869     markInvalid : function(errors){
9870         if(errors instanceof Array){
9871             for(var i = 0, len = errors.length; i < len; i++){
9872                 var fieldError = errors[i];
9873                 var f = this.findField(fieldError.id);
9874                 if(f){
9875                     f.markInvalid(fieldError.msg);
9876                 }
9877             }
9878         }else{
9879             var field, id;
9880             for(id in errors){
9881                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9882                     field.markInvalid(errors[id]);
9883                 }
9884             }
9885         }
9886         //Roo.each(this.childForms || [], function (f) {
9887         //    f.markInvalid(errors);
9888         //});
9889
9890         return this;
9891     },
9892
9893     /**
9894      * Set values for fields in this form in bulk.
9895      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9896      * @return {BasicForm} this
9897      */
9898     setValues : function(values){
9899         if(values instanceof Array){ // array of objects
9900             for(var i = 0, len = values.length; i < len; i++){
9901                 var v = values[i];
9902                 var f = this.findField(v.id);
9903                 if(f){
9904                     f.setValue(v.value);
9905                     if(this.trackResetOnLoad){
9906                         f.originalValue = f.getValue();
9907                     }
9908                 }
9909             }
9910         }else{ // object hash
9911             var field, id;
9912             for(id in values){
9913                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9914
9915                     if (field.setFromData &&
9916                         field.valueField &&
9917                         field.displayField &&
9918                         // combos' with local stores can
9919                         // be queried via setValue()
9920                         // to set their value..
9921                         (field.store && !field.store.isLocal)
9922                         ) {
9923                         // it's a combo
9924                         var sd = { };
9925                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9926                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9927                         field.setFromData(sd);
9928
9929                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9930                         
9931                         field.setFromData(values);
9932                         
9933                     } else {
9934                         field.setValue(values[id]);
9935                     }
9936
9937
9938                     if(this.trackResetOnLoad){
9939                         field.originalValue = field.getValue();
9940                     }
9941                 }
9942             }
9943         }
9944
9945         //Roo.each(this.childForms || [], function (f) {
9946         //    f.setValues(values);
9947         //});
9948
9949         return this;
9950     },
9951
9952     /**
9953      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9954      * they are returned as an array.
9955      * @param {Boolean} asString
9956      * @return {Object}
9957      */
9958     getValues : function(asString){
9959         //if (this.childForms) {
9960             // copy values from the child forms
9961         //    Roo.each(this.childForms, function (f) {
9962         //        this.setValues(f.getValues());
9963         //    }, this);
9964         //}
9965
9966
9967
9968         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9969         if(asString === true){
9970             return fs;
9971         }
9972         return Roo.urlDecode(fs);
9973     },
9974
9975     /**
9976      * Returns the fields in this form as an object with key/value pairs.
9977      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9978      * @return {Object}
9979      */
9980     getFieldValues : function(with_hidden)
9981     {
9982         var items = this.getItems();
9983         var ret = {};
9984         items.each(function(f){
9985             
9986             if (!f.getName()) {
9987                 return;
9988             }
9989             
9990             var v = f.getValue();
9991             
9992             if (f.inputType =='radio') {
9993                 if (typeof(ret[f.getName()]) == 'undefined') {
9994                     ret[f.getName()] = ''; // empty..
9995                 }
9996
9997                 if (!f.el.dom.checked) {
9998                     return;
9999
10000                 }
10001                 v = f.el.dom.value;
10002
10003             }
10004             
10005             if(f.xtype == 'MoneyField'){
10006                 ret[f.currencyName] = f.getCurrency();
10007             }
10008
10009             // not sure if this supported any more..
10010             if ((typeof(v) == 'object') && f.getRawValue) {
10011                 v = f.getRawValue() ; // dates..
10012             }
10013             // combo boxes where name != hiddenName...
10014             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10015                 ret[f.name] = f.getRawValue();
10016             }
10017             ret[f.getName()] = v;
10018         });
10019
10020         return ret;
10021     },
10022
10023     /**
10024      * Clears all invalid messages in this form.
10025      * @return {BasicForm} this
10026      */
10027     clearInvalid : function(){
10028         var items = this.getItems();
10029
10030         items.each(function(f){
10031            f.clearInvalid();
10032         });
10033
10034         return this;
10035     },
10036
10037     /**
10038      * Resets this form.
10039      * @return {BasicForm} this
10040      */
10041     reset : function(){
10042         var items = this.getItems();
10043         items.each(function(f){
10044             f.reset();
10045         });
10046
10047         Roo.each(this.childForms || [], function (f) {
10048             f.reset();
10049         });
10050
10051
10052         return this;
10053     },
10054     
10055     getItems : function()
10056     {
10057         var r=new Roo.util.MixedCollection(false, function(o){
10058             return o.id || (o.id = Roo.id());
10059         });
10060         var iter = function(el) {
10061             if (el.inputEl) {
10062                 r.add(el);
10063             }
10064             if (!el.items) {
10065                 return;
10066             }
10067             Roo.each(el.items,function(e) {
10068                 iter(e);
10069             });
10070         };
10071
10072         iter(this);
10073         return r;
10074     },
10075     
10076     hideFields : function(items)
10077     {
10078         Roo.each(items, function(i){
10079             
10080             var f = this.findField(i);
10081             
10082             if(!f){
10083                 return;
10084             }
10085             
10086             f.hide();
10087             
10088         }, this);
10089     },
10090     
10091     showFields : function(items)
10092     {
10093         Roo.each(items, function(i){
10094             
10095             var f = this.findField(i);
10096             
10097             if(!f){
10098                 return;
10099             }
10100             
10101             f.show();
10102             
10103         }, this);
10104     }
10105
10106 });
10107
10108 Roo.apply(Roo.bootstrap.Form, {
10109     
10110     popover : {
10111         
10112         padding : 5,
10113         
10114         isApplied : false,
10115         
10116         isMasked : false,
10117         
10118         form : false,
10119         
10120         target : false,
10121         
10122         toolTip : false,
10123         
10124         intervalID : false,
10125         
10126         maskEl : false,
10127         
10128         apply : function()
10129         {
10130             if(this.isApplied){
10131                 return;
10132             }
10133             
10134             this.maskEl = {
10135                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10136                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10137                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10138                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10139             };
10140             
10141             this.maskEl.top.enableDisplayMode("block");
10142             this.maskEl.left.enableDisplayMode("block");
10143             this.maskEl.bottom.enableDisplayMode("block");
10144             this.maskEl.right.enableDisplayMode("block");
10145             
10146             this.toolTip = new Roo.bootstrap.Tooltip({
10147                 cls : 'roo-form-error-popover',
10148                 alignment : {
10149                     'left' : ['r-l', [-2,0], 'right'],
10150                     'right' : ['l-r', [2,0], 'left'],
10151                     'bottom' : ['tl-bl', [0,2], 'top'],
10152                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10153                 }
10154             });
10155             
10156             this.toolTip.render(Roo.get(document.body));
10157
10158             this.toolTip.el.enableDisplayMode("block");
10159             
10160             Roo.get(document.body).on('click', function(){
10161                 this.unmask();
10162             }, this);
10163             
10164             Roo.get(document.body).on('touchstart', function(){
10165                 this.unmask();
10166             }, this);
10167             
10168             this.isApplied = true
10169         },
10170         
10171         mask : function(form, target)
10172         {
10173             this.form = form;
10174             
10175             this.target = target;
10176             
10177             if(!this.form.errorMask || !target.el){
10178                 return;
10179             }
10180             
10181             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10182             
10183             Roo.log(scrollable);
10184             
10185             var ot = this.target.el.calcOffsetsTo(scrollable);
10186             
10187             var scrollTo = ot[1] - this.form.maskOffset;
10188             
10189             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10190             
10191             scrollable.scrollTo('top', scrollTo);
10192             
10193             var box = this.target.el.getBox();
10194             Roo.log(box);
10195             var zIndex = Roo.bootstrap.Modal.zIndex++;
10196
10197             
10198             this.maskEl.top.setStyle('position', 'absolute');
10199             this.maskEl.top.setStyle('z-index', zIndex);
10200             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10201             this.maskEl.top.setLeft(0);
10202             this.maskEl.top.setTop(0);
10203             this.maskEl.top.show();
10204             
10205             this.maskEl.left.setStyle('position', 'absolute');
10206             this.maskEl.left.setStyle('z-index', zIndex);
10207             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10208             this.maskEl.left.setLeft(0);
10209             this.maskEl.left.setTop(box.y - this.padding);
10210             this.maskEl.left.show();
10211
10212             this.maskEl.bottom.setStyle('position', 'absolute');
10213             this.maskEl.bottom.setStyle('z-index', zIndex);
10214             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10215             this.maskEl.bottom.setLeft(0);
10216             this.maskEl.bottom.setTop(box.bottom + this.padding);
10217             this.maskEl.bottom.show();
10218
10219             this.maskEl.right.setStyle('position', 'absolute');
10220             this.maskEl.right.setStyle('z-index', zIndex);
10221             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10222             this.maskEl.right.setLeft(box.right + this.padding);
10223             this.maskEl.right.setTop(box.y - this.padding);
10224             this.maskEl.right.show();
10225
10226             this.toolTip.bindEl = this.target.el;
10227
10228             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10229
10230             var tip = this.target.blankText;
10231
10232             if(this.target.getValue() !== '' ) {
10233                 
10234                 if (this.target.invalidText.length) {
10235                     tip = this.target.invalidText;
10236                 } else if (this.target.regexText.length){
10237                     tip = this.target.regexText;
10238                 }
10239             }
10240
10241             this.toolTip.show(tip);
10242
10243             this.intervalID = window.setInterval(function() {
10244                 Roo.bootstrap.Form.popover.unmask();
10245             }, 10000);
10246
10247             window.onwheel = function(){ return false;};
10248             
10249             (function(){ this.isMasked = true; }).defer(500, this);
10250             
10251         },
10252         
10253         unmask : function()
10254         {
10255             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10256                 return;
10257             }
10258             
10259             this.maskEl.top.setStyle('position', 'absolute');
10260             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10261             this.maskEl.top.hide();
10262
10263             this.maskEl.left.setStyle('position', 'absolute');
10264             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10265             this.maskEl.left.hide();
10266
10267             this.maskEl.bottom.setStyle('position', 'absolute');
10268             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10269             this.maskEl.bottom.hide();
10270
10271             this.maskEl.right.setStyle('position', 'absolute');
10272             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10273             this.maskEl.right.hide();
10274             
10275             this.toolTip.hide();
10276             
10277             this.toolTip.el.hide();
10278             
10279             window.onwheel = function(){ return true;};
10280             
10281             if(this.intervalID){
10282                 window.clearInterval(this.intervalID);
10283                 this.intervalID = false;
10284             }
10285             
10286             this.isMasked = false;
10287             
10288         }
10289         
10290     }
10291     
10292 });
10293
10294 /*
10295  * Based on:
10296  * Ext JS Library 1.1.1
10297  * Copyright(c) 2006-2007, Ext JS, LLC.
10298  *
10299  * Originally Released Under LGPL - original licence link has changed is not relivant.
10300  *
10301  * Fork - LGPL
10302  * <script type="text/javascript">
10303  */
10304 /**
10305  * @class Roo.form.VTypes
10306  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10307  * @singleton
10308  */
10309 Roo.form.VTypes = function(){
10310     // closure these in so they are only created once.
10311     var alpha = /^[a-zA-Z_]+$/;
10312     var alphanum = /^[a-zA-Z0-9_]+$/;
10313     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10314     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10315
10316     // All these messages and functions are configurable
10317     return {
10318         /**
10319          * The function used to validate email addresses
10320          * @param {String} value The email address
10321          */
10322         'email' : function(v){
10323             return email.test(v);
10324         },
10325         /**
10326          * The error text to display when the email validation function returns false
10327          * @type String
10328          */
10329         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10330         /**
10331          * The keystroke filter mask to be applied on email input
10332          * @type RegExp
10333          */
10334         'emailMask' : /[a-z0-9_\.\-@]/i,
10335
10336         /**
10337          * The function used to validate URLs
10338          * @param {String} value The URL
10339          */
10340         'url' : function(v){
10341             return url.test(v);
10342         },
10343         /**
10344          * The error text to display when the url validation function returns false
10345          * @type String
10346          */
10347         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10348         
10349         /**
10350          * The function used to validate alpha values
10351          * @param {String} value The value
10352          */
10353         'alpha' : function(v){
10354             return alpha.test(v);
10355         },
10356         /**
10357          * The error text to display when the alpha validation function returns false
10358          * @type String
10359          */
10360         'alphaText' : 'This field should only contain letters and _',
10361         /**
10362          * The keystroke filter mask to be applied on alpha input
10363          * @type RegExp
10364          */
10365         'alphaMask' : /[a-z_]/i,
10366
10367         /**
10368          * The function used to validate alphanumeric values
10369          * @param {String} value The value
10370          */
10371         'alphanum' : function(v){
10372             return alphanum.test(v);
10373         },
10374         /**
10375          * The error text to display when the alphanumeric validation function returns false
10376          * @type String
10377          */
10378         'alphanumText' : 'This field should only contain letters, numbers and _',
10379         /**
10380          * The keystroke filter mask to be applied on alphanumeric input
10381          * @type RegExp
10382          */
10383         'alphanumMask' : /[a-z0-9_]/i
10384     };
10385 }();/*
10386  * - LGPL
10387  *
10388  * Input
10389  * 
10390  */
10391
10392 /**
10393  * @class Roo.bootstrap.Input
10394  * @extends Roo.bootstrap.Component
10395  * Bootstrap Input class
10396  * @cfg {Boolean} disabled is it disabled
10397  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10398  * @cfg {String} name name of the input
10399  * @cfg {string} fieldLabel - the label associated
10400  * @cfg {string} placeholder - placeholder to put in text.
10401  * @cfg {string}  before - input group add on before
10402  * @cfg {string} after - input group add on after
10403  * @cfg {string} size - (lg|sm) or leave empty..
10404  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10405  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10406  * @cfg {Number} md colspan out of 12 for computer-sized screens
10407  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10408  * @cfg {string} value default value of the input
10409  * @cfg {Number} labelWidth set the width of label 
10410  * @cfg {Number} labellg set the width of label (1-12)
10411  * @cfg {Number} labelmd set the width of label (1-12)
10412  * @cfg {Number} labelsm set the width of label (1-12)
10413  * @cfg {Number} labelxs set the width of label (1-12)
10414  * @cfg {String} labelAlign (top|left)
10415  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10416  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10417  * @cfg {String} indicatorpos (left|right) default left
10418  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10419  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10420  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10421
10422  * @cfg {String} align (left|center|right) Default left
10423  * @cfg {Boolean} forceFeedback (true|false) Default false
10424  * 
10425  * @constructor
10426  * Create a new Input
10427  * @param {Object} config The config object
10428  */
10429
10430 Roo.bootstrap.Input = function(config){
10431     
10432     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10433     
10434     this.addEvents({
10435         /**
10436          * @event focus
10437          * Fires when this field receives input focus.
10438          * @param {Roo.form.Field} this
10439          */
10440         focus : true,
10441         /**
10442          * @event blur
10443          * Fires when this field loses input focus.
10444          * @param {Roo.form.Field} this
10445          */
10446         blur : true,
10447         /**
10448          * @event specialkey
10449          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10450          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10451          * @param {Roo.form.Field} this
10452          * @param {Roo.EventObject} e The event object
10453          */
10454         specialkey : true,
10455         /**
10456          * @event change
10457          * Fires just before the field blurs if the field value has changed.
10458          * @param {Roo.form.Field} this
10459          * @param {Mixed} newValue The new value
10460          * @param {Mixed} oldValue The original value
10461          */
10462         change : true,
10463         /**
10464          * @event invalid
10465          * Fires after the field has been marked as invalid.
10466          * @param {Roo.form.Field} this
10467          * @param {String} msg The validation message
10468          */
10469         invalid : true,
10470         /**
10471          * @event valid
10472          * Fires after the field has been validated with no errors.
10473          * @param {Roo.form.Field} this
10474          */
10475         valid : true,
10476          /**
10477          * @event keyup
10478          * Fires after the key up
10479          * @param {Roo.form.Field} this
10480          * @param {Roo.EventObject}  e The event Object
10481          */
10482         keyup : true
10483     });
10484 };
10485
10486 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10487      /**
10488      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10489       automatic validation (defaults to "keyup").
10490      */
10491     validationEvent : "keyup",
10492      /**
10493      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10494      */
10495     validateOnBlur : true,
10496     /**
10497      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10498      */
10499     validationDelay : 250,
10500      /**
10501      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10502      */
10503     focusClass : "x-form-focus",  // not needed???
10504     
10505        
10506     /**
10507      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10508      */
10509     invalidClass : "has-warning",
10510     
10511     /**
10512      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10513      */
10514     validClass : "has-success",
10515     
10516     /**
10517      * @cfg {Boolean} hasFeedback (true|false) default true
10518      */
10519     hasFeedback : true,
10520     
10521     /**
10522      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10523      */
10524     invalidFeedbackClass : "glyphicon-warning-sign",
10525     
10526     /**
10527      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10528      */
10529     validFeedbackClass : "glyphicon-ok",
10530     
10531     /**
10532      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10533      */
10534     selectOnFocus : false,
10535     
10536      /**
10537      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10538      */
10539     maskRe : null,
10540        /**
10541      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10542      */
10543     vtype : null,
10544     
10545       /**
10546      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10547      */
10548     disableKeyFilter : false,
10549     
10550        /**
10551      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10552      */
10553     disabled : false,
10554      /**
10555      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10556      */
10557     allowBlank : true,
10558     /**
10559      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10560      */
10561     blankText : "Please complete this mandatory field",
10562     
10563      /**
10564      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10565      */
10566     minLength : 0,
10567     /**
10568      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10569      */
10570     maxLength : Number.MAX_VALUE,
10571     /**
10572      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10573      */
10574     minLengthText : "The minimum length for this field is {0}",
10575     /**
10576      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10577      */
10578     maxLengthText : "The maximum length for this field is {0}",
10579   
10580     
10581     /**
10582      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10583      * If available, this function will be called only after the basic validators all return true, and will be passed the
10584      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10585      */
10586     validator : null,
10587     /**
10588      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10589      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10590      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10591      */
10592     regex : null,
10593     /**
10594      * @cfg {String} regexText -- Depricated - use Invalid Text
10595      */
10596     regexText : "",
10597     
10598     /**
10599      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10600      */
10601     invalidText : "",
10602     
10603     
10604     
10605     autocomplete: false,
10606     
10607     
10608     fieldLabel : '',
10609     inputType : 'text',
10610     
10611     name : false,
10612     placeholder: false,
10613     before : false,
10614     after : false,
10615     size : false,
10616     hasFocus : false,
10617     preventMark: false,
10618     isFormField : true,
10619     value : '',
10620     labelWidth : 2,
10621     labelAlign : false,
10622     readOnly : false,
10623     align : false,
10624     formatedValue : false,
10625     forceFeedback : false,
10626     
10627     indicatorpos : 'left',
10628     
10629     labellg : 0,
10630     labelmd : 0,
10631     labelsm : 0,
10632     labelxs : 0,
10633     
10634     capture : '',
10635     accept : '',
10636     
10637     parentLabelAlign : function()
10638     {
10639         var parent = this;
10640         while (parent.parent()) {
10641             parent = parent.parent();
10642             if (typeof(parent.labelAlign) !='undefined') {
10643                 return parent.labelAlign;
10644             }
10645         }
10646         return 'left';
10647         
10648     },
10649     
10650     getAutoCreate : function()
10651     {
10652         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10653         
10654         var id = Roo.id();
10655         
10656         var cfg = {};
10657         
10658         if(this.inputType != 'hidden'){
10659             cfg.cls = 'form-group' //input-group
10660         }
10661         
10662         var input =  {
10663             tag: 'input',
10664             id : id,
10665             type : this.inputType,
10666             value : this.value,
10667             cls : 'form-control',
10668             placeholder : this.placeholder || '',
10669             autocomplete : this.autocomplete || 'new-password'
10670         };
10671         if (this.inputType == 'file') {
10672             input.style = 'overflow:hidden'; // why not in CSS?
10673         }
10674         
10675         if(this.capture.length){
10676             input.capture = this.capture;
10677         }
10678         
10679         if(this.accept.length){
10680             input.accept = this.accept + "/*";
10681         }
10682         
10683         if(this.align){
10684             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10685         }
10686         
10687         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10688             input.maxLength = this.maxLength;
10689         }
10690         
10691         if (this.disabled) {
10692             input.disabled=true;
10693         }
10694         
10695         if (this.readOnly) {
10696             input.readonly=true;
10697         }
10698         
10699         if (this.name) {
10700             input.name = this.name;
10701         }
10702         
10703         if (this.size) {
10704             input.cls += ' input-' + this.size;
10705         }
10706         
10707         var settings=this;
10708         ['xs','sm','md','lg'].map(function(size){
10709             if (settings[size]) {
10710                 cfg.cls += ' col-' + size + '-' + settings[size];
10711             }
10712         });
10713         
10714         var inputblock = input;
10715         
10716         var feedback = {
10717             tag: 'span',
10718             cls: 'glyphicon form-control-feedback'
10719         };
10720             
10721         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10722             
10723             inputblock = {
10724                 cls : 'has-feedback',
10725                 cn :  [
10726                     input,
10727                     feedback
10728                 ] 
10729             };  
10730         }
10731         
10732         if (this.before || this.after) {
10733             
10734             inputblock = {
10735                 cls : 'input-group',
10736                 cn :  [] 
10737             };
10738             
10739             if (this.before && typeof(this.before) == 'string') {
10740                 
10741                 inputblock.cn.push({
10742                     tag :'span',
10743                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10744                     html : this.before
10745                 });
10746             }
10747             if (this.before && typeof(this.before) == 'object') {
10748                 this.before = Roo.factory(this.before);
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-prepend   input-group-' +
10753                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10754                 });
10755             }
10756             
10757             inputblock.cn.push(input);
10758             
10759             if (this.after && typeof(this.after) == 'string') {
10760                 inputblock.cn.push({
10761                     tag :'span',
10762                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10763                     html : this.after
10764                 });
10765             }
10766             if (this.after && typeof(this.after) == 'object') {
10767                 this.after = Roo.factory(this.after);
10768                 
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append  input-group-' +
10772                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10773                 });
10774             }
10775             
10776             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10777                 inputblock.cls += ' has-feedback';
10778                 inputblock.cn.push(feedback);
10779             }
10780         };
10781         var indicator = {
10782             tag : 'i',
10783             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10784             tooltip : 'This field is required'
10785         };
10786         if (this.allowBlank ) {
10787             indicator.style = this.allowBlank ? ' display:none' : '';
10788         }
10789         if (align ==='left' && this.fieldLabel.length) {
10790             
10791             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10792             
10793             cfg.cn = [
10794                 indicator,
10795                 {
10796                     tag: 'label',
10797                     'for' :  id,
10798                     cls : 'control-label col-form-label',
10799                     html : this.fieldLabel
10800
10801                 },
10802                 {
10803                     cls : "", 
10804                     cn: [
10805                         inputblock
10806                     ]
10807                 }
10808             ];
10809             
10810             var labelCfg = cfg.cn[1];
10811             var contentCfg = cfg.cn[2];
10812             
10813             if(this.indicatorpos == 'right'){
10814                 cfg.cn = [
10815                     {
10816                         tag: 'label',
10817                         'for' :  id,
10818                         cls : 'control-label col-form-label',
10819                         cn : [
10820                             {
10821                                 tag : 'span',
10822                                 html : this.fieldLabel
10823                             },
10824                             indicator
10825                         ]
10826                     },
10827                     {
10828                         cls : "",
10829                         cn: [
10830                             inputblock
10831                         ]
10832                     }
10833
10834                 ];
10835                 
10836                 labelCfg = cfg.cn[0];
10837                 contentCfg = cfg.cn[1];
10838             
10839             }
10840             
10841             if(this.labelWidth > 12){
10842                 labelCfg.style = "width: " + this.labelWidth + 'px';
10843             }
10844             
10845             if(this.labelWidth < 13 && this.labelmd == 0){
10846                 this.labelmd = this.labelWidth;
10847             }
10848             
10849             if(this.labellg > 0){
10850                 labelCfg.cls += ' col-lg-' + this.labellg;
10851                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10852             }
10853             
10854             if(this.labelmd > 0){
10855                 labelCfg.cls += ' col-md-' + this.labelmd;
10856                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10857             }
10858             
10859             if(this.labelsm > 0){
10860                 labelCfg.cls += ' col-sm-' + this.labelsm;
10861                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10862             }
10863             
10864             if(this.labelxs > 0){
10865                 labelCfg.cls += ' col-xs-' + this.labelxs;
10866                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10867             }
10868             
10869             
10870         } else if ( this.fieldLabel.length) {
10871                 
10872             
10873             
10874             cfg.cn = [
10875                 {
10876                     tag : 'i',
10877                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10878                     tooltip : 'This field is required',
10879                     style : this.allowBlank ? ' display:none' : '' 
10880                 },
10881                 {
10882                     tag: 'label',
10883                    //cls : 'input-group-addon',
10884                     html : this.fieldLabel
10885
10886                 },
10887
10888                inputblock
10889
10890            ];
10891            
10892            if(this.indicatorpos == 'right'){
10893        
10894                 cfg.cn = [
10895                     {
10896                         tag: 'label',
10897                        //cls : 'input-group-addon',
10898                         html : this.fieldLabel
10899
10900                     },
10901                     {
10902                         tag : 'i',
10903                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10904                         tooltip : 'This field is required',
10905                         style : this.allowBlank ? ' display:none' : '' 
10906                     },
10907
10908                    inputblock
10909
10910                ];
10911
10912             }
10913
10914         } else {
10915             
10916             cfg.cn = [
10917
10918                     inputblock
10919
10920             ];
10921                 
10922                 
10923         };
10924         
10925         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10926            cfg.cls += ' navbar-form';
10927         }
10928         
10929         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10930             // on BS4 we do this only if not form 
10931             cfg.cls += ' navbar-form';
10932             cfg.tag = 'li';
10933         }
10934         
10935         return cfg;
10936         
10937     },
10938     /**
10939      * return the real input element.
10940      */
10941     inputEl: function ()
10942     {
10943         return this.el.select('input.form-control',true).first();
10944     },
10945     
10946     tooltipEl : function()
10947     {
10948         return this.inputEl();
10949     },
10950     
10951     indicatorEl : function()
10952     {
10953         if (Roo.bootstrap.version == 4) {
10954             return false; // not enabled in v4 yet.
10955         }
10956         
10957         var indicator = this.el.select('i.roo-required-indicator',true).first();
10958         
10959         if(!indicator){
10960             return false;
10961         }
10962         
10963         return indicator;
10964         
10965     },
10966     
10967     setDisabled : function(v)
10968     {
10969         var i  = this.inputEl().dom;
10970         if (!v) {
10971             i.removeAttribute('disabled');
10972             return;
10973             
10974         }
10975         i.setAttribute('disabled','true');
10976     },
10977     initEvents : function()
10978     {
10979           
10980         this.inputEl().on("keydown" , this.fireKey,  this);
10981         this.inputEl().on("focus", this.onFocus,  this);
10982         this.inputEl().on("blur", this.onBlur,  this);
10983         
10984         this.inputEl().relayEvent('keyup', this);
10985         
10986         this.indicator = this.indicatorEl();
10987         
10988         if(this.indicator){
10989             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10990         }
10991  
10992         // reference to original value for reset
10993         this.originalValue = this.getValue();
10994         //Roo.form.TextField.superclass.initEvents.call(this);
10995         if(this.validationEvent == 'keyup'){
10996             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10997             this.inputEl().on('keyup', this.filterValidation, this);
10998         }
10999         else if(this.validationEvent !== false){
11000             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11001         }
11002         
11003         if(this.selectOnFocus){
11004             this.on("focus", this.preFocus, this);
11005             
11006         }
11007         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11008             this.inputEl().on("keypress", this.filterKeys, this);
11009         } else {
11010             this.inputEl().relayEvent('keypress', this);
11011         }
11012        /* if(this.grow){
11013             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11014             this.el.on("click", this.autoSize,  this);
11015         }
11016         */
11017         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11018             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11019         }
11020         
11021         if (typeof(this.before) == 'object') {
11022             this.before.render(this.el.select('.roo-input-before',true).first());
11023         }
11024         if (typeof(this.after) == 'object') {
11025             this.after.render(this.el.select('.roo-input-after',true).first());
11026         }
11027         
11028         this.inputEl().on('change', this.onChange, this);
11029         
11030     },
11031     filterValidation : function(e){
11032         if(!e.isNavKeyPress()){
11033             this.validationTask.delay(this.validationDelay);
11034         }
11035     },
11036      /**
11037      * Validates the field value
11038      * @return {Boolean} True if the value is valid, else false
11039      */
11040     validate : function(){
11041         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11042         if(this.disabled || this.validateValue(this.getRawValue())){
11043             this.markValid();
11044             return true;
11045         }
11046         
11047         this.markInvalid();
11048         return false;
11049     },
11050     
11051     
11052     /**
11053      * Validates a value according to the field's validation rules and marks the field as invalid
11054      * if the validation fails
11055      * @param {Mixed} value The value to validate
11056      * @return {Boolean} True if the value is valid, else false
11057      */
11058     validateValue : function(value)
11059     {
11060         if(this.getVisibilityEl().hasClass('hidden')){
11061             return true;
11062         }
11063         
11064         if(value.length < 1)  { // if it's blank
11065             if(this.allowBlank){
11066                 return true;
11067             }
11068             return false;
11069         }
11070         
11071         if(value.length < this.minLength){
11072             return false;
11073         }
11074         if(value.length > this.maxLength){
11075             return false;
11076         }
11077         if(this.vtype){
11078             var vt = Roo.form.VTypes;
11079             if(!vt[this.vtype](value, this)){
11080                 return false;
11081             }
11082         }
11083         if(typeof this.validator == "function"){
11084             var msg = this.validator(value);
11085             if(msg !== true){
11086                 return false;
11087             }
11088             if (typeof(msg) == 'string') {
11089                 this.invalidText = msg;
11090             }
11091         }
11092         
11093         if(this.regex && !this.regex.test(value)){
11094             return false;
11095         }
11096         
11097         return true;
11098     },
11099     
11100      // private
11101     fireKey : function(e){
11102         //Roo.log('field ' + e.getKey());
11103         if(e.isNavKeyPress()){
11104             this.fireEvent("specialkey", this, e);
11105         }
11106     },
11107     focus : function (selectText){
11108         if(this.rendered){
11109             this.inputEl().focus();
11110             if(selectText === true){
11111                 this.inputEl().dom.select();
11112             }
11113         }
11114         return this;
11115     } ,
11116     
11117     onFocus : function(){
11118         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11119            // this.el.addClass(this.focusClass);
11120         }
11121         if(!this.hasFocus){
11122             this.hasFocus = true;
11123             this.startValue = this.getValue();
11124             this.fireEvent("focus", this);
11125         }
11126     },
11127     
11128     beforeBlur : Roo.emptyFn,
11129
11130     
11131     // private
11132     onBlur : function(){
11133         this.beforeBlur();
11134         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11135             //this.el.removeClass(this.focusClass);
11136         }
11137         this.hasFocus = false;
11138         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11139             this.validate();
11140         }
11141         var v = this.getValue();
11142         if(String(v) !== String(this.startValue)){
11143             this.fireEvent('change', this, v, this.startValue);
11144         }
11145         this.fireEvent("blur", this);
11146     },
11147     
11148     onChange : function(e)
11149     {
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         
11155     },
11156     
11157     /**
11158      * Resets the current field value to the originally loaded value and clears any validation messages
11159      */
11160     reset : function(){
11161         this.setValue(this.originalValue);
11162         this.validate();
11163     },
11164      /**
11165      * Returns the name of the field
11166      * @return {Mixed} name The name field
11167      */
11168     getName: function(){
11169         return this.name;
11170     },
11171      /**
11172      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11173      * @return {Mixed} value The field value
11174      */
11175     getValue : function(){
11176         
11177         var v = this.inputEl().getValue();
11178         
11179         return v;
11180     },
11181     /**
11182      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11183      * @return {Mixed} value The field value
11184      */
11185     getRawValue : function(){
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     
11191     /**
11192      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11193      * @param {Mixed} value The value to set
11194      */
11195     setRawValue : function(v){
11196         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11197     },
11198     
11199     selectText : function(start, end){
11200         var v = this.getRawValue();
11201         if(v.length > 0){
11202             start = start === undefined ? 0 : start;
11203             end = end === undefined ? v.length : end;
11204             var d = this.inputEl().dom;
11205             if(d.setSelectionRange){
11206                 d.setSelectionRange(start, end);
11207             }else if(d.createTextRange){
11208                 var range = d.createTextRange();
11209                 range.moveStart("character", start);
11210                 range.moveEnd("character", v.length-end);
11211                 range.select();
11212             }
11213         }
11214     },
11215     
11216     /**
11217      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11218      * @param {Mixed} value The value to set
11219      */
11220     setValue : function(v){
11221         this.value = v;
11222         if(this.rendered){
11223             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11224             this.validate();
11225         }
11226     },
11227     
11228     /*
11229     processValue : function(value){
11230         if(this.stripCharsRe){
11231             var newValue = value.replace(this.stripCharsRe, '');
11232             if(newValue !== value){
11233                 this.setRawValue(newValue);
11234                 return newValue;
11235             }
11236         }
11237         return value;
11238     },
11239   */
11240     preFocus : function(){
11241         
11242         if(this.selectOnFocus){
11243             this.inputEl().dom.select();
11244         }
11245     },
11246     filterKeys : function(e){
11247         var k = e.getKey();
11248         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11249             return;
11250         }
11251         var c = e.getCharCode(), cc = String.fromCharCode(c);
11252         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11253             return;
11254         }
11255         if(!this.maskRe.test(cc)){
11256             e.stopEvent();
11257         }
11258     },
11259      /**
11260      * Clear any invalid styles/messages for this field
11261      */
11262     clearInvalid : function(){
11263         
11264         if(!this.el || this.preventMark){ // not rendered
11265             return;
11266         }
11267         
11268         
11269         this.el.removeClass([this.invalidClass, 'is-invalid']);
11270         
11271         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11272             
11273             var feedback = this.el.select('.form-control-feedback', true).first();
11274             
11275             if(feedback){
11276                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11277             }
11278             
11279         }
11280         
11281         if(this.indicator){
11282             this.indicator.removeClass('visible');
11283             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11284         }
11285         
11286         this.fireEvent('valid', this);
11287     },
11288     
11289      /**
11290      * Mark this field as valid
11291      */
11292     markValid : function()
11293     {
11294         if(!this.el  || this.preventMark){ // not rendered...
11295             return;
11296         }
11297         
11298         this.el.removeClass([this.invalidClass, this.validClass]);
11299         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11300
11301         var feedback = this.el.select('.form-control-feedback', true).first();
11302             
11303         if(feedback){
11304             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11305         }
11306         
11307         if(this.indicator){
11308             this.indicator.removeClass('visible');
11309             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11310         }
11311         
11312         if(this.disabled){
11313             return;
11314         }
11315         
11316            
11317         if(this.allowBlank && !this.getRawValue().length){
11318             return;
11319         }
11320         if (Roo.bootstrap.version == 3) {
11321             this.el.addClass(this.validClass);
11322         } else {
11323             this.inputEl().addClass('is-valid');
11324         }
11325
11326         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11327             
11328             var feedback = this.el.select('.form-control-feedback', true).first();
11329             
11330             if(feedback){
11331                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11332                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11333             }
11334             
11335         }
11336         
11337         this.fireEvent('valid', this);
11338     },
11339     
11340      /**
11341      * Mark this field as invalid
11342      * @param {String} msg The validation message
11343      */
11344     markInvalid : function(msg)
11345     {
11346         if(!this.el  || this.preventMark){ // not rendered
11347             return;
11348         }
11349         
11350         this.el.removeClass([this.invalidClass, this.validClass]);
11351         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11352         
11353         var feedback = this.el.select('.form-control-feedback', true).first();
11354             
11355         if(feedback){
11356             this.el.select('.form-control-feedback', true).first().removeClass(
11357                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11358         }
11359
11360         if(this.disabled){
11361             return;
11362         }
11363         
11364         if(this.allowBlank && !this.getRawValue().length){
11365             return;
11366         }
11367         
11368         if(this.indicator){
11369             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11370             this.indicator.addClass('visible');
11371         }
11372         if (Roo.bootstrap.version == 3) {
11373             this.el.addClass(this.invalidClass);
11374         } else {
11375             this.inputEl().addClass('is-invalid');
11376         }
11377         
11378         
11379         
11380         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11381             
11382             var feedback = this.el.select('.form-control-feedback', true).first();
11383             
11384             if(feedback){
11385                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11386                 
11387                 if(this.getValue().length || this.forceFeedback){
11388                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11389                 }
11390                 
11391             }
11392             
11393         }
11394         
11395         this.fireEvent('invalid', this, msg);
11396     },
11397     // private
11398     SafariOnKeyDown : function(event)
11399     {
11400         // this is a workaround for a password hang bug on chrome/ webkit.
11401         if (this.inputEl().dom.type != 'password') {
11402             return;
11403         }
11404         
11405         var isSelectAll = false;
11406         
11407         if(this.inputEl().dom.selectionEnd > 0){
11408             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11409         }
11410         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11411             event.preventDefault();
11412             this.setValue('');
11413             return;
11414         }
11415         
11416         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11417             
11418             event.preventDefault();
11419             // this is very hacky as keydown always get's upper case.
11420             //
11421             var cc = String.fromCharCode(event.getCharCode());
11422             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11423             
11424         }
11425     },
11426     adjustWidth : function(tag, w){
11427         tag = tag.toLowerCase();
11428         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11429             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11430                 if(tag == 'input'){
11431                     return w + 2;
11432                 }
11433                 if(tag == 'textarea'){
11434                     return w-2;
11435                 }
11436             }else if(Roo.isOpera){
11437                 if(tag == 'input'){
11438                     return w + 2;
11439                 }
11440                 if(tag == 'textarea'){
11441                     return w-2;
11442                 }
11443             }
11444         }
11445         return w;
11446     },
11447     
11448     setFieldLabel : function(v)
11449     {
11450         if(!this.rendered){
11451             return;
11452         }
11453         
11454         if(this.indicatorEl()){
11455             var ar = this.el.select('label > span',true);
11456             
11457             if (ar.elements.length) {
11458                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11459                 this.fieldLabel = v;
11460                 return;
11461             }
11462             
11463             var br = this.el.select('label',true);
11464             
11465             if(br.elements.length) {
11466                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11467                 this.fieldLabel = v;
11468                 return;
11469             }
11470             
11471             Roo.log('Cannot Found any of label > span || label in input');
11472             return;
11473         }
11474         
11475         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476         this.fieldLabel = v;
11477         
11478         
11479     }
11480 });
11481
11482  
11483 /*
11484  * - LGPL
11485  *
11486  * Input
11487  * 
11488  */
11489
11490 /**
11491  * @class Roo.bootstrap.TextArea
11492  * @extends Roo.bootstrap.Input
11493  * Bootstrap TextArea class
11494  * @cfg {Number} cols Specifies the visible width of a text area
11495  * @cfg {Number} rows Specifies the visible number of lines in a text area
11496  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11497  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11498  * @cfg {string} html text
11499  * 
11500  * @constructor
11501  * Create a new TextArea
11502  * @param {Object} config The config object
11503  */
11504
11505 Roo.bootstrap.TextArea = function(config){
11506     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11507    
11508 };
11509
11510 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11511      
11512     cols : false,
11513     rows : 5,
11514     readOnly : false,
11515     warp : 'soft',
11516     resize : false,
11517     value: false,
11518     html: false,
11519     
11520     getAutoCreate : function(){
11521         
11522         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11523         
11524         var id = Roo.id();
11525         
11526         var cfg = {};
11527         
11528         if(this.inputType != 'hidden'){
11529             cfg.cls = 'form-group' //input-group
11530         }
11531         
11532         var input =  {
11533             tag: 'textarea',
11534             id : id,
11535             warp : this.warp,
11536             rows : this.rows,
11537             value : this.value || '',
11538             html: this.html || '',
11539             cls : 'form-control',
11540             placeholder : this.placeholder || '' 
11541             
11542         };
11543         
11544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11545             input.maxLength = this.maxLength;
11546         }
11547         
11548         if(this.resize){
11549             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11550         }
11551         
11552         if(this.cols){
11553             input.cols = this.cols;
11554         }
11555         
11556         if (this.readOnly) {
11557             input.readonly = true;
11558         }
11559         
11560         if (this.name) {
11561             input.name = this.name;
11562         }
11563         
11564         if (this.size) {
11565             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11566         }
11567         
11568         var settings=this;
11569         ['xs','sm','md','lg'].map(function(size){
11570             if (settings[size]) {
11571                 cfg.cls += ' col-' + size + '-' + settings[size];
11572             }
11573         });
11574         
11575         var inputblock = input;
11576         
11577         if(this.hasFeedback && !this.allowBlank){
11578             
11579             var feedback = {
11580                 tag: 'span',
11581                 cls: 'glyphicon form-control-feedback'
11582             };
11583
11584             inputblock = {
11585                 cls : 'has-feedback',
11586                 cn :  [
11587                     input,
11588                     feedback
11589                 ] 
11590             };  
11591         }
11592         
11593         
11594         if (this.before || this.after) {
11595             
11596             inputblock = {
11597                 cls : 'input-group',
11598                 cn :  [] 
11599             };
11600             if (this.before) {
11601                 inputblock.cn.push({
11602                     tag :'span',
11603                     cls : 'input-group-addon',
11604                     html : this.before
11605                 });
11606             }
11607             
11608             inputblock.cn.push(input);
11609             
11610             if(this.hasFeedback && !this.allowBlank){
11611                 inputblock.cls += ' has-feedback';
11612                 inputblock.cn.push(feedback);
11613             }
11614             
11615             if (this.after) {
11616                 inputblock.cn.push({
11617                     tag :'span',
11618                     cls : 'input-group-addon',
11619                     html : this.after
11620                 });
11621             }
11622             
11623         }
11624         
11625         if (align ==='left' && this.fieldLabel.length) {
11626             cfg.cn = [
11627                 {
11628                     tag: 'label',
11629                     'for' :  id,
11630                     cls : 'control-label',
11631                     html : this.fieldLabel
11632                 },
11633                 {
11634                     cls : "",
11635                     cn: [
11636                         inputblock
11637                     ]
11638                 }
11639
11640             ];
11641             
11642             if(this.labelWidth > 12){
11643                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11644             }
11645
11646             if(this.labelWidth < 13 && this.labelmd == 0){
11647                 this.labelmd = this.labelWidth;
11648             }
11649
11650             if(this.labellg > 0){
11651                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11652                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11653             }
11654
11655             if(this.labelmd > 0){
11656                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11657                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11658             }
11659
11660             if(this.labelsm > 0){
11661                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11662                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11663             }
11664
11665             if(this.labelxs > 0){
11666                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11667                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11668             }
11669             
11670         } else if ( this.fieldLabel.length) {
11671             cfg.cn = [
11672
11673                {
11674                    tag: 'label',
11675                    //cls : 'input-group-addon',
11676                    html : this.fieldLabel
11677
11678                },
11679
11680                inputblock
11681
11682            ];
11683
11684         } else {
11685
11686             cfg.cn = [
11687
11688                 inputblock
11689
11690             ];
11691                 
11692         }
11693         
11694         if (this.disabled) {
11695             input.disabled=true;
11696         }
11697         
11698         return cfg;
11699         
11700     },
11701     /**
11702      * return the real textarea element.
11703      */
11704     inputEl: function ()
11705     {
11706         return this.el.select('textarea.form-control',true).first();
11707     },
11708     
11709     /**
11710      * Clear any invalid styles/messages for this field
11711      */
11712     clearInvalid : function()
11713     {
11714         
11715         if(!this.el || this.preventMark){ // not rendered
11716             return;
11717         }
11718         
11719         var label = this.el.select('label', true).first();
11720         var icon = this.el.select('i.fa-star', true).first();
11721         
11722         if(label && icon){
11723             icon.remove();
11724         }
11725         this.el.removeClass( this.validClass);
11726         this.inputEl().removeClass('is-invalid');
11727          
11728         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11729             
11730             var feedback = this.el.select('.form-control-feedback', true).first();
11731             
11732             if(feedback){
11733                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11734             }
11735             
11736         }
11737         
11738         this.fireEvent('valid', this);
11739     },
11740     
11741      /**
11742      * Mark this field as valid
11743      */
11744     markValid : function()
11745     {
11746         if(!this.el  || this.preventMark){ // not rendered
11747             return;
11748         }
11749         
11750         this.el.removeClass([this.invalidClass, this.validClass]);
11751         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11752         
11753         var feedback = this.el.select('.form-control-feedback', true).first();
11754             
11755         if(feedback){
11756             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11757         }
11758
11759         if(this.disabled || this.allowBlank){
11760             return;
11761         }
11762         
11763         var label = this.el.select('label', true).first();
11764         var icon = this.el.select('i.fa-star', true).first();
11765         
11766         if(label && icon){
11767             icon.remove();
11768         }
11769         if (Roo.bootstrap.version == 3) {
11770             this.el.addClass(this.validClass);
11771         } else {
11772             this.inputEl().addClass('is-valid');
11773         }
11774         
11775         
11776         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11777             
11778             var feedback = this.el.select('.form-control-feedback', true).first();
11779             
11780             if(feedback){
11781                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11782                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11783             }
11784             
11785         }
11786         
11787         this.fireEvent('valid', this);
11788     },
11789     
11790      /**
11791      * Mark this field as invalid
11792      * @param {String} msg The validation message
11793      */
11794     markInvalid : function(msg)
11795     {
11796         if(!this.el  || this.preventMark){ // not rendered
11797             return;
11798         }
11799         
11800         this.el.removeClass([this.invalidClass, this.validClass]);
11801         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11802         
11803         var feedback = this.el.select('.form-control-feedback', true).first();
11804             
11805         if(feedback){
11806             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11807         }
11808
11809         if(this.disabled || this.allowBlank){
11810             return;
11811         }
11812         
11813         var label = this.el.select('label', true).first();
11814         var icon = this.el.select('i.fa-star', true).first();
11815         
11816         if(!this.getValue().length && label && !icon){
11817             this.el.createChild({
11818                 tag : 'i',
11819                 cls : 'text-danger fa fa-lg fa-star',
11820                 tooltip : 'This field is required',
11821                 style : 'margin-right:5px;'
11822             }, label, true);
11823         }
11824         
11825         if (Roo.bootstrap.version == 3) {
11826             this.el.addClass(this.invalidClass);
11827         } else {
11828             this.inputEl().addClass('is-invalid');
11829         }
11830         
11831         // fixme ... this may be depricated need to test..
11832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11833             
11834             var feedback = this.el.select('.form-control-feedback', true).first();
11835             
11836             if(feedback){
11837                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11838                 
11839                 if(this.getValue().length || this.forceFeedback){
11840                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11841                 }
11842                 
11843             }
11844             
11845         }
11846         
11847         this.fireEvent('invalid', this, msg);
11848     }
11849 });
11850
11851  
11852 /*
11853  * - LGPL
11854  *
11855  * trigger field - base class for combo..
11856  * 
11857  */
11858  
11859 /**
11860  * @class Roo.bootstrap.TriggerField
11861  * @extends Roo.bootstrap.Input
11862  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11863  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11864  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11865  * for which you can provide a custom implementation.  For example:
11866  * <pre><code>
11867 var trigger = new Roo.bootstrap.TriggerField();
11868 trigger.onTriggerClick = myTriggerFn;
11869 trigger.applyTo('my-field');
11870 </code></pre>
11871  *
11872  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11873  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11874  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11875  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11876  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11877
11878  * @constructor
11879  * Create a new TriggerField.
11880  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11881  * to the base TextField)
11882  */
11883 Roo.bootstrap.TriggerField = function(config){
11884     this.mimicing = false;
11885     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11886 };
11887
11888 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11889     /**
11890      * @cfg {String} triggerClass A CSS class to apply to the trigger
11891      */
11892      /**
11893      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11894      */
11895     hideTrigger:false,
11896
11897     /**
11898      * @cfg {Boolean} removable (true|false) special filter default false
11899      */
11900     removable : false,
11901     
11902     /** @cfg {Boolean} grow @hide */
11903     /** @cfg {Number} growMin @hide */
11904     /** @cfg {Number} growMax @hide */
11905
11906     /**
11907      * @hide 
11908      * @method
11909      */
11910     autoSize: Roo.emptyFn,
11911     // private
11912     monitorTab : true,
11913     // private
11914     deferHeight : true,
11915
11916     
11917     actionMode : 'wrap',
11918     
11919     caret : false,
11920     
11921     
11922     getAutoCreate : function(){
11923        
11924         var align = this.labelAlign || this.parentLabelAlign();
11925         
11926         var id = Roo.id();
11927         
11928         var cfg = {
11929             cls: 'form-group' //input-group
11930         };
11931         
11932         
11933         var input =  {
11934             tag: 'input',
11935             id : id,
11936             type : this.inputType,
11937             cls : 'form-control',
11938             autocomplete: 'new-password',
11939             placeholder : this.placeholder || '' 
11940             
11941         };
11942         if (this.name) {
11943             input.name = this.name;
11944         }
11945         if (this.size) {
11946             input.cls += ' input-' + this.size;
11947         }
11948         
11949         if (this.disabled) {
11950             input.disabled=true;
11951         }
11952         
11953         var inputblock = input;
11954         
11955         if(this.hasFeedback && !this.allowBlank){
11956             
11957             var feedback = {
11958                 tag: 'span',
11959                 cls: 'glyphicon form-control-feedback'
11960             };
11961             
11962             if(this.removable && !this.editable  ){
11963                 inputblock = {
11964                     cls : 'has-feedback',
11965                     cn :  [
11966                         inputblock,
11967                         {
11968                             tag: 'button',
11969                             html : 'x',
11970                             cls : 'roo-combo-removable-btn close'
11971                         },
11972                         feedback
11973                     ] 
11974                 };
11975             } else {
11976                 inputblock = {
11977                     cls : 'has-feedback',
11978                     cn :  [
11979                         inputblock,
11980                         feedback
11981                     ] 
11982                 };
11983             }
11984
11985         } else {
11986             if(this.removable && !this.editable ){
11987                 inputblock = {
11988                     cls : 'roo-removable',
11989                     cn :  [
11990                         inputblock,
11991                         {
11992                             tag: 'button',
11993                             html : 'x',
11994                             cls : 'roo-combo-removable-btn close'
11995                         }
11996                     ] 
11997                 };
11998             }
11999         }
12000         
12001         if (this.before || this.after) {
12002             
12003             inputblock = {
12004                 cls : 'input-group',
12005                 cn :  [] 
12006             };
12007             if (this.before) {
12008                 inputblock.cn.push({
12009                     tag :'span',
12010                     cls : 'input-group-addon input-group-prepend input-group-text',
12011                     html : this.before
12012                 });
12013             }
12014             
12015             inputblock.cn.push(input);
12016             
12017             if(this.hasFeedback && !this.allowBlank){
12018                 inputblock.cls += ' has-feedback';
12019                 inputblock.cn.push(feedback);
12020             }
12021             
12022             if (this.after) {
12023                 inputblock.cn.push({
12024                     tag :'span',
12025                     cls : 'input-group-addon input-group-append input-group-text',
12026                     html : this.after
12027                 });
12028             }
12029             
12030         };
12031         
12032       
12033         
12034         var ibwrap = inputblock;
12035         
12036         if(this.multiple){
12037             ibwrap = {
12038                 tag: 'ul',
12039                 cls: 'roo-select2-choices',
12040                 cn:[
12041                     {
12042                         tag: 'li',
12043                         cls: 'roo-select2-search-field',
12044                         cn: [
12045
12046                             inputblock
12047                         ]
12048                     }
12049                 ]
12050             };
12051                 
12052         }
12053         
12054         var combobox = {
12055             cls: 'roo-select2-container input-group',
12056             cn: [
12057                  {
12058                     tag: 'input',
12059                     type : 'hidden',
12060                     cls: 'form-hidden-field'
12061                 },
12062                 ibwrap
12063             ]
12064         };
12065         
12066         if(!this.multiple && this.showToggleBtn){
12067             
12068             var caret = {
12069                         tag: 'span',
12070                         cls: 'caret'
12071              };
12072             if (this.caret != false) {
12073                 caret = {
12074                      tag: 'i',
12075                      cls: 'fa fa-' + this.caret
12076                 };
12077                 
12078             }
12079             
12080             combobox.cn.push({
12081                 tag :'span',
12082                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12083                 cn : [
12084                     Roo.bootstrap.version == 3 ? caret : '',
12085                     {
12086                         tag: 'span',
12087                         cls: 'combobox-clear',
12088                         cn  : [
12089                             {
12090                                 tag : 'i',
12091                                 cls: 'icon-remove'
12092                             }
12093                         ]
12094                     }
12095                 ]
12096
12097             })
12098         }
12099         
12100         if(this.multiple){
12101             combobox.cls += ' roo-select2-container-multi';
12102         }
12103          var indicator = {
12104             tag : 'i',
12105             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12106             tooltip : 'This field is required'
12107         };
12108         if (Roo.bootstrap.version == 4) {
12109             indicator = {
12110                 tag : 'i',
12111                 style : 'display:none'
12112             };
12113         }
12114         
12115         
12116         if (align ==='left' && this.fieldLabel.length) {
12117             
12118             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12119
12120             cfg.cn = [
12121                 indicator,
12122                 {
12123                     tag: 'label',
12124                     'for' :  id,
12125                     cls : 'control-label',
12126                     html : this.fieldLabel
12127
12128                 },
12129                 {
12130                     cls : "", 
12131                     cn: [
12132                         combobox
12133                     ]
12134                 }
12135
12136             ];
12137             
12138             var labelCfg = cfg.cn[1];
12139             var contentCfg = cfg.cn[2];
12140             
12141             if(this.indicatorpos == 'right'){
12142                 cfg.cn = [
12143                     {
12144                         tag: 'label',
12145                         'for' :  id,
12146                         cls : 'control-label',
12147                         cn : [
12148                             {
12149                                 tag : 'span',
12150                                 html : this.fieldLabel
12151                             },
12152                             indicator
12153                         ]
12154                     },
12155                     {
12156                         cls : "", 
12157                         cn: [
12158                             combobox
12159                         ]
12160                     }
12161
12162                 ];
12163                 
12164                 labelCfg = cfg.cn[0];
12165                 contentCfg = cfg.cn[1];
12166             }
12167             
12168             if(this.labelWidth > 12){
12169                 labelCfg.style = "width: " + this.labelWidth + 'px';
12170             }
12171             
12172             if(this.labelWidth < 13 && this.labelmd == 0){
12173                 this.labelmd = this.labelWidth;
12174             }
12175             
12176             if(this.labellg > 0){
12177                 labelCfg.cls += ' col-lg-' + this.labellg;
12178                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12179             }
12180             
12181             if(this.labelmd > 0){
12182                 labelCfg.cls += ' col-md-' + this.labelmd;
12183                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12184             }
12185             
12186             if(this.labelsm > 0){
12187                 labelCfg.cls += ' col-sm-' + this.labelsm;
12188                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12189             }
12190             
12191             if(this.labelxs > 0){
12192                 labelCfg.cls += ' col-xs-' + this.labelxs;
12193                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12194             }
12195             
12196         } else if ( this.fieldLabel.length) {
12197 //                Roo.log(" label");
12198             cfg.cn = [
12199                 indicator,
12200                {
12201                    tag: 'label',
12202                    //cls : 'input-group-addon',
12203                    html : this.fieldLabel
12204
12205                },
12206
12207                combobox
12208
12209             ];
12210             
12211             if(this.indicatorpos == 'right'){
12212                 
12213                 cfg.cn = [
12214                     {
12215                        tag: 'label',
12216                        cn : [
12217                            {
12218                                tag : 'span',
12219                                html : this.fieldLabel
12220                            },
12221                            indicator
12222                        ]
12223
12224                     },
12225                     combobox
12226
12227                 ];
12228
12229             }
12230
12231         } else {
12232             
12233 //                Roo.log(" no label && no align");
12234                 cfg = combobox
12235                      
12236                 
12237         }
12238         
12239         var settings=this;
12240         ['xs','sm','md','lg'].map(function(size){
12241             if (settings[size]) {
12242                 cfg.cls += ' col-' + size + '-' + settings[size];
12243             }
12244         });
12245         
12246         return cfg;
12247         
12248     },
12249     
12250     
12251     
12252     // private
12253     onResize : function(w, h){
12254 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12255 //        if(typeof w == 'number'){
12256 //            var x = w - this.trigger.getWidth();
12257 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12258 //            this.trigger.setStyle('left', x+'px');
12259 //        }
12260     },
12261
12262     // private
12263     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12264
12265     // private
12266     getResizeEl : function(){
12267         return this.inputEl();
12268     },
12269
12270     // private
12271     getPositionEl : function(){
12272         return this.inputEl();
12273     },
12274
12275     // private
12276     alignErrorIcon : function(){
12277         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12278     },
12279
12280     // private
12281     initEvents : function(){
12282         
12283         this.createList();
12284         
12285         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12286         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12287         if(!this.multiple && this.showToggleBtn){
12288             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12289             if(this.hideTrigger){
12290                 this.trigger.setDisplayed(false);
12291             }
12292             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12293         }
12294         
12295         if(this.multiple){
12296             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12297         }
12298         
12299         if(this.removable && !this.editable && !this.tickable){
12300             var close = this.closeTriggerEl();
12301             
12302             if(close){
12303                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12304                 close.on('click', this.removeBtnClick, this, close);
12305             }
12306         }
12307         
12308         //this.trigger.addClassOnOver('x-form-trigger-over');
12309         //this.trigger.addClassOnClick('x-form-trigger-click');
12310         
12311         //if(!this.width){
12312         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12313         //}
12314     },
12315     
12316     closeTriggerEl : function()
12317     {
12318         var close = this.el.select('.roo-combo-removable-btn', true).first();
12319         return close ? close : false;
12320     },
12321     
12322     removeBtnClick : function(e, h, el)
12323     {
12324         e.preventDefault();
12325         
12326         if(this.fireEvent("remove", this) !== false){
12327             this.reset();
12328             this.fireEvent("afterremove", this)
12329         }
12330     },
12331     
12332     createList : function()
12333     {
12334         this.list = Roo.get(document.body).createChild({
12335             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12336             cls: 'typeahead typeahead-long dropdown-menu',
12337             style: 'display:none'
12338         });
12339         
12340         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12341         
12342     },
12343
12344     // private
12345     initTrigger : function(){
12346        
12347     },
12348
12349     // private
12350     onDestroy : function(){
12351         if(this.trigger){
12352             this.trigger.removeAllListeners();
12353           //  this.trigger.remove();
12354         }
12355         //if(this.wrap){
12356         //    this.wrap.remove();
12357         //}
12358         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12359     },
12360
12361     // private
12362     onFocus : function(){
12363         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12364         /*
12365         if(!this.mimicing){
12366             this.wrap.addClass('x-trigger-wrap-focus');
12367             this.mimicing = true;
12368             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12369             if(this.monitorTab){
12370                 this.el.on("keydown", this.checkTab, this);
12371             }
12372         }
12373         */
12374     },
12375
12376     // private
12377     checkTab : function(e){
12378         if(e.getKey() == e.TAB){
12379             this.triggerBlur();
12380         }
12381     },
12382
12383     // private
12384     onBlur : function(){
12385         // do nothing
12386     },
12387
12388     // private
12389     mimicBlur : function(e, t){
12390         /*
12391         if(!this.wrap.contains(t) && this.validateBlur()){
12392             this.triggerBlur();
12393         }
12394         */
12395     },
12396
12397     // private
12398     triggerBlur : function(){
12399         this.mimicing = false;
12400         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12401         if(this.monitorTab){
12402             this.el.un("keydown", this.checkTab, this);
12403         }
12404         //this.wrap.removeClass('x-trigger-wrap-focus');
12405         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12406     },
12407
12408     // private
12409     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12410     validateBlur : function(e, t){
12411         return true;
12412     },
12413
12414     // private
12415     onDisable : function(){
12416         this.inputEl().dom.disabled = true;
12417         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12418         //if(this.wrap){
12419         //    this.wrap.addClass('x-item-disabled');
12420         //}
12421     },
12422
12423     // private
12424     onEnable : function(){
12425         this.inputEl().dom.disabled = false;
12426         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12427         //if(this.wrap){
12428         //    this.el.removeClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onShow : function(){
12434         var ae = this.getActionEl();
12435         
12436         if(ae){
12437             ae.dom.style.display = '';
12438             ae.dom.style.visibility = 'visible';
12439         }
12440     },
12441
12442     // private
12443     
12444     onHide : function(){
12445         var ae = this.getActionEl();
12446         ae.dom.style.display = 'none';
12447     },
12448
12449     /**
12450      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12451      * by an implementing function.
12452      * @method
12453      * @param {EventObject} e
12454      */
12455     onTriggerClick : Roo.emptyFn
12456 });
12457  
12458 /*
12459 * Licence: LGPL
12460 */
12461
12462 /**
12463  * @class Roo.bootstrap.CardUploader
12464  * @extends Roo.bootstrap.Button
12465  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12466  * @cfg {Number} errorTimeout default 3000
12467  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12468  * @cfg {Array}  html The button text.
12469
12470  *
12471  * @constructor
12472  * Create a new CardUploader
12473  * @param {Object} config The config object
12474  */
12475
12476 Roo.bootstrap.CardUploader = function(config){
12477     
12478  
12479     
12480     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12481     
12482     
12483     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12484         return r.data.id
12485         });
12486     
12487     
12488 };
12489
12490 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12491     
12492      
12493     errorTimeout : 3000,
12494      
12495     images : false,
12496    
12497     fileCollection : false,
12498     allowBlank : true,
12499     
12500     getAutoCreate : function()
12501     {
12502         
12503         var cfg =  {
12504             cls :'form-group' ,
12505             cn : [
12506                
12507                 {
12508                     tag: 'label',
12509                    //cls : 'input-group-addon',
12510                     html : this.fieldLabel
12511
12512                 },
12513
12514                 {
12515                     tag: 'input',
12516                     type : 'hidden',
12517                     value : this.value,
12518                     cls : 'd-none  form-control'
12519                 },
12520                 
12521                 {
12522                     tag: 'input',
12523                     multiple : 'multiple',
12524                     type : 'file',
12525                     cls : 'd-none  roo-card-upload-selector'
12526                 },
12527                 
12528                 {
12529                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12530                 },
12531                 {
12532                     cls : 'card-columns roo-card-uploader-container'
12533                 }
12534
12535             ]
12536         };
12537            
12538          
12539         return cfg;
12540     },
12541     
12542     getChildContainer : function() /// what children are added to.
12543     {
12544         return this.containerEl;
12545     },
12546    
12547     getButtonContainer : function() /// what children are added to.
12548     {
12549         return this.el.select(".roo-card-uploader-button-container").first();
12550     },
12551    
12552     initEvents : function()
12553     {
12554         
12555         Roo.bootstrap.Input.prototype.initEvents.call(this);
12556         
12557         var t = this;
12558         this.addxtype({
12559             xns: Roo.bootstrap,
12560
12561             xtype : 'Button',
12562             container_method : 'getButtonContainer' ,            
12563             html :  this.html, // fix changable?
12564             cls : 'w-100 ',
12565             listeners : {
12566                 'click' : function(btn, e) {
12567                     t.onClick(e);
12568                 }
12569             }
12570         });
12571         
12572         
12573         
12574         
12575         this.urlAPI = (window.createObjectURL && window) || 
12576                                 (window.URL && URL.revokeObjectURL && URL) || 
12577                                 (window.webkitURL && webkitURL);
12578                         
12579          
12580          
12581          
12582         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12583         
12584         this.selectorEl.on('change', this.onFileSelected, this);
12585         if (this.images) {
12586             var t = this;
12587             this.images.forEach(function(img) {
12588                 t.addCard(img)
12589             });
12590             this.images = false;
12591         }
12592         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12593          
12594        
12595     },
12596     
12597    
12598     onClick : function(e)
12599     {
12600         e.preventDefault();
12601          
12602         this.selectorEl.dom.click();
12603          
12604     },
12605     
12606     onFileSelected : function(e)
12607     {
12608         e.preventDefault();
12609         
12610         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12611             return;
12612         }
12613         
12614         Roo.each(this.selectorEl.dom.files, function(file){    
12615             this.addFile(file);
12616         }, this);
12617          
12618     },
12619     
12620       
12621     
12622       
12623     
12624     addFile : function(file)
12625     {
12626            
12627         if(typeof(file) === 'string'){
12628             throw "Add file by name?"; // should not happen
12629             return;
12630         }
12631         
12632         if(!file || !this.urlAPI){
12633             return;
12634         }
12635         
12636         // file;
12637         // file.type;
12638         
12639         var _this = this;
12640         
12641         
12642         var url = _this.urlAPI.createObjectURL( file);
12643            
12644         this.addCard({
12645             id : Roo.bootstrap.CardUploader.ID--,
12646             is_uploaded : false,
12647             src : url,
12648             title : file.name,
12649             mimetype : file.type,
12650             preview : false,
12651             is_deleted : 0
12652         })
12653         
12654     },
12655     
12656     addCard : function (data)
12657     {
12658         // hidden input element?
12659         // if the file is not an image...
12660         //then we need to use something other that and header_image
12661         var t = this;
12662         //   remove.....
12663         var footer = [
12664             {
12665                 xns : Roo.bootstrap,
12666                 xtype : 'CardFooter',
12667                 items: [
12668                     {
12669                         xns : Roo.bootstrap,
12670                         xtype : 'Element',
12671                         cls : 'd-flex',
12672                         items : [
12673                             
12674                             {
12675                                 xns : Roo.bootstrap,
12676                                 xtype : 'Button',
12677                                 html : String.format("<small>{0}</small>", data.title),
12678                                 cls : 'col-11 text-left',
12679                                 size: 'sm',
12680                                 weight: 'link',
12681                                 fa : 'download',
12682                                 listeners : {
12683                                     click : function() {
12684                                         this.downloadCard(data.id)
12685                                     }
12686                                 }
12687                             },
12688                           
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 
12693                                 size : 'sm',
12694                                 weight: 'danger',
12695                                 cls : 'col-1',
12696                                 fa : 'times',
12697                                 listeners : {
12698                                     click : function() {
12699                                         t.removeCard(data.id)
12700                                     }
12701                                 }
12702                             }
12703                         ]
12704                     }
12705                     
12706                 ] 
12707             }
12708             
12709         ];
12710
12711         var cn = this.addxtype(
12712             {
12713                  
12714                 xns : Roo.bootstrap,
12715                 xtype : 'Card',
12716                 closeable : true,
12717                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12718                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12719                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12720                 data : data,
12721                 html : false,
12722                  
12723                 items : footer,
12724                 initEvents : function() {
12725                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12726                     this.imgEl = this.el.select('.card-img-top').first();
12727                     if (this.imgEl) {
12728                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12729                         this.imgEl.set({ 'pointer' : 'cursor' });
12730                                   
12731                     }
12732                     
12733                   
12734                 }
12735                 
12736             }
12737         );
12738         // dont' really need ot update items.
12739         // this.items.push(cn);
12740         this.fileCollection.add(cn);
12741         this.updateInput();
12742         
12743     },
12744     removeCard : function(id)
12745     {
12746         
12747         var card  = this.fileCollection.get(id);
12748         card.data.is_deleted = 1;
12749         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12750         this.fileCollection.remove(card);
12751         //this.items = this.items.filter(function(e) { return e != card });
12752         // dont' really need ot update items.
12753         card.el.dom.parentNode.removeChild(card.el.dom);
12754         
12755     },
12756     reset: function()
12757     {
12758         this.fileCollection.each(function(card) {
12759             card.el.dom.parentNode.removeChild(card.el.dom);    
12760         });
12761         this.fileCollection.clear();
12762         this.updateInput();
12763     },
12764     
12765     updateInput : function()
12766     {
12767         var data = [];
12768         this.fileCollection.each(function(e) {
12769             data.push(e.data);
12770         });
12771         
12772         this.inputEl().dom.value = JSON.stringify(data);
12773     }
12774     
12775     
12776 });
12777
12778
12779 Roo.bootstrap.CardUploader.ID = -1;/*
12780  * Based on:
12781  * Ext JS Library 1.1.1
12782  * Copyright(c) 2006-2007, Ext JS, LLC.
12783  *
12784  * Originally Released Under LGPL - original licence link has changed is not relivant.
12785  *
12786  * Fork - LGPL
12787  * <script type="text/javascript">
12788  */
12789
12790
12791 /**
12792  * @class Roo.data.SortTypes
12793  * @singleton
12794  * Defines the default sorting (casting?) comparison functions used when sorting data.
12795  */
12796 Roo.data.SortTypes = {
12797     /**
12798      * Default sort that does nothing
12799      * @param {Mixed} s The value being converted
12800      * @return {Mixed} The comparison value
12801      */
12802     none : function(s){
12803         return s;
12804     },
12805     
12806     /**
12807      * The regular expression used to strip tags
12808      * @type {RegExp}
12809      * @property
12810      */
12811     stripTagsRE : /<\/?[^>]+>/gi,
12812     
12813     /**
12814      * Strips all HTML tags to sort on text only
12815      * @param {Mixed} s The value being converted
12816      * @return {String} The comparison value
12817      */
12818     asText : function(s){
12819         return String(s).replace(this.stripTagsRE, "");
12820     },
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only - Case insensitive
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asUCText : function(s){
12828         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Case insensitive string
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCString : function(s) {
12837         return String(s).toUpperCase();
12838     },
12839     
12840     /**
12841      * Date sorting
12842      * @param {Mixed} s The value being converted
12843      * @return {Number} The comparison value
12844      */
12845     asDate : function(s) {
12846         if(!s){
12847             return 0;
12848         }
12849         if(s instanceof Date){
12850             return s.getTime();
12851         }
12852         return Date.parse(String(s));
12853     },
12854     
12855     /**
12856      * Float sorting
12857      * @param {Mixed} s The value being converted
12858      * @return {Float} The comparison value
12859      */
12860     asFloat : function(s) {
12861         var val = parseFloat(String(s).replace(/,/g, ""));
12862         if(isNaN(val)) {
12863             val = 0;
12864         }
12865         return val;
12866     },
12867     
12868     /**
12869      * Integer sorting
12870      * @param {Mixed} s The value being converted
12871      * @return {Number} The comparison value
12872      */
12873     asInt : function(s) {
12874         var val = parseInt(String(s).replace(/,/g, ""));
12875         if(isNaN(val)) {
12876             val = 0;
12877         }
12878         return val;
12879     }
12880 };/*
12881  * Based on:
12882  * Ext JS Library 1.1.1
12883  * Copyright(c) 2006-2007, Ext JS, LLC.
12884  *
12885  * Originally Released Under LGPL - original licence link has changed is not relivant.
12886  *
12887  * Fork - LGPL
12888  * <script type="text/javascript">
12889  */
12890
12891 /**
12892 * @class Roo.data.Record
12893  * Instances of this class encapsulate both record <em>definition</em> information, and record
12894  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12895  * to access Records cached in an {@link Roo.data.Store} object.<br>
12896  * <p>
12897  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12898  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12899  * objects.<br>
12900  * <p>
12901  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12902  * @constructor
12903  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12904  * {@link #create}. The parameters are the same.
12905  * @param {Array} data An associative Array of data values keyed by the field name.
12906  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12907  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12908  * not specified an integer id is generated.
12909  */
12910 Roo.data.Record = function(data, id){
12911     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12912     this.data = data;
12913 };
12914
12915 /**
12916  * Generate a constructor for a specific record layout.
12917  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12918  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12919  * Each field definition object may contain the following properties: <ul>
12920  * <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,
12921  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12922  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12923  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12924  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12925  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12926  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12927  * this may be omitted.</p></li>
12928  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12929  * <ul><li>auto (Default, implies no conversion)</li>
12930  * <li>string</li>
12931  * <li>int</li>
12932  * <li>float</li>
12933  * <li>boolean</li>
12934  * <li>date</li></ul></p></li>
12935  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12936  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12937  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12938  * by the Reader into an object that will be stored in the Record. It is passed the
12939  * following parameters:<ul>
12940  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12941  * </ul></p></li>
12942  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12943  * </ul>
12944  * <br>usage:<br><pre><code>
12945 var TopicRecord = Roo.data.Record.create(
12946     {name: 'title', mapping: 'topic_title'},
12947     {name: 'author', mapping: 'username'},
12948     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12949     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12950     {name: 'lastPoster', mapping: 'user2'},
12951     {name: 'excerpt', mapping: 'post_text'}
12952 );
12953
12954 var myNewRecord = new TopicRecord({
12955     title: 'Do my job please',
12956     author: 'noobie',
12957     totalPosts: 1,
12958     lastPost: new Date(),
12959     lastPoster: 'Animal',
12960     excerpt: 'No way dude!'
12961 });
12962 myStore.add(myNewRecord);
12963 </code></pre>
12964  * @method create
12965  * @static
12966  */
12967 Roo.data.Record.create = function(o){
12968     var f = function(){
12969         f.superclass.constructor.apply(this, arguments);
12970     };
12971     Roo.extend(f, Roo.data.Record);
12972     var p = f.prototype;
12973     p.fields = new Roo.util.MixedCollection(false, function(field){
12974         return field.name;
12975     });
12976     for(var i = 0, len = o.length; i < len; i++){
12977         p.fields.add(new Roo.data.Field(o[i]));
12978     }
12979     f.getField = function(name){
12980         return p.fields.get(name);  
12981     };
12982     return f;
12983 };
12984
12985 Roo.data.Record.AUTO_ID = 1000;
12986 Roo.data.Record.EDIT = 'edit';
12987 Roo.data.Record.REJECT = 'reject';
12988 Roo.data.Record.COMMIT = 'commit';
12989
12990 Roo.data.Record.prototype = {
12991     /**
12992      * Readonly flag - true if this record has been modified.
12993      * @type Boolean
12994      */
12995     dirty : false,
12996     editing : false,
12997     error: null,
12998     modified: null,
12999
13000     // private
13001     join : function(store){
13002         this.store = store;
13003     },
13004
13005     /**
13006      * Set the named field to the specified value.
13007      * @param {String} name The name of the field to set.
13008      * @param {Object} value The value to set the field to.
13009      */
13010     set : function(name, value){
13011         if(this.data[name] == value){
13012             return;
13013         }
13014         this.dirty = true;
13015         if(!this.modified){
13016             this.modified = {};
13017         }
13018         if(typeof this.modified[name] == 'undefined'){
13019             this.modified[name] = this.data[name];
13020         }
13021         this.data[name] = value;
13022         if(!this.editing && this.store){
13023             this.store.afterEdit(this);
13024         }       
13025     },
13026
13027     /**
13028      * Get the value of the named field.
13029      * @param {String} name The name of the field to get the value of.
13030      * @return {Object} The value of the field.
13031      */
13032     get : function(name){
13033         return this.data[name]; 
13034     },
13035
13036     // private
13037     beginEdit : function(){
13038         this.editing = true;
13039         this.modified = {}; 
13040     },
13041
13042     // private
13043     cancelEdit : function(){
13044         this.editing = false;
13045         delete this.modified;
13046     },
13047
13048     // private
13049     endEdit : function(){
13050         this.editing = false;
13051         if(this.dirty && this.store){
13052             this.store.afterEdit(this);
13053         }
13054     },
13055
13056     /**
13057      * Usually called by the {@link Roo.data.Store} which owns the Record.
13058      * Rejects all changes made to the Record since either creation, or the last commit operation.
13059      * Modified fields are reverted to their original values.
13060      * <p>
13061      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13062      * of reject operations.
13063      */
13064     reject : function(){
13065         var m = this.modified;
13066         for(var n in m){
13067             if(typeof m[n] != "function"){
13068                 this.data[n] = m[n];
13069             }
13070         }
13071         this.dirty = false;
13072         delete this.modified;
13073         this.editing = false;
13074         if(this.store){
13075             this.store.afterReject(this);
13076         }
13077     },
13078
13079     /**
13080      * Usually called by the {@link Roo.data.Store} which owns the Record.
13081      * Commits all changes made to the Record since either creation, or the last commit operation.
13082      * <p>
13083      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13084      * of commit operations.
13085      */
13086     commit : function(){
13087         this.dirty = false;
13088         delete this.modified;
13089         this.editing = false;
13090         if(this.store){
13091             this.store.afterCommit(this);
13092         }
13093     },
13094
13095     // private
13096     hasError : function(){
13097         return this.error != null;
13098     },
13099
13100     // private
13101     clearError : function(){
13102         this.error = null;
13103     },
13104
13105     /**
13106      * Creates a copy of this record.
13107      * @param {String} id (optional) A new record id if you don't want to use this record's id
13108      * @return {Record}
13109      */
13110     copy : function(newId) {
13111         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13112     }
13113 };/*
13114  * Based on:
13115  * Ext JS Library 1.1.1
13116  * Copyright(c) 2006-2007, Ext JS, LLC.
13117  *
13118  * Originally Released Under LGPL - original licence link has changed is not relivant.
13119  *
13120  * Fork - LGPL
13121  * <script type="text/javascript">
13122  */
13123
13124
13125
13126 /**
13127  * @class Roo.data.Store
13128  * @extends Roo.util.Observable
13129  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13130  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13131  * <p>
13132  * 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
13133  * has no knowledge of the format of the data returned by the Proxy.<br>
13134  * <p>
13135  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13136  * instances from the data object. These records are cached and made available through accessor functions.
13137  * @constructor
13138  * Creates a new Store.
13139  * @param {Object} config A config object containing the objects needed for the Store to access data,
13140  * and read the data into Records.
13141  */
13142 Roo.data.Store = function(config){
13143     this.data = new Roo.util.MixedCollection(false);
13144     this.data.getKey = function(o){
13145         return o.id;
13146     };
13147     this.baseParams = {};
13148     // private
13149     this.paramNames = {
13150         "start" : "start",
13151         "limit" : "limit",
13152         "sort" : "sort",
13153         "dir" : "dir",
13154         "multisort" : "_multisort"
13155     };
13156
13157     if(config && config.data){
13158         this.inlineData = config.data;
13159         delete config.data;
13160     }
13161
13162     Roo.apply(this, config);
13163     
13164     if(this.reader){ // reader passed
13165         this.reader = Roo.factory(this.reader, Roo.data);
13166         this.reader.xmodule = this.xmodule || false;
13167         if(!this.recordType){
13168             this.recordType = this.reader.recordType;
13169         }
13170         if(this.reader.onMetaChange){
13171             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13172         }
13173     }
13174
13175     if(this.recordType){
13176         this.fields = this.recordType.prototype.fields;
13177     }
13178     this.modified = [];
13179
13180     this.addEvents({
13181         /**
13182          * @event datachanged
13183          * Fires when the data cache has changed, and a widget which is using this Store
13184          * as a Record cache should refresh its view.
13185          * @param {Store} this
13186          */
13187         datachanged : true,
13188         /**
13189          * @event metachange
13190          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13191          * @param {Store} this
13192          * @param {Object} meta The JSON metadata
13193          */
13194         metachange : true,
13195         /**
13196          * @event add
13197          * Fires when Records have been added to the Store
13198          * @param {Store} this
13199          * @param {Roo.data.Record[]} records The array of Records added
13200          * @param {Number} index The index at which the record(s) were added
13201          */
13202         add : true,
13203         /**
13204          * @event remove
13205          * Fires when a Record has been removed from the Store
13206          * @param {Store} this
13207          * @param {Roo.data.Record} record The Record that was removed
13208          * @param {Number} index The index at which the record was removed
13209          */
13210         remove : true,
13211         /**
13212          * @event update
13213          * Fires when a Record has been updated
13214          * @param {Store} this
13215          * @param {Roo.data.Record} record The Record that was updated
13216          * @param {String} operation The update operation being performed.  Value may be one of:
13217          * <pre><code>
13218  Roo.data.Record.EDIT
13219  Roo.data.Record.REJECT
13220  Roo.data.Record.COMMIT
13221          * </code></pre>
13222          */
13223         update : true,
13224         /**
13225          * @event clear
13226          * Fires when the data cache has been cleared.
13227          * @param {Store} this
13228          */
13229         clear : true,
13230         /**
13231          * @event beforeload
13232          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13233          * the load action will be canceled.
13234          * @param {Store} this
13235          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13236          */
13237         beforeload : true,
13238         /**
13239          * @event beforeloadadd
13240          * Fires after a new set of Records has been loaded.
13241          * @param {Store} this
13242          * @param {Roo.data.Record[]} records The Records that were loaded
13243          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13244          */
13245         beforeloadadd : true,
13246         /**
13247          * @event load
13248          * Fires after a new set of Records has been loaded, before they are added to the store.
13249          * @param {Store} this
13250          * @param {Roo.data.Record[]} records The Records that were loaded
13251          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13252          * @params {Object} return from reader
13253          */
13254         load : true,
13255         /**
13256          * @event loadexception
13257          * Fires if an exception occurs in the Proxy during loading.
13258          * Called with the signature of the Proxy's "loadexception" event.
13259          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13260          * 
13261          * @param {Proxy} 
13262          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13263          * @param {Object} load options 
13264          * @param {Object} jsonData from your request (normally this contains the Exception)
13265          */
13266         loadexception : true
13267     });
13268     
13269     if(this.proxy){
13270         this.proxy = Roo.factory(this.proxy, Roo.data);
13271         this.proxy.xmodule = this.xmodule || false;
13272         this.relayEvents(this.proxy,  ["loadexception"]);
13273     }
13274     this.sortToggle = {};
13275     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13276
13277     Roo.data.Store.superclass.constructor.call(this);
13278
13279     if(this.inlineData){
13280         this.loadData(this.inlineData);
13281         delete this.inlineData;
13282     }
13283 };
13284
13285 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13286      /**
13287     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13288     * without a remote query - used by combo/forms at present.
13289     */
13290     
13291     /**
13292     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13293     */
13294     /**
13295     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13296     */
13297     /**
13298     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13299     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13300     */
13301     /**
13302     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13303     * on any HTTP request
13304     */
13305     /**
13306     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13307     */
13308     /**
13309     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13310     */
13311     multiSort: false,
13312     /**
13313     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13314     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13315     */
13316     remoteSort : false,
13317
13318     /**
13319     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13320      * loaded or when a record is removed. (defaults to false).
13321     */
13322     pruneModifiedRecords : false,
13323
13324     // private
13325     lastOptions : null,
13326
13327     /**
13328      * Add Records to the Store and fires the add event.
13329      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13330      */
13331     add : function(records){
13332         records = [].concat(records);
13333         for(var i = 0, len = records.length; i < len; i++){
13334             records[i].join(this);
13335         }
13336         var index = this.data.length;
13337         this.data.addAll(records);
13338         this.fireEvent("add", this, records, index);
13339     },
13340
13341     /**
13342      * Remove a Record from the Store and fires the remove event.
13343      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13344      */
13345     remove : function(record){
13346         var index = this.data.indexOf(record);
13347         this.data.removeAt(index);
13348  
13349         if(this.pruneModifiedRecords){
13350             this.modified.remove(record);
13351         }
13352         this.fireEvent("remove", this, record, index);
13353     },
13354
13355     /**
13356      * Remove all Records from the Store and fires the clear event.
13357      */
13358     removeAll : function(){
13359         this.data.clear();
13360         if(this.pruneModifiedRecords){
13361             this.modified = [];
13362         }
13363         this.fireEvent("clear", this);
13364     },
13365
13366     /**
13367      * Inserts Records to the Store at the given index and fires the add event.
13368      * @param {Number} index The start index at which to insert the passed Records.
13369      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13370      */
13371     insert : function(index, records){
13372         records = [].concat(records);
13373         for(var i = 0, len = records.length; i < len; i++){
13374             this.data.insert(index, records[i]);
13375             records[i].join(this);
13376         }
13377         this.fireEvent("add", this, records, index);
13378     },
13379
13380     /**
13381      * Get the index within the cache of the passed Record.
13382      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13383      * @return {Number} The index of the passed Record. Returns -1 if not found.
13384      */
13385     indexOf : function(record){
13386         return this.data.indexOf(record);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the Record with the passed id.
13391      * @param {String} id The id of the Record to find.
13392      * @return {Number} The index of the Record. Returns -1 if not found.
13393      */
13394     indexOfId : function(id){
13395         return this.data.indexOfKey(id);
13396     },
13397
13398     /**
13399      * Get the Record with the specified id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13402      */
13403     getById : function(id){
13404         return this.data.key(id);
13405     },
13406
13407     /**
13408      * Get the Record at the specified index.
13409      * @param {Number} index The index of the Record to find.
13410      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13411      */
13412     getAt : function(index){
13413         return this.data.itemAt(index);
13414     },
13415
13416     /**
13417      * Returns a range of Records between specified indices.
13418      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13419      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13420      * @return {Roo.data.Record[]} An array of Records
13421      */
13422     getRange : function(start, end){
13423         return this.data.getRange(start, end);
13424     },
13425
13426     // private
13427     storeOptions : function(o){
13428         o = Roo.apply({}, o);
13429         delete o.callback;
13430         delete o.scope;
13431         this.lastOptions = o;
13432     },
13433
13434     /**
13435      * Loads the Record cache from the configured Proxy using the configured Reader.
13436      * <p>
13437      * If using remote paging, then the first load call must specify the <em>start</em>
13438      * and <em>limit</em> properties in the options.params property to establish the initial
13439      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13440      * <p>
13441      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13442      * and this call will return before the new data has been loaded. Perform any post-processing
13443      * in a callback function, or in a "load" event handler.</strong>
13444      * <p>
13445      * @param {Object} options An object containing properties which control loading options:<ul>
13446      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13447      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13448      * passed the following arguments:<ul>
13449      * <li>r : Roo.data.Record[]</li>
13450      * <li>options: Options object from the load call</li>
13451      * <li>success: Boolean success indicator</li></ul></li>
13452      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13453      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13454      * </ul>
13455      */
13456     load : function(options){
13457         options = options || {};
13458         if(this.fireEvent("beforeload", this, options) !== false){
13459             this.storeOptions(options);
13460             var p = Roo.apply(options.params || {}, this.baseParams);
13461             // if meta was not loaded from remote source.. try requesting it.
13462             if (!this.reader.metaFromRemote) {
13463                 p._requestMeta = 1;
13464             }
13465             if(this.sortInfo && this.remoteSort){
13466                 var pn = this.paramNames;
13467                 p[pn["sort"]] = this.sortInfo.field;
13468                 p[pn["dir"]] = this.sortInfo.direction;
13469             }
13470             if (this.multiSort) {
13471                 var pn = this.paramNames;
13472                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13473             }
13474             
13475             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13476         }
13477     },
13478
13479     /**
13480      * Reloads the Record cache from the configured Proxy using the configured Reader and
13481      * the options from the last load operation performed.
13482      * @param {Object} options (optional) An object containing properties which may override the options
13483      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13484      * the most recently used options are reused).
13485      */
13486     reload : function(options){
13487         this.load(Roo.applyIf(options||{}, this.lastOptions));
13488     },
13489
13490     // private
13491     // Called as a callback by the Reader during a load operation.
13492     loadRecords : function(o, options, success){
13493         if(!o || success === false){
13494             if(success !== false){
13495                 this.fireEvent("load", this, [], options, o);
13496             }
13497             if(options.callback){
13498                 options.callback.call(options.scope || this, [], options, false);
13499             }
13500             return;
13501         }
13502         // if data returned failure - throw an exception.
13503         if (o.success === false) {
13504             // show a message if no listener is registered.
13505             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13506                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13507             }
13508             // loadmask wil be hooked into this..
13509             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13510             return;
13511         }
13512         var r = o.records, t = o.totalRecords || r.length;
13513         
13514         this.fireEvent("beforeloadadd", this, r, options, o);
13515         
13516         if(!options || options.add !== true){
13517             if(this.pruneModifiedRecords){
13518                 this.modified = [];
13519             }
13520             for(var i = 0, len = r.length; i < len; i++){
13521                 r[i].join(this);
13522             }
13523             if(this.snapshot){
13524                 this.data = this.snapshot;
13525                 delete this.snapshot;
13526             }
13527             this.data.clear();
13528             this.data.addAll(r);
13529             this.totalLength = t;
13530             this.applySort();
13531             this.fireEvent("datachanged", this);
13532         }else{
13533             this.totalLength = Math.max(t, this.data.length+r.length);
13534             this.add(r);
13535         }
13536         
13537         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13538                 
13539             var e = new Roo.data.Record({});
13540
13541             e.set(this.parent.displayField, this.parent.emptyTitle);
13542             e.set(this.parent.valueField, '');
13543
13544             this.insert(0, e);
13545         }
13546             
13547         this.fireEvent("load", this, r, options, o);
13548         if(options.callback){
13549             options.callback.call(options.scope || this, r, options, true);
13550         }
13551     },
13552
13553
13554     /**
13555      * Loads data from a passed data block. A Reader which understands the format of the data
13556      * must have been configured in the constructor.
13557      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13558      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13559      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13560      */
13561     loadData : function(o, append){
13562         var r = this.reader.readRecords(o);
13563         this.loadRecords(r, {add: append}, true);
13564     },
13565     
13566      /**
13567      * using 'cn' the nested child reader read the child array into it's child stores.
13568      * @param {Object} rec The record with a 'children array
13569      */
13570     loadDataFromChildren : function(rec)
13571     {
13572         this.loadData(this.reader.toLoadData(rec));
13573     },
13574     
13575
13576     /**
13577      * Gets the number of cached records.
13578      * <p>
13579      * <em>If using paging, this may not be the total size of the dataset. If the data object
13580      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13581      * the data set size</em>
13582      */
13583     getCount : function(){
13584         return this.data.length || 0;
13585     },
13586
13587     /**
13588      * Gets the total number of records in the dataset as returned by the server.
13589      * <p>
13590      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13591      * the dataset size</em>
13592      */
13593     getTotalCount : function(){
13594         return this.totalLength || 0;
13595     },
13596
13597     /**
13598      * Returns the sort state of the Store as an object with two properties:
13599      * <pre><code>
13600  field {String} The name of the field by which the Records are sorted
13601  direction {String} The sort order, "ASC" or "DESC"
13602      * </code></pre>
13603      */
13604     getSortState : function(){
13605         return this.sortInfo;
13606     },
13607
13608     // private
13609     applySort : function(){
13610         if(this.sortInfo && !this.remoteSort){
13611             var s = this.sortInfo, f = s.field;
13612             var st = this.fields.get(f).sortType;
13613             var fn = function(r1, r2){
13614                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13615                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13616             };
13617             this.data.sort(s.direction, fn);
13618             if(this.snapshot && this.snapshot != this.data){
13619                 this.snapshot.sort(s.direction, fn);
13620             }
13621         }
13622     },
13623
13624     /**
13625      * Sets the default sort column and order to be used by the next load operation.
13626      * @param {String} fieldName The name of the field to sort by.
13627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13628      */
13629     setDefaultSort : function(field, dir){
13630         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13631     },
13632
13633     /**
13634      * Sort the Records.
13635      * If remote sorting is used, the sort is performed on the server, and the cache is
13636      * reloaded. If local sorting is used, the cache is sorted internally.
13637      * @param {String} fieldName The name of the field to sort by.
13638      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13639      */
13640     sort : function(fieldName, dir){
13641         var f = this.fields.get(fieldName);
13642         if(!dir){
13643             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13644             
13645             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13646                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13647             }else{
13648                 dir = f.sortDir;
13649             }
13650         }
13651         this.sortToggle[f.name] = dir;
13652         this.sortInfo = {field: f.name, direction: dir};
13653         if(!this.remoteSort){
13654             this.applySort();
13655             this.fireEvent("datachanged", this);
13656         }else{
13657             this.load(this.lastOptions);
13658         }
13659     },
13660
13661     /**
13662      * Calls the specified function for each of the Records in the cache.
13663      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13664      * Returning <em>false</em> aborts and exits the iteration.
13665      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13666      */
13667     each : function(fn, scope){
13668         this.data.each(fn, scope);
13669     },
13670
13671     /**
13672      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13673      * (e.g., during paging).
13674      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13675      */
13676     getModifiedRecords : function(){
13677         return this.modified;
13678     },
13679
13680     // private
13681     createFilterFn : function(property, value, anyMatch){
13682         if(!value.exec){ // not a regex
13683             value = String(value);
13684             if(value.length == 0){
13685                 return false;
13686             }
13687             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13688         }
13689         return function(r){
13690             return value.test(r.data[property]);
13691         };
13692     },
13693
13694     /**
13695      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13696      * @param {String} property A field on your records
13697      * @param {Number} start The record index to start at (defaults to 0)
13698      * @param {Number} end The last record index to include (defaults to length - 1)
13699      * @return {Number} The sum
13700      */
13701     sum : function(property, start, end){
13702         var rs = this.data.items, v = 0;
13703         start = start || 0;
13704         end = (end || end === 0) ? end : rs.length-1;
13705
13706         for(var i = start; i <= end; i++){
13707             v += (rs[i].data[property] || 0);
13708         }
13709         return v;
13710     },
13711
13712     /**
13713      * Filter the records by a specified property.
13714      * @param {String} field A field on your records
13715      * @param {String/RegExp} value Either a string that the field
13716      * should start with or a RegExp to test against the field
13717      * @param {Boolean} anyMatch True to match any part not just the beginning
13718      */
13719     filter : function(property, value, anyMatch){
13720         var fn = this.createFilterFn(property, value, anyMatch);
13721         return fn ? this.filterBy(fn) : this.clearFilter();
13722     },
13723
13724     /**
13725      * Filter by a function. The specified function will be called with each
13726      * record in this data source. If the function returns true the record is included,
13727      * otherwise it is filtered.
13728      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13729      * @param {Object} scope (optional) The scope of the function (defaults to this)
13730      */
13731     filterBy : function(fn, scope){
13732         this.snapshot = this.snapshot || this.data;
13733         this.data = this.queryBy(fn, scope||this);
13734         this.fireEvent("datachanged", this);
13735     },
13736
13737     /**
13738      * Query the records by a specified property.
13739      * @param {String} field A field on your records
13740      * @param {String/RegExp} value Either a string that the field
13741      * should start with or a RegExp to test against the field
13742      * @param {Boolean} anyMatch True to match any part not just the beginning
13743      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13744      */
13745     query : function(property, value, anyMatch){
13746         var fn = this.createFilterFn(property, value, anyMatch);
13747         return fn ? this.queryBy(fn) : this.data.clone();
13748     },
13749
13750     /**
13751      * Query by a function. The specified function will be called with each
13752      * record in this data source. If the function returns true the record is included
13753      * in the results.
13754      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13755      * @param {Object} scope (optional) The scope of the function (defaults to this)
13756       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13757      **/
13758     queryBy : function(fn, scope){
13759         var data = this.snapshot || this.data;
13760         return data.filterBy(fn, scope||this);
13761     },
13762
13763     /**
13764      * Collects unique values for a particular dataIndex from this store.
13765      * @param {String} dataIndex The property to collect
13766      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13767      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13768      * @return {Array} An array of the unique values
13769      **/
13770     collect : function(dataIndex, allowNull, bypassFilter){
13771         var d = (bypassFilter === true && this.snapshot) ?
13772                 this.snapshot.items : this.data.items;
13773         var v, sv, r = [], l = {};
13774         for(var i = 0, len = d.length; i < len; i++){
13775             v = d[i].data[dataIndex];
13776             sv = String(v);
13777             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13778                 l[sv] = true;
13779                 r[r.length] = v;
13780             }
13781         }
13782         return r;
13783     },
13784
13785     /**
13786      * Revert to a view of the Record cache with no filtering applied.
13787      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13788      */
13789     clearFilter : function(suppressEvent){
13790         if(this.snapshot && this.snapshot != this.data){
13791             this.data = this.snapshot;
13792             delete this.snapshot;
13793             if(suppressEvent !== true){
13794                 this.fireEvent("datachanged", this);
13795             }
13796         }
13797     },
13798
13799     // private
13800     afterEdit : function(record){
13801         if(this.modified.indexOf(record) == -1){
13802             this.modified.push(record);
13803         }
13804         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13805     },
13806     
13807     // private
13808     afterReject : function(record){
13809         this.modified.remove(record);
13810         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13811     },
13812
13813     // private
13814     afterCommit : function(record){
13815         this.modified.remove(record);
13816         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13817     },
13818
13819     /**
13820      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13821      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13822      */
13823     commitChanges : function(){
13824         var m = this.modified.slice(0);
13825         this.modified = [];
13826         for(var i = 0, len = m.length; i < len; i++){
13827             m[i].commit();
13828         }
13829     },
13830
13831     /**
13832      * Cancel outstanding changes on all changed records.
13833      */
13834     rejectChanges : function(){
13835         var m = this.modified.slice(0);
13836         this.modified = [];
13837         for(var i = 0, len = m.length; i < len; i++){
13838             m[i].reject();
13839         }
13840     },
13841
13842     onMetaChange : function(meta, rtype, o){
13843         this.recordType = rtype;
13844         this.fields = rtype.prototype.fields;
13845         delete this.snapshot;
13846         this.sortInfo = meta.sortInfo || this.sortInfo;
13847         this.modified = [];
13848         this.fireEvent('metachange', this, this.reader.meta);
13849     },
13850     
13851     moveIndex : function(data, type)
13852     {
13853         var index = this.indexOf(data);
13854         
13855         var newIndex = index + type;
13856         
13857         this.remove(data);
13858         
13859         this.insert(newIndex, data);
13860         
13861     }
13862 });/*
13863  * Based on:
13864  * Ext JS Library 1.1.1
13865  * Copyright(c) 2006-2007, Ext JS, LLC.
13866  *
13867  * Originally Released Under LGPL - original licence link has changed is not relivant.
13868  *
13869  * Fork - LGPL
13870  * <script type="text/javascript">
13871  */
13872
13873 /**
13874  * @class Roo.data.SimpleStore
13875  * @extends Roo.data.Store
13876  * Small helper class to make creating Stores from Array data easier.
13877  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13878  * @cfg {Array} fields An array of field definition objects, or field name strings.
13879  * @cfg {Object} an existing reader (eg. copied from another store)
13880  * @cfg {Array} data The multi-dimensional array of data
13881  * @constructor
13882  * @param {Object} config
13883  */
13884 Roo.data.SimpleStore = function(config)
13885 {
13886     Roo.data.SimpleStore.superclass.constructor.call(this, {
13887         isLocal : true,
13888         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13889                 id: config.id
13890             },
13891             Roo.data.Record.create(config.fields)
13892         ),
13893         proxy : new Roo.data.MemoryProxy(config.data)
13894     });
13895     this.load();
13896 };
13897 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13898  * Based on:
13899  * Ext JS Library 1.1.1
13900  * Copyright(c) 2006-2007, Ext JS, LLC.
13901  *
13902  * Originally Released Under LGPL - original licence link has changed is not relivant.
13903  *
13904  * Fork - LGPL
13905  * <script type="text/javascript">
13906  */
13907
13908 /**
13909 /**
13910  * @extends Roo.data.Store
13911  * @class Roo.data.JsonStore
13912  * Small helper class to make creating Stores for JSON data easier. <br/>
13913 <pre><code>
13914 var store = new Roo.data.JsonStore({
13915     url: 'get-images.php',
13916     root: 'images',
13917     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13918 });
13919 </code></pre>
13920  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13921  * JsonReader and HttpProxy (unless inline data is provided).</b>
13922  * @cfg {Array} fields An array of field definition objects, or field name strings.
13923  * @constructor
13924  * @param {Object} config
13925  */
13926 Roo.data.JsonStore = function(c){
13927     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13928         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13929         reader: new Roo.data.JsonReader(c, c.fields)
13930     }));
13931 };
13932 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13933  * Based on:
13934  * Ext JS Library 1.1.1
13935  * Copyright(c) 2006-2007, Ext JS, LLC.
13936  *
13937  * Originally Released Under LGPL - original licence link has changed is not relivant.
13938  *
13939  * Fork - LGPL
13940  * <script type="text/javascript">
13941  */
13942
13943  
13944 Roo.data.Field = function(config){
13945     if(typeof config == "string"){
13946         config = {name: config};
13947     }
13948     Roo.apply(this, config);
13949     
13950     if(!this.type){
13951         this.type = "auto";
13952     }
13953     
13954     var st = Roo.data.SortTypes;
13955     // named sortTypes are supported, here we look them up
13956     if(typeof this.sortType == "string"){
13957         this.sortType = st[this.sortType];
13958     }
13959     
13960     // set default sortType for strings and dates
13961     if(!this.sortType){
13962         switch(this.type){
13963             case "string":
13964                 this.sortType = st.asUCString;
13965                 break;
13966             case "date":
13967                 this.sortType = st.asDate;
13968                 break;
13969             default:
13970                 this.sortType = st.none;
13971         }
13972     }
13973
13974     // define once
13975     var stripRe = /[\$,%]/g;
13976
13977     // prebuilt conversion function for this field, instead of
13978     // switching every time we're reading a value
13979     if(!this.convert){
13980         var cv, dateFormat = this.dateFormat;
13981         switch(this.type){
13982             case "":
13983             case "auto":
13984             case undefined:
13985                 cv = function(v){ return v; };
13986                 break;
13987             case "string":
13988                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13989                 break;
13990             case "int":
13991                 cv = function(v){
13992                     return v !== undefined && v !== null && v !== '' ?
13993                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13994                     };
13995                 break;
13996             case "float":
13997                 cv = function(v){
13998                     return v !== undefined && v !== null && v !== '' ?
13999                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14000                     };
14001                 break;
14002             case "bool":
14003             case "boolean":
14004                 cv = function(v){ return v === true || v === "true" || v == 1; };
14005                 break;
14006             case "date":
14007                 cv = function(v){
14008                     if(!v){
14009                         return '';
14010                     }
14011                     if(v instanceof Date){
14012                         return v;
14013                     }
14014                     if(dateFormat){
14015                         if(dateFormat == "timestamp"){
14016                             return new Date(v*1000);
14017                         }
14018                         return Date.parseDate(v, dateFormat);
14019                     }
14020                     var parsed = Date.parse(v);
14021                     return parsed ? new Date(parsed) : null;
14022                 };
14023              break;
14024             
14025         }
14026         this.convert = cv;
14027     }
14028 };
14029
14030 Roo.data.Field.prototype = {
14031     dateFormat: null,
14032     defaultValue: "",
14033     mapping: null,
14034     sortType : null,
14035     sortDir : "ASC"
14036 };/*
14037  * Based on:
14038  * Ext JS Library 1.1.1
14039  * Copyright(c) 2006-2007, Ext JS, LLC.
14040  *
14041  * Originally Released Under LGPL - original licence link has changed is not relivant.
14042  *
14043  * Fork - LGPL
14044  * <script type="text/javascript">
14045  */
14046  
14047 // Base class for reading structured data from a data source.  This class is intended to be
14048 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14049
14050 /**
14051  * @class Roo.data.DataReader
14052  * Base class for reading structured data from a data source.  This class is intended to be
14053  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14054  */
14055
14056 Roo.data.DataReader = function(meta, recordType){
14057     
14058     this.meta = meta;
14059     
14060     this.recordType = recordType instanceof Array ? 
14061         Roo.data.Record.create(recordType) : recordType;
14062 };
14063
14064 Roo.data.DataReader.prototype = {
14065     
14066     
14067     readerType : 'Data',
14068      /**
14069      * Create an empty record
14070      * @param {Object} data (optional) - overlay some values
14071      * @return {Roo.data.Record} record created.
14072      */
14073     newRow :  function(d) {
14074         var da =  {};
14075         this.recordType.prototype.fields.each(function(c) {
14076             switch( c.type) {
14077                 case 'int' : da[c.name] = 0; break;
14078                 case 'date' : da[c.name] = new Date(); break;
14079                 case 'float' : da[c.name] = 0.0; break;
14080                 case 'boolean' : da[c.name] = false; break;
14081                 default : da[c.name] = ""; break;
14082             }
14083             
14084         });
14085         return new this.recordType(Roo.apply(da, d));
14086     }
14087     
14088     
14089 };/*
14090  * Based on:
14091  * Ext JS Library 1.1.1
14092  * Copyright(c) 2006-2007, Ext JS, LLC.
14093  *
14094  * Originally Released Under LGPL - original licence link has changed is not relivant.
14095  *
14096  * Fork - LGPL
14097  * <script type="text/javascript">
14098  */
14099
14100 /**
14101  * @class Roo.data.DataProxy
14102  * @extends Roo.data.Observable
14103  * This class is an abstract base class for implementations which provide retrieval of
14104  * unformatted data objects.<br>
14105  * <p>
14106  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14107  * (of the appropriate type which knows how to parse the data object) to provide a block of
14108  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14109  * <p>
14110  * Custom implementations must implement the load method as described in
14111  * {@link Roo.data.HttpProxy#load}.
14112  */
14113 Roo.data.DataProxy = function(){
14114     this.addEvents({
14115         /**
14116          * @event beforeload
14117          * Fires before a network request is made to retrieve a data object.
14118          * @param {Object} This DataProxy object.
14119          * @param {Object} params The params parameter to the load function.
14120          */
14121         beforeload : true,
14122         /**
14123          * @event load
14124          * Fires before the load method's callback is called.
14125          * @param {Object} This DataProxy object.
14126          * @param {Object} o The data object.
14127          * @param {Object} arg The callback argument object passed to the load function.
14128          */
14129         load : true,
14130         /**
14131          * @event loadexception
14132          * Fires if an Exception occurs during data retrieval.
14133          * @param {Object} This DataProxy object.
14134          * @param {Object} o The data object.
14135          * @param {Object} arg The callback argument object passed to the load function.
14136          * @param {Object} e The Exception.
14137          */
14138         loadexception : true
14139     });
14140     Roo.data.DataProxy.superclass.constructor.call(this);
14141 };
14142
14143 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14144
14145     /**
14146      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14147      */
14148 /*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158 /**
14159  * @class Roo.data.MemoryProxy
14160  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14161  * to the Reader when its load method is called.
14162  * @constructor
14163  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14164  */
14165 Roo.data.MemoryProxy = function(data){
14166     if (data.data) {
14167         data = data.data;
14168     }
14169     Roo.data.MemoryProxy.superclass.constructor.call(this);
14170     this.data = data;
14171 };
14172
14173 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14174     
14175     /**
14176      * Load data from the requested source (in this case an in-memory
14177      * data object passed to the constructor), read the data object into
14178      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14179      * process that block using the passed callback.
14180      * @param {Object} params This parameter is not used by the MemoryProxy class.
14181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14182      * object into a block of Roo.data.Records.
14183      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14184      * The function must be passed <ul>
14185      * <li>The Record block object</li>
14186      * <li>The "arg" argument from the load function</li>
14187      * <li>A boolean success indicator</li>
14188      * </ul>
14189      * @param {Object} scope The scope in which to call the callback
14190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14191      */
14192     load : function(params, reader, callback, scope, arg){
14193         params = params || {};
14194         var result;
14195         try {
14196             result = reader.readRecords(params.data ? params.data :this.data);
14197         }catch(e){
14198             this.fireEvent("loadexception", this, arg, null, e);
14199             callback.call(scope, null, arg, false);
14200             return;
14201         }
14202         callback.call(scope, result, arg, true);
14203     },
14204     
14205     // private
14206     update : function(params, records){
14207         
14208     }
14209 });/*
14210  * Based on:
14211  * Ext JS Library 1.1.1
14212  * Copyright(c) 2006-2007, Ext JS, LLC.
14213  *
14214  * Originally Released Under LGPL - original licence link has changed is not relivant.
14215  *
14216  * Fork - LGPL
14217  * <script type="text/javascript">
14218  */
14219 /**
14220  * @class Roo.data.HttpProxy
14221  * @extends Roo.data.DataProxy
14222  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14223  * configured to reference a certain URL.<br><br>
14224  * <p>
14225  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14226  * from which the running page was served.<br><br>
14227  * <p>
14228  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14229  * <p>
14230  * Be aware that to enable the browser to parse an XML document, the server must set
14231  * the Content-Type header in the HTTP response to "text/xml".
14232  * @constructor
14233  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14234  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14235  * will be used to make the request.
14236  */
14237 Roo.data.HttpProxy = function(conn){
14238     Roo.data.HttpProxy.superclass.constructor.call(this);
14239     // is conn a conn config or a real conn?
14240     this.conn = conn;
14241     this.useAjax = !conn || !conn.events;
14242   
14243 };
14244
14245 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14246     // thse are take from connection...
14247     
14248     /**
14249      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14250      */
14251     /**
14252      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14253      * extra parameters to each request made by this object. (defaults to undefined)
14254      */
14255     /**
14256      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14257      *  to each request made by this object. (defaults to undefined)
14258      */
14259     /**
14260      * @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)
14261      */
14262     /**
14263      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14264      */
14265      /**
14266      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14267      * @type Boolean
14268      */
14269   
14270
14271     /**
14272      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14273      * @type Boolean
14274      */
14275     /**
14276      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14277      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14278      * a finer-grained basis than the DataProxy events.
14279      */
14280     getConnection : function(){
14281         return this.useAjax ? Roo.Ajax : this.conn;
14282     },
14283
14284     /**
14285      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14286      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14287      * process that block using the passed callback.
14288      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14289      * for the request to the remote server.
14290      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14291      * object into a block of Roo.data.Records.
14292      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14293      * The function must be passed <ul>
14294      * <li>The Record block object</li>
14295      * <li>The "arg" argument from the load function</li>
14296      * <li>A boolean success indicator</li>
14297      * </ul>
14298      * @param {Object} scope The scope in which to call the callback
14299      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14300      */
14301     load : function(params, reader, callback, scope, arg){
14302         if(this.fireEvent("beforeload", this, params) !== false){
14303             var  o = {
14304                 params : params || {},
14305                 request: {
14306                     callback : callback,
14307                     scope : scope,
14308                     arg : arg
14309                 },
14310                 reader: reader,
14311                 callback : this.loadResponse,
14312                 scope: this
14313             };
14314             if(this.useAjax){
14315                 Roo.applyIf(o, this.conn);
14316                 if(this.activeRequest){
14317                     Roo.Ajax.abort(this.activeRequest);
14318                 }
14319                 this.activeRequest = Roo.Ajax.request(o);
14320             }else{
14321                 this.conn.request(o);
14322             }
14323         }else{
14324             callback.call(scope||this, null, arg, false);
14325         }
14326     },
14327
14328     // private
14329     loadResponse : function(o, success, response){
14330         delete this.activeRequest;
14331         if(!success){
14332             this.fireEvent("loadexception", this, o, response);
14333             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14334             return;
14335         }
14336         var result;
14337         try {
14338             result = o.reader.read(response);
14339         }catch(e){
14340             this.fireEvent("loadexception", this, o, response, e);
14341             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14342             return;
14343         }
14344         
14345         this.fireEvent("load", this, o, o.request.arg);
14346         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14347     },
14348
14349     // private
14350     update : function(dataSet){
14351
14352     },
14353
14354     // private
14355     updateResponse : function(dataSet){
14356
14357     }
14358 });/*
14359  * Based on:
14360  * Ext JS Library 1.1.1
14361  * Copyright(c) 2006-2007, Ext JS, LLC.
14362  *
14363  * Originally Released Under LGPL - original licence link has changed is not relivant.
14364  *
14365  * Fork - LGPL
14366  * <script type="text/javascript">
14367  */
14368
14369 /**
14370  * @class Roo.data.ScriptTagProxy
14371  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14372  * other than the originating domain of the running page.<br><br>
14373  * <p>
14374  * <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
14375  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14376  * <p>
14377  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14378  * source code that is used as the source inside a &lt;script> tag.<br><br>
14379  * <p>
14380  * In order for the browser to process the returned data, the server must wrap the data object
14381  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14382  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14383  * depending on whether the callback name was passed:
14384  * <p>
14385  * <pre><code>
14386 boolean scriptTag = false;
14387 String cb = request.getParameter("callback");
14388 if (cb != null) {
14389     scriptTag = true;
14390     response.setContentType("text/javascript");
14391 } else {
14392     response.setContentType("application/x-json");
14393 }
14394 Writer out = response.getWriter();
14395 if (scriptTag) {
14396     out.write(cb + "(");
14397 }
14398 out.print(dataBlock.toJsonString());
14399 if (scriptTag) {
14400     out.write(");");
14401 }
14402 </pre></code>
14403  *
14404  * @constructor
14405  * @param {Object} config A configuration object.
14406  */
14407 Roo.data.ScriptTagProxy = function(config){
14408     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14409     Roo.apply(this, config);
14410     this.head = document.getElementsByTagName("head")[0];
14411 };
14412
14413 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14414
14415 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14416     /**
14417      * @cfg {String} url The URL from which to request the data object.
14418      */
14419     /**
14420      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14421      */
14422     timeout : 30000,
14423     /**
14424      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14425      * the server the name of the callback function set up by the load call to process the returned data object.
14426      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14427      * javascript output which calls this named function passing the data object as its only parameter.
14428      */
14429     callbackParam : "callback",
14430     /**
14431      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14432      * name to the request.
14433      */
14434     nocache : true,
14435
14436     /**
14437      * Load data from the configured URL, read the data object into
14438      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14439      * process that block using the passed callback.
14440      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14441      * for the request to the remote server.
14442      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14443      * object into a block of Roo.data.Records.
14444      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14445      * The function must be passed <ul>
14446      * <li>The Record block object</li>
14447      * <li>The "arg" argument from the load function</li>
14448      * <li>A boolean success indicator</li>
14449      * </ul>
14450      * @param {Object} scope The scope in which to call the callback
14451      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14452      */
14453     load : function(params, reader, callback, scope, arg){
14454         if(this.fireEvent("beforeload", this, params) !== false){
14455
14456             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14457
14458             var url = this.url;
14459             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14460             if(this.nocache){
14461                 url += "&_dc=" + (new Date().getTime());
14462             }
14463             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14464             var trans = {
14465                 id : transId,
14466                 cb : "stcCallback"+transId,
14467                 scriptId : "stcScript"+transId,
14468                 params : params,
14469                 arg : arg,
14470                 url : url,
14471                 callback : callback,
14472                 scope : scope,
14473                 reader : reader
14474             };
14475             var conn = this;
14476
14477             window[trans.cb] = function(o){
14478                 conn.handleResponse(o, trans);
14479             };
14480
14481             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14482
14483             if(this.autoAbort !== false){
14484                 this.abort();
14485             }
14486
14487             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14488
14489             var script = document.createElement("script");
14490             script.setAttribute("src", url);
14491             script.setAttribute("type", "text/javascript");
14492             script.setAttribute("id", trans.scriptId);
14493             this.head.appendChild(script);
14494
14495             this.trans = trans;
14496         }else{
14497             callback.call(scope||this, null, arg, false);
14498         }
14499     },
14500
14501     // private
14502     isLoading : function(){
14503         return this.trans ? true : false;
14504     },
14505
14506     /**
14507      * Abort the current server request.
14508      */
14509     abort : function(){
14510         if(this.isLoading()){
14511             this.destroyTrans(this.trans);
14512         }
14513     },
14514
14515     // private
14516     destroyTrans : function(trans, isLoaded){
14517         this.head.removeChild(document.getElementById(trans.scriptId));
14518         clearTimeout(trans.timeoutId);
14519         if(isLoaded){
14520             window[trans.cb] = undefined;
14521             try{
14522                 delete window[trans.cb];
14523             }catch(e){}
14524         }else{
14525             // if hasn't been loaded, wait for load to remove it to prevent script error
14526             window[trans.cb] = function(){
14527                 window[trans.cb] = undefined;
14528                 try{
14529                     delete window[trans.cb];
14530                 }catch(e){}
14531             };
14532         }
14533     },
14534
14535     // private
14536     handleResponse : function(o, trans){
14537         this.trans = false;
14538         this.destroyTrans(trans, true);
14539         var result;
14540         try {
14541             result = trans.reader.readRecords(o);
14542         }catch(e){
14543             this.fireEvent("loadexception", this, o, trans.arg, e);
14544             trans.callback.call(trans.scope||window, null, trans.arg, false);
14545             return;
14546         }
14547         this.fireEvent("load", this, o, trans.arg);
14548         trans.callback.call(trans.scope||window, result, trans.arg, true);
14549     },
14550
14551     // private
14552     handleFailure : function(trans){
14553         this.trans = false;
14554         this.destroyTrans(trans, false);
14555         this.fireEvent("loadexception", this, null, trans.arg);
14556         trans.callback.call(trans.scope||window, null, trans.arg, false);
14557     }
14558 });/*
14559  * Based on:
14560  * Ext JS Library 1.1.1
14561  * Copyright(c) 2006-2007, Ext JS, LLC.
14562  *
14563  * Originally Released Under LGPL - original licence link has changed is not relivant.
14564  *
14565  * Fork - LGPL
14566  * <script type="text/javascript">
14567  */
14568
14569 /**
14570  * @class Roo.data.JsonReader
14571  * @extends Roo.data.DataReader
14572  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14573  * based on mappings in a provided Roo.data.Record constructor.
14574  * 
14575  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14576  * in the reply previously. 
14577  * 
14578  * <p>
14579  * Example code:
14580  * <pre><code>
14581 var RecordDef = Roo.data.Record.create([
14582     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14583     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14584 ]);
14585 var myReader = new Roo.data.JsonReader({
14586     totalProperty: "results",    // The property which contains the total dataset size (optional)
14587     root: "rows",                // The property which contains an Array of row objects
14588     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14589 }, RecordDef);
14590 </code></pre>
14591  * <p>
14592  * This would consume a JSON file like this:
14593  * <pre><code>
14594 { 'results': 2, 'rows': [
14595     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14596     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14597 }
14598 </code></pre>
14599  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14600  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14601  * paged from the remote server.
14602  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14603  * @cfg {String} root name of the property which contains the Array of row objects.
14604  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14605  * @cfg {Array} fields Array of field definition objects
14606  * @constructor
14607  * Create a new JsonReader
14608  * @param {Object} meta Metadata configuration options
14609  * @param {Object} recordType Either an Array of field definition objects,
14610  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14611  */
14612 Roo.data.JsonReader = function(meta, recordType){
14613     
14614     meta = meta || {};
14615     // set some defaults:
14616     Roo.applyIf(meta, {
14617         totalProperty: 'total',
14618         successProperty : 'success',
14619         root : 'data',
14620         id : 'id'
14621     });
14622     
14623     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14624 };
14625 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14626     
14627     readerType : 'Json',
14628     
14629     /**
14630      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14631      * Used by Store query builder to append _requestMeta to params.
14632      * 
14633      */
14634     metaFromRemote : false,
14635     /**
14636      * This method is only used by a DataProxy which has retrieved data from a remote server.
14637      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14638      * @return {Object} data A data block which is used by an Roo.data.Store object as
14639      * a cache of Roo.data.Records.
14640      */
14641     read : function(response){
14642         var json = response.responseText;
14643        
14644         var o = /* eval:var:o */ eval("("+json+")");
14645         if(!o) {
14646             throw {message: "JsonReader.read: Json object not found"};
14647         }
14648         
14649         if(o.metaData){
14650             
14651             delete this.ef;
14652             this.metaFromRemote = true;
14653             this.meta = o.metaData;
14654             this.recordType = Roo.data.Record.create(o.metaData.fields);
14655             this.onMetaChange(this.meta, this.recordType, o);
14656         }
14657         return this.readRecords(o);
14658     },
14659
14660     // private function a store will implement
14661     onMetaChange : function(meta, recordType, o){
14662
14663     },
14664
14665     /**
14666          * @ignore
14667          */
14668     simpleAccess: function(obj, subsc) {
14669         return obj[subsc];
14670     },
14671
14672         /**
14673          * @ignore
14674          */
14675     getJsonAccessor: function(){
14676         var re = /[\[\.]/;
14677         return function(expr) {
14678             try {
14679                 return(re.test(expr))
14680                     ? new Function("obj", "return obj." + expr)
14681                     : function(obj){
14682                         return obj[expr];
14683                     };
14684             } catch(e){}
14685             return Roo.emptyFn;
14686         };
14687     }(),
14688
14689     /**
14690      * Create a data block containing Roo.data.Records from an XML document.
14691      * @param {Object} o An object which contains an Array of row objects in the property specified
14692      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14693      * which contains the total size of the dataset.
14694      * @return {Object} data A data block which is used by an Roo.data.Store object as
14695      * a cache of Roo.data.Records.
14696      */
14697     readRecords : function(o){
14698         /**
14699          * After any data loads, the raw JSON data is available for further custom processing.
14700          * @type Object
14701          */
14702         this.o = o;
14703         var s = this.meta, Record = this.recordType,
14704             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14705
14706 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14707         if (!this.ef) {
14708             if(s.totalProperty) {
14709                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14710                 }
14711                 if(s.successProperty) {
14712                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14713                 }
14714                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14715                 if (s.id) {
14716                         var g = this.getJsonAccessor(s.id);
14717                         this.getId = function(rec) {
14718                                 var r = g(rec);  
14719                                 return (r === undefined || r === "") ? null : r;
14720                         };
14721                 } else {
14722                         this.getId = function(){return null;};
14723                 }
14724             this.ef = [];
14725             for(var jj = 0; jj < fl; jj++){
14726                 f = fi[jj];
14727                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14728                 this.ef[jj] = this.getJsonAccessor(map);
14729             }
14730         }
14731
14732         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14733         if(s.totalProperty){
14734             var vt = parseInt(this.getTotal(o), 10);
14735             if(!isNaN(vt)){
14736                 totalRecords = vt;
14737             }
14738         }
14739         if(s.successProperty){
14740             var vs = this.getSuccess(o);
14741             if(vs === false || vs === 'false'){
14742                 success = false;
14743             }
14744         }
14745         var records = [];
14746         for(var i = 0; i < c; i++){
14747                 var n = root[i];
14748             var values = {};
14749             var id = this.getId(n);
14750             for(var j = 0; j < fl; j++){
14751                 f = fi[j];
14752             var v = this.ef[j](n);
14753             if (!f.convert) {
14754                 Roo.log('missing convert for ' + f.name);
14755                 Roo.log(f);
14756                 continue;
14757             }
14758             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14759             }
14760             var record = new Record(values, id);
14761             record.json = n;
14762             records[i] = record;
14763         }
14764         return {
14765             raw : o,
14766             success : success,
14767             records : records,
14768             totalRecords : totalRecords
14769         };
14770     },
14771     // used when loading children.. @see loadDataFromChildren
14772     toLoadData: function(rec)
14773     {
14774         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14775         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14776         return { data : data, total : data.length };
14777         
14778     }
14779 });/*
14780  * Based on:
14781  * Ext JS Library 1.1.1
14782  * Copyright(c) 2006-2007, Ext JS, LLC.
14783  *
14784  * Originally Released Under LGPL - original licence link has changed is not relivant.
14785  *
14786  * Fork - LGPL
14787  * <script type="text/javascript">
14788  */
14789
14790 /**
14791  * @class Roo.data.ArrayReader
14792  * @extends Roo.data.DataReader
14793  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14794  * Each element of that Array represents a row of data fields. The
14795  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14796  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14797  * <p>
14798  * Example code:.
14799  * <pre><code>
14800 var RecordDef = Roo.data.Record.create([
14801     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14802     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14803 ]);
14804 var myReader = new Roo.data.ArrayReader({
14805     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14806 }, RecordDef);
14807 </code></pre>
14808  * <p>
14809  * This would consume an Array like this:
14810  * <pre><code>
14811 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14812   </code></pre>
14813  
14814  * @constructor
14815  * Create a new JsonReader
14816  * @param {Object} meta Metadata configuration options.
14817  * @param {Object|Array} recordType Either an Array of field definition objects
14818  * 
14819  * @cfg {Array} fields Array of field definition objects
14820  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14821  * as specified to {@link Roo.data.Record#create},
14822  * or an {@link Roo.data.Record} object
14823  *
14824  * 
14825  * created using {@link Roo.data.Record#create}.
14826  */
14827 Roo.data.ArrayReader = function(meta, recordType)
14828 {    
14829     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14830 };
14831
14832 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14833     
14834       /**
14835      * Create a data block containing Roo.data.Records from an XML document.
14836      * @param {Object} o An Array of row objects which represents the dataset.
14837      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14838      * a cache of Roo.data.Records.
14839      */
14840     readRecords : function(o)
14841     {
14842         var sid = this.meta ? this.meta.id : null;
14843         var recordType = this.recordType, fields = recordType.prototype.fields;
14844         var records = [];
14845         var root = o;
14846         for(var i = 0; i < root.length; i++){
14847                 var n = root[i];
14848             var values = {};
14849             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14850             for(var j = 0, jlen = fields.length; j < jlen; j++){
14851                 var f = fields.items[j];
14852                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14853                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14854                 v = f.convert(v);
14855                 values[f.name] = v;
14856             }
14857             var record = new recordType(values, id);
14858             record.json = n;
14859             records[records.length] = record;
14860         }
14861         return {
14862             records : records,
14863             totalRecords : records.length
14864         };
14865     },
14866     // used when loading children.. @see loadDataFromChildren
14867     toLoadData: function(rec)
14868     {
14869         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14870         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14871         
14872     }
14873     
14874     
14875 });/*
14876  * - LGPL
14877  * * 
14878  */
14879
14880 /**
14881  * @class Roo.bootstrap.ComboBox
14882  * @extends Roo.bootstrap.TriggerField
14883  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14884  * @cfg {Boolean} append (true|false) default false
14885  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14886  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14887  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14888  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14889  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14890  * @cfg {Boolean} animate default true
14891  * @cfg {Boolean} emptyResultText only for touch device
14892  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14893  * @cfg {String} emptyTitle default ''
14894  * @cfg {Number} width fixed with? experimental
14895  * @constructor
14896  * Create a new ComboBox.
14897  * @param {Object} config Configuration options
14898  */
14899 Roo.bootstrap.ComboBox = function(config){
14900     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14901     this.addEvents({
14902         /**
14903          * @event expand
14904          * Fires when the dropdown list is expanded
14905         * @param {Roo.bootstrap.ComboBox} combo This combo box
14906         */
14907         'expand' : true,
14908         /**
14909          * @event collapse
14910          * Fires when the dropdown list is collapsed
14911         * @param {Roo.bootstrap.ComboBox} combo This combo box
14912         */
14913         'collapse' : true,
14914         /**
14915          * @event beforeselect
14916          * Fires before a list item is selected. Return false to cancel the selection.
14917         * @param {Roo.bootstrap.ComboBox} combo This combo box
14918         * @param {Roo.data.Record} record The data record returned from the underlying store
14919         * @param {Number} index The index of the selected item in the dropdown list
14920         */
14921         'beforeselect' : true,
14922         /**
14923          * @event select
14924          * Fires when a list item is selected
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14927         * @param {Number} index The index of the selected item in the dropdown list
14928         */
14929         'select' : true,
14930         /**
14931          * @event beforequery
14932          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14933          * The event object passed has these properties:
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {String} query The query
14936         * @param {Boolean} forceAll true to force "all" query
14937         * @param {Boolean} cancel true to cancel the query
14938         * @param {Object} e The query event object
14939         */
14940         'beforequery': true,
14941          /**
14942          * @event add
14943          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14944         * @param {Roo.bootstrap.ComboBox} combo This combo box
14945         */
14946         'add' : true,
14947         /**
14948          * @event edit
14949          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14950         * @param {Roo.bootstrap.ComboBox} combo This combo box
14951         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14952         */
14953         'edit' : true,
14954         /**
14955          * @event remove
14956          * Fires when the remove value from the combobox array
14957         * @param {Roo.bootstrap.ComboBox} combo This combo box
14958         */
14959         'remove' : true,
14960         /**
14961          * @event afterremove
14962          * Fires when the remove value from the combobox array
14963         * @param {Roo.bootstrap.ComboBox} combo This combo box
14964         */
14965         'afterremove' : true,
14966         /**
14967          * @event specialfilter
14968          * Fires when specialfilter
14969             * @param {Roo.bootstrap.ComboBox} combo This combo box
14970             */
14971         'specialfilter' : true,
14972         /**
14973          * @event tick
14974          * Fires when tick the element
14975             * @param {Roo.bootstrap.ComboBox} combo This combo box
14976             */
14977         'tick' : true,
14978         /**
14979          * @event touchviewdisplay
14980          * Fires when touch view require special display (default is using displayField)
14981             * @param {Roo.bootstrap.ComboBox} combo This combo box
14982             * @param {Object} cfg set html .
14983             */
14984         'touchviewdisplay' : true
14985         
14986     });
14987     
14988     this.item = [];
14989     this.tickItems = [];
14990     
14991     this.selectedIndex = -1;
14992     if(this.mode == 'local'){
14993         if(config.queryDelay === undefined){
14994             this.queryDelay = 10;
14995         }
14996         if(config.minChars === undefined){
14997             this.minChars = 0;
14998         }
14999     }
15000 };
15001
15002 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15003      
15004     /**
15005      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15006      * rendering into an Roo.Editor, defaults to false)
15007      */
15008     /**
15009      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15010      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15011      */
15012     /**
15013      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15014      */
15015     /**
15016      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15017      * the dropdown list (defaults to undefined, with no header element)
15018      */
15019
15020      /**
15021      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15022      */
15023      
15024      /**
15025      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15026      */
15027     listWidth: undefined,
15028     /**
15029      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15030      * mode = 'remote' or 'text' if mode = 'local')
15031      */
15032     displayField: undefined,
15033     
15034     /**
15035      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15036      * mode = 'remote' or 'value' if mode = 'local'). 
15037      * Note: use of a valueField requires the user make a selection
15038      * in order for a value to be mapped.
15039      */
15040     valueField: undefined,
15041     /**
15042      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15043      */
15044     modalTitle : '',
15045     
15046     /**
15047      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15048      * field's data value (defaults to the underlying DOM element's name)
15049      */
15050     hiddenName: undefined,
15051     /**
15052      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15053      */
15054     listClass: '',
15055     /**
15056      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15057      */
15058     selectedClass: 'active',
15059     
15060     /**
15061      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15062      */
15063     shadow:'sides',
15064     /**
15065      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15066      * anchor positions (defaults to 'tl-bl')
15067      */
15068     listAlign: 'tl-bl?',
15069     /**
15070      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15071      */
15072     maxHeight: 300,
15073     /**
15074      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15075      * query specified by the allQuery config option (defaults to 'query')
15076      */
15077     triggerAction: 'query',
15078     /**
15079      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15080      * (defaults to 4, does not apply if editable = false)
15081      */
15082     minChars : 4,
15083     /**
15084      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15085      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15086      */
15087     typeAhead: false,
15088     /**
15089      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15090      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15091      */
15092     queryDelay: 500,
15093     /**
15094      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15095      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15096      */
15097     pageSize: 0,
15098     /**
15099      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15100      * when editable = true (defaults to false)
15101      */
15102     selectOnFocus:false,
15103     /**
15104      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15105      */
15106     queryParam: 'query',
15107     /**
15108      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15109      * when mode = 'remote' (defaults to 'Loading...')
15110      */
15111     loadingText: 'Loading...',
15112     /**
15113      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15114      */
15115     resizable: false,
15116     /**
15117      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15118      */
15119     handleHeight : 8,
15120     /**
15121      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15122      * traditional select (defaults to true)
15123      */
15124     editable: true,
15125     /**
15126      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15127      */
15128     allQuery: '',
15129     /**
15130      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15131      */
15132     mode: 'remote',
15133     /**
15134      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15135      * listWidth has a higher value)
15136      */
15137     minListWidth : 70,
15138     /**
15139      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15140      * allow the user to set arbitrary text into the field (defaults to false)
15141      */
15142     forceSelection:false,
15143     /**
15144      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15145      * if typeAhead = true (defaults to 250)
15146      */
15147     typeAheadDelay : 250,
15148     /**
15149      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15150      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15151      */
15152     valueNotFoundText : undefined,
15153     /**
15154      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15155      */
15156     blockFocus : false,
15157     
15158     /**
15159      * @cfg {Boolean} disableClear Disable showing of clear button.
15160      */
15161     disableClear : false,
15162     /**
15163      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15164      */
15165     alwaysQuery : false,
15166     
15167     /**
15168      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15169      */
15170     multiple : false,
15171     
15172     /**
15173      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15174      */
15175     invalidClass : "has-warning",
15176     
15177     /**
15178      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15179      */
15180     validClass : "has-success",
15181     
15182     /**
15183      * @cfg {Boolean} specialFilter (true|false) special filter default false
15184      */
15185     specialFilter : false,
15186     
15187     /**
15188      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15189      */
15190     mobileTouchView : true,
15191     
15192     /**
15193      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15194      */
15195     useNativeIOS : false,
15196     
15197     /**
15198      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15199      */
15200     mobile_restrict_height : false,
15201     
15202     ios_options : false,
15203     
15204     //private
15205     addicon : false,
15206     editicon: false,
15207     
15208     page: 0,
15209     hasQuery: false,
15210     append: false,
15211     loadNext: false,
15212     autoFocus : true,
15213     tickable : false,
15214     btnPosition : 'right',
15215     triggerList : true,
15216     showToggleBtn : true,
15217     animate : true,
15218     emptyResultText: 'Empty',
15219     triggerText : 'Select',
15220     emptyTitle : '',
15221     width : false,
15222     
15223     // element that contains real text value.. (when hidden is used..)
15224     
15225     getAutoCreate : function()
15226     {   
15227         var cfg = false;
15228         //render
15229         /*
15230          * Render classic select for iso
15231          */
15232         
15233         if(Roo.isIOS && this.useNativeIOS){
15234             cfg = this.getAutoCreateNativeIOS();
15235             return cfg;
15236         }
15237         
15238         /*
15239          * Touch Devices
15240          */
15241         
15242         if(Roo.isTouch && this.mobileTouchView){
15243             cfg = this.getAutoCreateTouchView();
15244             return cfg;;
15245         }
15246         
15247         /*
15248          *  Normal ComboBox
15249          */
15250         if(!this.tickable){
15251             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15252             return cfg;
15253         }
15254         
15255         /*
15256          *  ComboBox with tickable selections
15257          */
15258              
15259         var align = this.labelAlign || this.parentLabelAlign();
15260         
15261         cfg = {
15262             cls : 'form-group roo-combobox-tickable' //input-group
15263         };
15264         
15265         var btn_text_select = '';
15266         var btn_text_done = '';
15267         var btn_text_cancel = '';
15268         
15269         if (this.btn_text_show) {
15270             btn_text_select = 'Select';
15271             btn_text_done = 'Done';
15272             btn_text_cancel = 'Cancel'; 
15273         }
15274         
15275         var buttons = {
15276             tag : 'div',
15277             cls : 'tickable-buttons',
15278             cn : [
15279                 {
15280                     tag : 'button',
15281                     type : 'button',
15282                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15283                     //html : this.triggerText
15284                     html: btn_text_select
15285                 },
15286                 {
15287                     tag : 'button',
15288                     type : 'button',
15289                     name : 'ok',
15290                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15291                     //html : 'Done'
15292                     html: btn_text_done
15293                 },
15294                 {
15295                     tag : 'button',
15296                     type : 'button',
15297                     name : 'cancel',
15298                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15299                     //html : 'Cancel'
15300                     html: btn_text_cancel
15301                 }
15302             ]
15303         };
15304         
15305         if(this.editable){
15306             buttons.cn.unshift({
15307                 tag: 'input',
15308                 cls: 'roo-select2-search-field-input'
15309             });
15310         }
15311         
15312         var _this = this;
15313         
15314         Roo.each(buttons.cn, function(c){
15315             if (_this.size) {
15316                 c.cls += ' btn-' + _this.size;
15317             }
15318
15319             if (_this.disabled) {
15320                 c.disabled = true;
15321             }
15322         });
15323         
15324         var box = {
15325             tag: 'div',
15326             style : 'display: contents',
15327             cn: [
15328                 {
15329                     tag: 'input',
15330                     type : 'hidden',
15331                     cls: 'form-hidden-field'
15332                 },
15333                 {
15334                     tag: 'ul',
15335                     cls: 'roo-select2-choices',
15336                     cn:[
15337                         {
15338                             tag: 'li',
15339                             cls: 'roo-select2-search-field',
15340                             cn: [
15341                                 buttons
15342                             ]
15343                         }
15344                     ]
15345                 }
15346             ]
15347         };
15348         
15349         var combobox = {
15350             cls: 'roo-select2-container input-group roo-select2-container-multi',
15351             cn: [
15352                 
15353                 box
15354 //                {
15355 //                    tag: 'ul',
15356 //                    cls: 'typeahead typeahead-long dropdown-menu',
15357 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15358 //                }
15359             ]
15360         };
15361         
15362         if(this.hasFeedback && !this.allowBlank){
15363             
15364             var feedback = {
15365                 tag: 'span',
15366                 cls: 'glyphicon form-control-feedback'
15367             };
15368
15369             combobox.cn.push(feedback);
15370         }
15371         
15372         
15373         
15374         var indicator = {
15375             tag : 'i',
15376             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15377             tooltip : 'This field is required'
15378         };
15379         if (Roo.bootstrap.version == 4) {
15380             indicator = {
15381                 tag : 'i',
15382                 style : 'display:none'
15383             };
15384         }
15385         if (align ==='left' && this.fieldLabel.length) {
15386             
15387             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15388             
15389             cfg.cn = [
15390                 indicator,
15391                 {
15392                     tag: 'label',
15393                     'for' :  id,
15394                     cls : 'control-label col-form-label',
15395                     html : this.fieldLabel
15396
15397                 },
15398                 {
15399                     cls : "", 
15400                     cn: [
15401                         combobox
15402                     ]
15403                 }
15404
15405             ];
15406             
15407             var labelCfg = cfg.cn[1];
15408             var contentCfg = cfg.cn[2];
15409             
15410
15411             if(this.indicatorpos == 'right'){
15412                 
15413                 cfg.cn = [
15414                     {
15415                         tag: 'label',
15416                         'for' :  id,
15417                         cls : 'control-label col-form-label',
15418                         cn : [
15419                             {
15420                                 tag : 'span',
15421                                 html : this.fieldLabel
15422                             },
15423                             indicator
15424                         ]
15425                     },
15426                     {
15427                         cls : "",
15428                         cn: [
15429                             combobox
15430                         ]
15431                     }
15432
15433                 ];
15434                 
15435                 
15436                 
15437                 labelCfg = cfg.cn[0];
15438                 contentCfg = cfg.cn[1];
15439             
15440             }
15441             
15442             if(this.labelWidth > 12){
15443                 labelCfg.style = "width: " + this.labelWidth + 'px';
15444             }
15445             if(this.width * 1 > 0){
15446                 contentCfg.style = "width: " + this.width + 'px';
15447             }
15448             if(this.labelWidth < 13 && this.labelmd == 0){
15449                 this.labelmd = this.labelWidth;
15450             }
15451             
15452             if(this.labellg > 0){
15453                 labelCfg.cls += ' col-lg-' + this.labellg;
15454                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15455             }
15456             
15457             if(this.labelmd > 0){
15458                 labelCfg.cls += ' col-md-' + this.labelmd;
15459                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15460             }
15461             
15462             if(this.labelsm > 0){
15463                 labelCfg.cls += ' col-sm-' + this.labelsm;
15464                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15465             }
15466             
15467             if(this.labelxs > 0){
15468                 labelCfg.cls += ' col-xs-' + this.labelxs;
15469                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15470             }
15471                 
15472                 
15473         } else if ( this.fieldLabel.length) {
15474 //                Roo.log(" label");
15475                  cfg.cn = [
15476                    indicator,
15477                     {
15478                         tag: 'label',
15479                         //cls : 'input-group-addon',
15480                         html : this.fieldLabel
15481                     },
15482                     combobox
15483                 ];
15484                 
15485                 if(this.indicatorpos == 'right'){
15486                     cfg.cn = [
15487                         {
15488                             tag: 'label',
15489                             //cls : 'input-group-addon',
15490                             html : this.fieldLabel
15491                         },
15492                         indicator,
15493                         combobox
15494                     ];
15495                     
15496                 }
15497
15498         } else {
15499             
15500 //                Roo.log(" no label && no align");
15501                 cfg = combobox
15502                      
15503                 
15504         }
15505          
15506         var settings=this;
15507         ['xs','sm','md','lg'].map(function(size){
15508             if (settings[size]) {
15509                 cfg.cls += ' col-' + size + '-' + settings[size];
15510             }
15511         });
15512         
15513         return cfg;
15514         
15515     },
15516     
15517     _initEventsCalled : false,
15518     
15519     // private
15520     initEvents: function()
15521     {   
15522         if (this._initEventsCalled) { // as we call render... prevent looping...
15523             return;
15524         }
15525         this._initEventsCalled = true;
15526         
15527         if (!this.store) {
15528             throw "can not find store for combo";
15529         }
15530         
15531         this.indicator = this.indicatorEl();
15532         
15533         this.store = Roo.factory(this.store, Roo.data);
15534         this.store.parent = this;
15535         
15536         // if we are building from html. then this element is so complex, that we can not really
15537         // use the rendered HTML.
15538         // so we have to trash and replace the previous code.
15539         if (Roo.XComponent.build_from_html) {
15540             // remove this element....
15541             var e = this.el.dom, k=0;
15542             while (e ) { e = e.previousSibling;  ++k;}
15543
15544             this.el.remove();
15545             
15546             this.el=false;
15547             this.rendered = false;
15548             
15549             this.render(this.parent().getChildContainer(true), k);
15550         }
15551         
15552         if(Roo.isIOS && this.useNativeIOS){
15553             this.initIOSView();
15554             return;
15555         }
15556         
15557         /*
15558          * Touch Devices
15559          */
15560         
15561         if(Roo.isTouch && this.mobileTouchView){
15562             this.initTouchView();
15563             return;
15564         }
15565         
15566         if(this.tickable){
15567             this.initTickableEvents();
15568             return;
15569         }
15570         
15571         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15572         
15573         if(this.hiddenName){
15574             
15575             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15576             
15577             this.hiddenField.dom.value =
15578                 this.hiddenValue !== undefined ? this.hiddenValue :
15579                 this.value !== undefined ? this.value : '';
15580
15581             // prevent input submission
15582             this.el.dom.removeAttribute('name');
15583             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15584              
15585              
15586         }
15587         //if(Roo.isGecko){
15588         //    this.el.dom.setAttribute('autocomplete', 'off');
15589         //}
15590         
15591         var cls = 'x-combo-list';
15592         
15593         //this.list = new Roo.Layer({
15594         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15595         //});
15596         
15597         var _this = this;
15598         
15599         (function(){
15600             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15601             _this.list.setWidth(lw);
15602         }).defer(100);
15603         
15604         this.list.on('mouseover', this.onViewOver, this);
15605         this.list.on('mousemove', this.onViewMove, this);
15606         this.list.on('scroll', this.onViewScroll, this);
15607         
15608         /*
15609         this.list.swallowEvent('mousewheel');
15610         this.assetHeight = 0;
15611
15612         if(this.title){
15613             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15614             this.assetHeight += this.header.getHeight();
15615         }
15616
15617         this.innerList = this.list.createChild({cls:cls+'-inner'});
15618         this.innerList.on('mouseover', this.onViewOver, this);
15619         this.innerList.on('mousemove', this.onViewMove, this);
15620         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15621         
15622         if(this.allowBlank && !this.pageSize && !this.disableClear){
15623             this.footer = this.list.createChild({cls:cls+'-ft'});
15624             this.pageTb = new Roo.Toolbar(this.footer);
15625            
15626         }
15627         if(this.pageSize){
15628             this.footer = this.list.createChild({cls:cls+'-ft'});
15629             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15630                     {pageSize: this.pageSize});
15631             
15632         }
15633         
15634         if (this.pageTb && this.allowBlank && !this.disableClear) {
15635             var _this = this;
15636             this.pageTb.add(new Roo.Toolbar.Fill(), {
15637                 cls: 'x-btn-icon x-btn-clear',
15638                 text: '&#160;',
15639                 handler: function()
15640                 {
15641                     _this.collapse();
15642                     _this.clearValue();
15643                     _this.onSelect(false, -1);
15644                 }
15645             });
15646         }
15647         if (this.footer) {
15648             this.assetHeight += this.footer.getHeight();
15649         }
15650         */
15651             
15652         if(!this.tpl){
15653             this.tpl = Roo.bootstrap.version == 4 ?
15654                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15655                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15656         }
15657
15658         this.view = new Roo.View(this.list, this.tpl, {
15659             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15660         });
15661         //this.view.wrapEl.setDisplayed(false);
15662         this.view.on('click', this.onViewClick, this);
15663         
15664         
15665         this.store.on('beforeload', this.onBeforeLoad, this);
15666         this.store.on('load', this.onLoad, this);
15667         this.store.on('loadexception', this.onLoadException, this);
15668         /*
15669         if(this.resizable){
15670             this.resizer = new Roo.Resizable(this.list,  {
15671                pinned:true, handles:'se'
15672             });
15673             this.resizer.on('resize', function(r, w, h){
15674                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15675                 this.listWidth = w;
15676                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15677                 this.restrictHeight();
15678             }, this);
15679             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15680         }
15681         */
15682         if(!this.editable){
15683             this.editable = true;
15684             this.setEditable(false);
15685         }
15686         
15687         /*
15688         
15689         if (typeof(this.events.add.listeners) != 'undefined') {
15690             
15691             this.addicon = this.wrap.createChild(
15692                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15693        
15694             this.addicon.on('click', function(e) {
15695                 this.fireEvent('add', this);
15696             }, this);
15697         }
15698         if (typeof(this.events.edit.listeners) != 'undefined') {
15699             
15700             this.editicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15702             if (this.addicon) {
15703                 this.editicon.setStyle('margin-left', '40px');
15704             }
15705             this.editicon.on('click', function(e) {
15706                 
15707                 // we fire even  if inothing is selected..
15708                 this.fireEvent('edit', this, this.lastData );
15709                 
15710             }, this);
15711         }
15712         */
15713         
15714         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15715             "up" : function(e){
15716                 this.inKeyMode = true;
15717                 this.selectPrev();
15718             },
15719
15720             "down" : function(e){
15721                 if(!this.isExpanded()){
15722                     this.onTriggerClick();
15723                 }else{
15724                     this.inKeyMode = true;
15725                     this.selectNext();
15726                 }
15727             },
15728
15729             "enter" : function(e){
15730 //                this.onViewClick();
15731                 //return true;
15732                 this.collapse();
15733                 
15734                 if(this.fireEvent("specialkey", this, e)){
15735                     this.onViewClick(false);
15736                 }
15737                 
15738                 return true;
15739             },
15740
15741             "esc" : function(e){
15742                 this.collapse();
15743             },
15744
15745             "tab" : function(e){
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             scope : this,
15756
15757             doRelay : function(foo, bar, hname){
15758                 if(hname == 'down' || this.scope.isExpanded()){
15759                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15760                 }
15761                 return true;
15762             },
15763
15764             forceKeyDown: true
15765         });
15766         
15767         
15768         this.queryDelay = Math.max(this.queryDelay || 10,
15769                 this.mode == 'local' ? 10 : 250);
15770         
15771         
15772         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15773         
15774         if(this.typeAhead){
15775             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15776         }
15777         if(this.editable !== false){
15778             this.inputEl().on("keyup", this.onKeyUp, this);
15779         }
15780         if(this.forceSelection){
15781             this.inputEl().on('blur', this.doForce, this);
15782         }
15783         
15784         if(this.multiple){
15785             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15786             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15787         }
15788     },
15789     
15790     initTickableEvents: function()
15791     {   
15792         this.createList();
15793         
15794         if(this.hiddenName){
15795             
15796             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15797             
15798             this.hiddenField.dom.value =
15799                 this.hiddenValue !== undefined ? this.hiddenValue :
15800                 this.value !== undefined ? this.value : '';
15801
15802             // prevent input submission
15803             this.el.dom.removeAttribute('name');
15804             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15805              
15806              
15807         }
15808         
15809 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15810         
15811         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15812         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15813         if(this.triggerList){
15814             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15815         }
15816          
15817         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15818         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15819         
15820         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15821         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15822         
15823         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15824         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15825         
15826         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15827         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15828         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15829         
15830         this.okBtn.hide();
15831         this.cancelBtn.hide();
15832         
15833         var _this = this;
15834         
15835         (function(){
15836             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15837             _this.list.setWidth(lw);
15838         }).defer(100);
15839         
15840         this.list.on('mouseover', this.onViewOver, this);
15841         this.list.on('mousemove', this.onViewMove, this);
15842         
15843         this.list.on('scroll', this.onViewScroll, this);
15844         
15845         if(!this.tpl){
15846             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15847                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15848         }
15849
15850         this.view = new Roo.View(this.list, this.tpl, {
15851             singleSelect:true,
15852             tickable:true,
15853             parent:this,
15854             store: this.store,
15855             selectedClass: this.selectedClass
15856         });
15857         
15858         //this.view.wrapEl.setDisplayed(false);
15859         this.view.on('click', this.onViewClick, this);
15860         
15861         
15862         
15863         this.store.on('beforeload', this.onBeforeLoad, this);
15864         this.store.on('load', this.onLoad, this);
15865         this.store.on('loadexception', this.onLoadException, this);
15866         
15867         if(this.editable){
15868             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15869                 "up" : function(e){
15870                     this.inKeyMode = true;
15871                     this.selectPrev();
15872                 },
15873
15874                 "down" : function(e){
15875                     this.inKeyMode = true;
15876                     this.selectNext();
15877                 },
15878
15879                 "enter" : function(e){
15880                     if(this.fireEvent("specialkey", this, e)){
15881                         this.onViewClick(false);
15882                     }
15883                     
15884                     return true;
15885                 },
15886
15887                 "esc" : function(e){
15888                     this.onTickableFooterButtonClick(e, false, false);
15889                 },
15890
15891                 "tab" : function(e){
15892                     this.fireEvent("specialkey", this, e);
15893                     
15894                     this.onTickableFooterButtonClick(e, false, false);
15895                     
15896                     return true;
15897                 },
15898
15899                 scope : this,
15900
15901                 doRelay : function(e, fn, key){
15902                     if(this.scope.isExpanded()){
15903                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15904                     }
15905                     return true;
15906                 },
15907
15908                 forceKeyDown: true
15909             });
15910         }
15911         
15912         this.queryDelay = Math.max(this.queryDelay || 10,
15913                 this.mode == 'local' ? 10 : 250);
15914         
15915         
15916         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15917         
15918         if(this.typeAhead){
15919             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15920         }
15921         
15922         if(this.editable !== false){
15923             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15924         }
15925         
15926         this.indicator = this.indicatorEl();
15927         
15928         if(this.indicator){
15929             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15930             this.indicator.hide();
15931         }
15932         
15933     },
15934
15935     onDestroy : function(){
15936         if(this.view){
15937             this.view.setStore(null);
15938             this.view.el.removeAllListeners();
15939             this.view.el.remove();
15940             this.view.purgeListeners();
15941         }
15942         if(this.list){
15943             this.list.dom.innerHTML  = '';
15944         }
15945         
15946         if(this.store){
15947             this.store.un('beforeload', this.onBeforeLoad, this);
15948             this.store.un('load', this.onLoad, this);
15949             this.store.un('loadexception', this.onLoadException, this);
15950         }
15951         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15952     },
15953
15954     // private
15955     fireKey : function(e){
15956         if(e.isNavKeyPress() && !this.list.isVisible()){
15957             this.fireEvent("specialkey", this, e);
15958         }
15959     },
15960
15961     // private
15962     onResize: function(w, h)
15963     {
15964         
15965         
15966 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15967 //        
15968 //        if(typeof w != 'number'){
15969 //            // we do not handle it!?!?
15970 //            return;
15971 //        }
15972 //        var tw = this.trigger.getWidth();
15973 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15974 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15975 //        var x = w - tw;
15976 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15977 //            
15978 //        //this.trigger.setStyle('left', x+'px');
15979 //        
15980 //        if(this.list && this.listWidth === undefined){
15981 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15982 //            this.list.setWidth(lw);
15983 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15984 //        }
15985         
15986     
15987         
15988     },
15989
15990     /**
15991      * Allow or prevent the user from directly editing the field text.  If false is passed,
15992      * the user will only be able to select from the items defined in the dropdown list.  This method
15993      * is the runtime equivalent of setting the 'editable' config option at config time.
15994      * @param {Boolean} value True to allow the user to directly edit the field text
15995      */
15996     setEditable : function(value){
15997         if(value == this.editable){
15998             return;
15999         }
16000         this.editable = value;
16001         if(!value){
16002             this.inputEl().dom.setAttribute('readOnly', true);
16003             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16004             this.inputEl().addClass('x-combo-noedit');
16005         }else{
16006             this.inputEl().dom.setAttribute('readOnly', false);
16007             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16008             this.inputEl().removeClass('x-combo-noedit');
16009         }
16010     },
16011
16012     // private
16013     
16014     onBeforeLoad : function(combo,opts){
16015         if(!this.hasFocus){
16016             return;
16017         }
16018          if (!opts.add) {
16019             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16020          }
16021         this.restrictHeight();
16022         this.selectedIndex = -1;
16023     },
16024
16025     // private
16026     onLoad : function(){
16027         
16028         this.hasQuery = false;
16029         
16030         if(!this.hasFocus){
16031             return;
16032         }
16033         
16034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16035             this.loading.hide();
16036         }
16037         
16038         if(this.store.getCount() > 0){
16039             
16040             this.expand();
16041             this.restrictHeight();
16042             if(this.lastQuery == this.allQuery){
16043                 if(this.editable && !this.tickable){
16044                     this.inputEl().dom.select();
16045                 }
16046                 
16047                 if(
16048                     !this.selectByValue(this.value, true) &&
16049                     this.autoFocus && 
16050                     (
16051                         !this.store.lastOptions ||
16052                         typeof(this.store.lastOptions.add) == 'undefined' || 
16053                         this.store.lastOptions.add != true
16054                     )
16055                 ){
16056                     this.select(0, true);
16057                 }
16058             }else{
16059                 if(this.autoFocus){
16060                     this.selectNext();
16061                 }
16062                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16063                     this.taTask.delay(this.typeAheadDelay);
16064                 }
16065             }
16066         }else{
16067             this.onEmptyResults();
16068         }
16069         
16070         //this.el.focus();
16071     },
16072     // private
16073     onLoadException : function()
16074     {
16075         this.hasQuery = false;
16076         
16077         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16078             this.loading.hide();
16079         }
16080         
16081         if(this.tickable && this.editable){
16082             return;
16083         }
16084         
16085         this.collapse();
16086         // only causes errors at present
16087         //Roo.log(this.store.reader.jsonData);
16088         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16089             // fixme
16090             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16091         //}
16092         
16093         
16094     },
16095     // private
16096     onTypeAhead : function(){
16097         if(this.store.getCount() > 0){
16098             var r = this.store.getAt(0);
16099             var newValue = r.data[this.displayField];
16100             var len = newValue.length;
16101             var selStart = this.getRawValue().length;
16102             
16103             if(selStart != len){
16104                 this.setRawValue(newValue);
16105                 this.selectText(selStart, newValue.length);
16106             }
16107         }
16108     },
16109
16110     // private
16111     onSelect : function(record, index){
16112         
16113         if(this.fireEvent('beforeselect', this, record, index) !== false){
16114         
16115             this.setFromData(index > -1 ? record.data : false);
16116             
16117             this.collapse();
16118             this.fireEvent('select', this, record, index);
16119         }
16120     },
16121
16122     /**
16123      * Returns the currently selected field value or empty string if no value is set.
16124      * @return {String} value The selected value
16125      */
16126     getValue : function()
16127     {
16128         if(Roo.isIOS && this.useNativeIOS){
16129             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16130         }
16131         
16132         if(this.multiple){
16133             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16134         }
16135         
16136         if(this.valueField){
16137             return typeof this.value != 'undefined' ? this.value : '';
16138         }else{
16139             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16140         }
16141     },
16142     
16143     getRawValue : function()
16144     {
16145         if(Roo.isIOS && this.useNativeIOS){
16146             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16147         }
16148         
16149         var v = this.inputEl().getValue();
16150         
16151         return v;
16152     },
16153
16154     /**
16155      * Clears any text/value currently set in the field
16156      */
16157     clearValue : function(){
16158         
16159         if(this.hiddenField){
16160             this.hiddenField.dom.value = '';
16161         }
16162         this.value = '';
16163         this.setRawValue('');
16164         this.lastSelectionText = '';
16165         this.lastData = false;
16166         
16167         var close = this.closeTriggerEl();
16168         
16169         if(close){
16170             close.hide();
16171         }
16172         
16173         this.validate();
16174         
16175     },
16176
16177     /**
16178      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16179      * will be displayed in the field.  If the value does not match the data value of an existing item,
16180      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16181      * Otherwise the field will be blank (although the value will still be set).
16182      * @param {String} value The value to match
16183      */
16184     setValue : function(v)
16185     {
16186         if(Roo.isIOS && this.useNativeIOS){
16187             this.setIOSValue(v);
16188             return;
16189         }
16190         
16191         if(this.multiple){
16192             this.syncValue();
16193             return;
16194         }
16195         
16196         var text = v;
16197         if(this.valueField){
16198             var r = this.findRecord(this.valueField, v);
16199             if(r){
16200                 text = r.data[this.displayField];
16201             }else if(this.valueNotFoundText !== undefined){
16202                 text = this.valueNotFoundText;
16203             }
16204         }
16205         this.lastSelectionText = text;
16206         if(this.hiddenField){
16207             this.hiddenField.dom.value = v;
16208         }
16209         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16210         this.value = v;
16211         
16212         var close = this.closeTriggerEl();
16213         
16214         if(close){
16215             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16216         }
16217         
16218         this.validate();
16219     },
16220     /**
16221      * @property {Object} the last set data for the element
16222      */
16223     
16224     lastData : false,
16225     /**
16226      * Sets the value of the field based on a object which is related to the record format for the store.
16227      * @param {Object} value the value to set as. or false on reset?
16228      */
16229     setFromData : function(o){
16230         
16231         if(this.multiple){
16232             this.addItem(o);
16233             return;
16234         }
16235             
16236         var dv = ''; // display value
16237         var vv = ''; // value value..
16238         this.lastData = o;
16239         if (this.displayField) {
16240             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16241         } else {
16242             // this is an error condition!!!
16243             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16244         }
16245         
16246         if(this.valueField){
16247             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16248         }
16249         
16250         var close = this.closeTriggerEl();
16251         
16252         if(close){
16253             if(dv.length || vv * 1 > 0){
16254                 close.show() ;
16255                 this.blockFocus=true;
16256             } else {
16257                 close.hide();
16258             }             
16259         }
16260         
16261         if(this.hiddenField){
16262             this.hiddenField.dom.value = vv;
16263             
16264             this.lastSelectionText = dv;
16265             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16266             this.value = vv;
16267             return;
16268         }
16269         // no hidden field.. - we store the value in 'value', but still display
16270         // display field!!!!
16271         this.lastSelectionText = dv;
16272         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16273         this.value = vv;
16274         
16275         
16276         
16277     },
16278     // private
16279     reset : function(){
16280         // overridden so that last data is reset..
16281         
16282         if(this.multiple){
16283             this.clearItem();
16284             return;
16285         }
16286         
16287         this.setValue(this.originalValue);
16288         //this.clearInvalid();
16289         this.lastData = false;
16290         if (this.view) {
16291             this.view.clearSelections();
16292         }
16293         
16294         this.validate();
16295     },
16296     // private
16297     findRecord : function(prop, value){
16298         var record;
16299         if(this.store.getCount() > 0){
16300             this.store.each(function(r){
16301                 if(r.data[prop] == value){
16302                     record = r;
16303                     return false;
16304                 }
16305                 return true;
16306             });
16307         }
16308         return record;
16309     },
16310     
16311     getName: function()
16312     {
16313         // returns hidden if it's set..
16314         if (!this.rendered) {return ''};
16315         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16316         
16317     },
16318     // private
16319     onViewMove : function(e, t){
16320         this.inKeyMode = false;
16321     },
16322
16323     // private
16324     onViewOver : function(e, t){
16325         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16326             return;
16327         }
16328         var item = this.view.findItemFromChild(t);
16329         
16330         if(item){
16331             var index = this.view.indexOf(item);
16332             this.select(index, false);
16333         }
16334     },
16335
16336     // private
16337     onViewClick : function(view, doFocus, el, e)
16338     {
16339         var index = this.view.getSelectedIndexes()[0];
16340         
16341         var r = this.store.getAt(index);
16342         
16343         if(this.tickable){
16344             
16345             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16346                 return;
16347             }
16348             
16349             var rm = false;
16350             var _this = this;
16351             
16352             Roo.each(this.tickItems, function(v,k){
16353                 
16354                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16355                     Roo.log(v);
16356                     _this.tickItems.splice(k, 1);
16357                     
16358                     if(typeof(e) == 'undefined' && view == false){
16359                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16360                     }
16361                     
16362                     rm = true;
16363                     return;
16364                 }
16365             });
16366             
16367             if(rm){
16368                 return;
16369             }
16370             
16371             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16372                 this.tickItems.push(r.data);
16373             }
16374             
16375             if(typeof(e) == 'undefined' && view == false){
16376                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16377             }
16378                     
16379             return;
16380         }
16381         
16382         if(r){
16383             this.onSelect(r, index);
16384         }
16385         if(doFocus !== false && !this.blockFocus){
16386             this.inputEl().focus();
16387         }
16388     },
16389
16390     // private
16391     restrictHeight : function(){
16392         //this.innerList.dom.style.height = '';
16393         //var inner = this.innerList.dom;
16394         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16395         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16396         //this.list.beginUpdate();
16397         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16398         this.list.alignTo(this.inputEl(), this.listAlign);
16399         this.list.alignTo(this.inputEl(), this.listAlign);
16400         //this.list.endUpdate();
16401     },
16402
16403     // private
16404     onEmptyResults : function(){
16405         
16406         if(this.tickable && this.editable){
16407             this.hasFocus = false;
16408             this.restrictHeight();
16409             return;
16410         }
16411         
16412         this.collapse();
16413     },
16414
16415     /**
16416      * Returns true if the dropdown list is expanded, else false.
16417      */
16418     isExpanded : function(){
16419         return this.list.isVisible();
16420     },
16421
16422     /**
16423      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16424      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16425      * @param {String} value The data value of the item to select
16426      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16427      * selected item if it is not currently in view (defaults to true)
16428      * @return {Boolean} True if the value matched an item in the list, else false
16429      */
16430     selectByValue : function(v, scrollIntoView){
16431         if(v !== undefined && v !== null){
16432             var r = this.findRecord(this.valueField || this.displayField, v);
16433             if(r){
16434                 this.select(this.store.indexOf(r), scrollIntoView);
16435                 return true;
16436             }
16437         }
16438         return false;
16439     },
16440
16441     /**
16442      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16443      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16444      * @param {Number} index The zero-based index of the list item to select
16445      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16446      * selected item if it is not currently in view (defaults to true)
16447      */
16448     select : function(index, scrollIntoView){
16449         this.selectedIndex = index;
16450         this.view.select(index);
16451         if(scrollIntoView !== false){
16452             var el = this.view.getNode(index);
16453             /*
16454              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16455              */
16456             if(el){
16457                 this.list.scrollChildIntoView(el, false);
16458             }
16459         }
16460     },
16461
16462     // private
16463     selectNext : function(){
16464         var ct = this.store.getCount();
16465         if(ct > 0){
16466             if(this.selectedIndex == -1){
16467                 this.select(0);
16468             }else if(this.selectedIndex < ct-1){
16469                 this.select(this.selectedIndex+1);
16470             }
16471         }
16472     },
16473
16474     // private
16475     selectPrev : function(){
16476         var ct = this.store.getCount();
16477         if(ct > 0){
16478             if(this.selectedIndex == -1){
16479                 this.select(0);
16480             }else if(this.selectedIndex != 0){
16481                 this.select(this.selectedIndex-1);
16482             }
16483         }
16484     },
16485
16486     // private
16487     onKeyUp : function(e){
16488         if(this.editable !== false && !e.isSpecialKey()){
16489             this.lastKey = e.getKey();
16490             this.dqTask.delay(this.queryDelay);
16491         }
16492     },
16493
16494     // private
16495     validateBlur : function(){
16496         return !this.list || !this.list.isVisible();   
16497     },
16498
16499     // private
16500     initQuery : function(){
16501         
16502         var v = this.getRawValue();
16503         
16504         if(this.tickable && this.editable){
16505             v = this.tickableInputEl().getValue();
16506         }
16507         
16508         this.doQuery(v);
16509     },
16510
16511     // private
16512     doForce : function(){
16513         if(this.inputEl().dom.value.length > 0){
16514             this.inputEl().dom.value =
16515                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16516              
16517         }
16518     },
16519
16520     /**
16521      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16522      * query allowing the query action to be canceled if needed.
16523      * @param {String} query The SQL query to execute
16524      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16525      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16526      * saved in the current store (defaults to false)
16527      */
16528     doQuery : function(q, forceAll){
16529         
16530         if(q === undefined || q === null){
16531             q = '';
16532         }
16533         var qe = {
16534             query: q,
16535             forceAll: forceAll,
16536             combo: this,
16537             cancel:false
16538         };
16539         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16540             return false;
16541         }
16542         q = qe.query;
16543         
16544         forceAll = qe.forceAll;
16545         if(forceAll === true || (q.length >= this.minChars)){
16546             
16547             this.hasQuery = true;
16548             
16549             if(this.lastQuery != q || this.alwaysQuery){
16550                 this.lastQuery = q;
16551                 if(this.mode == 'local'){
16552                     this.selectedIndex = -1;
16553                     if(forceAll){
16554                         this.store.clearFilter();
16555                     }else{
16556                         
16557                         if(this.specialFilter){
16558                             this.fireEvent('specialfilter', this);
16559                             this.onLoad();
16560                             return;
16561                         }
16562                         
16563                         this.store.filter(this.displayField, q);
16564                     }
16565                     
16566                     this.store.fireEvent("datachanged", this.store);
16567                     
16568                     this.onLoad();
16569                     
16570                     
16571                 }else{
16572                     
16573                     this.store.baseParams[this.queryParam] = q;
16574                     
16575                     var options = {params : this.getParams(q)};
16576                     
16577                     if(this.loadNext){
16578                         options.add = true;
16579                         options.params.start = this.page * this.pageSize;
16580                     }
16581                     
16582                     this.store.load(options);
16583                     
16584                     /*
16585                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16586                      *  we should expand the list on onLoad
16587                      *  so command out it
16588                      */
16589 //                    this.expand();
16590                 }
16591             }else{
16592                 this.selectedIndex = -1;
16593                 this.onLoad();   
16594             }
16595         }
16596         
16597         this.loadNext = false;
16598     },
16599     
16600     // private
16601     getParams : function(q){
16602         var p = {};
16603         //p[this.queryParam] = q;
16604         
16605         if(this.pageSize){
16606             p.start = 0;
16607             p.limit = this.pageSize;
16608         }
16609         return p;
16610     },
16611
16612     /**
16613      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16614      */
16615     collapse : function(){
16616         if(!this.isExpanded()){
16617             return;
16618         }
16619         
16620         this.list.hide();
16621         
16622         this.hasFocus = false;
16623         
16624         if(this.tickable){
16625             this.okBtn.hide();
16626             this.cancelBtn.hide();
16627             this.trigger.show();
16628             
16629             if(this.editable){
16630                 this.tickableInputEl().dom.value = '';
16631                 this.tickableInputEl().blur();
16632             }
16633             
16634         }
16635         
16636         Roo.get(document).un('mousedown', this.collapseIf, this);
16637         Roo.get(document).un('mousewheel', this.collapseIf, this);
16638         if (!this.editable) {
16639             Roo.get(document).un('keydown', this.listKeyPress, this);
16640         }
16641         this.fireEvent('collapse', this);
16642         
16643         this.validate();
16644     },
16645
16646     // private
16647     collapseIf : function(e){
16648         var in_combo  = e.within(this.el);
16649         var in_list =  e.within(this.list);
16650         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16651         
16652         if (in_combo || in_list || is_list) {
16653             //e.stopPropagation();
16654             return;
16655         }
16656         
16657         if(this.tickable){
16658             this.onTickableFooterButtonClick(e, false, false);
16659         }
16660
16661         this.collapse();
16662         
16663     },
16664
16665     /**
16666      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16667      */
16668     expand : function(){
16669        
16670         if(this.isExpanded() || !this.hasFocus){
16671             return;
16672         }
16673         
16674         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16675         this.list.setWidth(lw);
16676         
16677         Roo.log('expand');
16678         
16679         this.list.show();
16680         
16681         this.restrictHeight();
16682         
16683         if(this.tickable){
16684             
16685             this.tickItems = Roo.apply([], this.item);
16686             
16687             this.okBtn.show();
16688             this.cancelBtn.show();
16689             this.trigger.hide();
16690             
16691             if(this.editable){
16692                 this.tickableInputEl().focus();
16693             }
16694             
16695         }
16696         
16697         Roo.get(document).on('mousedown', this.collapseIf, this);
16698         Roo.get(document).on('mousewheel', this.collapseIf, this);
16699         if (!this.editable) {
16700             Roo.get(document).on('keydown', this.listKeyPress, this);
16701         }
16702         
16703         this.fireEvent('expand', this);
16704     },
16705
16706     // private
16707     // Implements the default empty TriggerField.onTriggerClick function
16708     onTriggerClick : function(e)
16709     {
16710         Roo.log('trigger click');
16711         
16712         if(this.disabled || !this.triggerList){
16713             return;
16714         }
16715         
16716         this.page = 0;
16717         this.loadNext = false;
16718         
16719         if(this.isExpanded()){
16720             this.collapse();
16721             if (!this.blockFocus) {
16722                 this.inputEl().focus();
16723             }
16724             
16725         }else {
16726             this.hasFocus = true;
16727             if(this.triggerAction == 'all') {
16728                 this.doQuery(this.allQuery, true);
16729             } else {
16730                 this.doQuery(this.getRawValue());
16731             }
16732             if (!this.blockFocus) {
16733                 this.inputEl().focus();
16734             }
16735         }
16736     },
16737     
16738     onTickableTriggerClick : function(e)
16739     {
16740         if(this.disabled){
16741             return;
16742         }
16743         
16744         this.page = 0;
16745         this.loadNext = false;
16746         this.hasFocus = true;
16747         
16748         if(this.triggerAction == 'all') {
16749             this.doQuery(this.allQuery, true);
16750         } else {
16751             this.doQuery(this.getRawValue());
16752         }
16753     },
16754     
16755     onSearchFieldClick : function(e)
16756     {
16757         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16758             this.onTickableFooterButtonClick(e, false, false);
16759             return;
16760         }
16761         
16762         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16763             return;
16764         }
16765         
16766         this.page = 0;
16767         this.loadNext = false;
16768         this.hasFocus = true;
16769         
16770         if(this.triggerAction == 'all') {
16771             this.doQuery(this.allQuery, true);
16772         } else {
16773             this.doQuery(this.getRawValue());
16774         }
16775     },
16776     
16777     listKeyPress : function(e)
16778     {
16779         //Roo.log('listkeypress');
16780         // scroll to first matching element based on key pres..
16781         if (e.isSpecialKey()) {
16782             return false;
16783         }
16784         var k = String.fromCharCode(e.getKey()).toUpperCase();
16785         //Roo.log(k);
16786         var match  = false;
16787         var csel = this.view.getSelectedNodes();
16788         var cselitem = false;
16789         if (csel.length) {
16790             var ix = this.view.indexOf(csel[0]);
16791             cselitem  = this.store.getAt(ix);
16792             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16793                 cselitem = false;
16794             }
16795             
16796         }
16797         
16798         this.store.each(function(v) { 
16799             if (cselitem) {
16800                 // start at existing selection.
16801                 if (cselitem.id == v.id) {
16802                     cselitem = false;
16803                 }
16804                 return true;
16805             }
16806                 
16807             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16808                 match = this.store.indexOf(v);
16809                 return false;
16810             }
16811             return true;
16812         }, this);
16813         
16814         if (match === false) {
16815             return true; // no more action?
16816         }
16817         // scroll to?
16818         this.view.select(match);
16819         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16820         sn.scrollIntoView(sn.dom.parentNode, false);
16821     },
16822     
16823     onViewScroll : function(e, t){
16824         
16825         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){
16826             return;
16827         }
16828         
16829         this.hasQuery = true;
16830         
16831         this.loading = this.list.select('.loading', true).first();
16832         
16833         if(this.loading === null){
16834             this.list.createChild({
16835                 tag: 'div',
16836                 cls: 'loading roo-select2-more-results roo-select2-active',
16837                 html: 'Loading more results...'
16838             });
16839             
16840             this.loading = this.list.select('.loading', true).first();
16841             
16842             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16843             
16844             this.loading.hide();
16845         }
16846         
16847         this.loading.show();
16848         
16849         var _combo = this;
16850         
16851         this.page++;
16852         this.loadNext = true;
16853         
16854         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16855         
16856         return;
16857     },
16858     
16859     addItem : function(o)
16860     {   
16861         var dv = ''; // display value
16862         
16863         if (this.displayField) {
16864             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16865         } else {
16866             // this is an error condition!!!
16867             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16868         }
16869         
16870         if(!dv.length){
16871             return;
16872         }
16873         
16874         var choice = this.choices.createChild({
16875             tag: 'li',
16876             cls: 'roo-select2-search-choice',
16877             cn: [
16878                 {
16879                     tag: 'div',
16880                     html: dv
16881                 },
16882                 {
16883                     tag: 'a',
16884                     href: '#',
16885                     cls: 'roo-select2-search-choice-close fa fa-times',
16886                     tabindex: '-1'
16887                 }
16888             ]
16889             
16890         }, this.searchField);
16891         
16892         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16893         
16894         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16895         
16896         this.item.push(o);
16897         
16898         this.lastData = o;
16899         
16900         this.syncValue();
16901         
16902         this.inputEl().dom.value = '';
16903         
16904         this.validate();
16905     },
16906     
16907     onRemoveItem : function(e, _self, o)
16908     {
16909         e.preventDefault();
16910         
16911         this.lastItem = Roo.apply([], this.item);
16912         
16913         var index = this.item.indexOf(o.data) * 1;
16914         
16915         if( index < 0){
16916             Roo.log('not this item?!');
16917             return;
16918         }
16919         
16920         this.item.splice(index, 1);
16921         o.item.remove();
16922         
16923         this.syncValue();
16924         
16925         this.fireEvent('remove', this, e);
16926         
16927         this.validate();
16928         
16929     },
16930     
16931     syncValue : function()
16932     {
16933         if(!this.item.length){
16934             this.clearValue();
16935             return;
16936         }
16937             
16938         var value = [];
16939         var _this = this;
16940         Roo.each(this.item, function(i){
16941             if(_this.valueField){
16942                 value.push(i[_this.valueField]);
16943                 return;
16944             }
16945
16946             value.push(i);
16947         });
16948
16949         this.value = value.join(',');
16950
16951         if(this.hiddenField){
16952             this.hiddenField.dom.value = this.value;
16953         }
16954         
16955         this.store.fireEvent("datachanged", this.store);
16956         
16957         this.validate();
16958     },
16959     
16960     clearItem : function()
16961     {
16962         if(!this.multiple){
16963             return;
16964         }
16965         
16966         this.item = [];
16967         
16968         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16969            c.remove();
16970         });
16971         
16972         this.syncValue();
16973         
16974         this.validate();
16975         
16976         if(this.tickable && !Roo.isTouch){
16977             this.view.refresh();
16978         }
16979     },
16980     
16981     inputEl: function ()
16982     {
16983         if(Roo.isIOS && this.useNativeIOS){
16984             return this.el.select('select.roo-ios-select', true).first();
16985         }
16986         
16987         if(Roo.isTouch && this.mobileTouchView){
16988             return this.el.select('input.form-control',true).first();
16989         }
16990         
16991         if(this.tickable){
16992             return this.searchField;
16993         }
16994         
16995         return this.el.select('input.form-control',true).first();
16996     },
16997     
16998     onTickableFooterButtonClick : function(e, btn, el)
16999     {
17000         e.preventDefault();
17001         
17002         this.lastItem = Roo.apply([], this.item);
17003         
17004         if(btn && btn.name == 'cancel'){
17005             this.tickItems = Roo.apply([], this.item);
17006             this.collapse();
17007             return;
17008         }
17009         
17010         this.clearItem();
17011         
17012         var _this = this;
17013         
17014         Roo.each(this.tickItems, function(o){
17015             _this.addItem(o);
17016         });
17017         
17018         this.collapse();
17019         
17020     },
17021     
17022     validate : function()
17023     {
17024         if(this.getVisibilityEl().hasClass('hidden')){
17025             return true;
17026         }
17027         
17028         var v = this.getRawValue();
17029         
17030         if(this.multiple){
17031             v = this.getValue();
17032         }
17033         
17034         if(this.disabled || this.allowBlank || v.length){
17035             this.markValid();
17036             return true;
17037         }
17038         
17039         this.markInvalid();
17040         return false;
17041     },
17042     
17043     tickableInputEl : function()
17044     {
17045         if(!this.tickable || !this.editable){
17046             return this.inputEl();
17047         }
17048         
17049         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17050     },
17051     
17052     
17053     getAutoCreateTouchView : function()
17054     {
17055         var id = Roo.id();
17056         
17057         var cfg = {
17058             cls: 'form-group' //input-group
17059         };
17060         
17061         var input =  {
17062             tag: 'input',
17063             id : id,
17064             type : this.inputType,
17065             cls : 'form-control x-combo-noedit',
17066             autocomplete: 'new-password',
17067             placeholder : this.placeholder || '',
17068             readonly : true
17069         };
17070         
17071         if (this.name) {
17072             input.name = this.name;
17073         }
17074         
17075         if (this.size) {
17076             input.cls += ' input-' + this.size;
17077         }
17078         
17079         if (this.disabled) {
17080             input.disabled = true;
17081         }
17082         
17083         var inputblock = {
17084             cls : 'roo-combobox-wrap',
17085             cn : [
17086                 input
17087             ]
17088         };
17089         
17090         if(this.before){
17091             inputblock.cls += ' input-group';
17092             
17093             inputblock.cn.unshift({
17094                 tag :'span',
17095                 cls : 'input-group-addon input-group-prepend input-group-text',
17096                 html : this.before
17097             });
17098         }
17099         
17100         if(this.removable && !this.multiple){
17101             inputblock.cls += ' roo-removable';
17102             
17103             inputblock.cn.push({
17104                 tag: 'button',
17105                 html : 'x',
17106                 cls : 'roo-combo-removable-btn close'
17107             });
17108         }
17109
17110         if(this.hasFeedback && !this.allowBlank){
17111             
17112             inputblock.cls += ' has-feedback';
17113             
17114             inputblock.cn.push({
17115                 tag: 'span',
17116                 cls: 'glyphicon form-control-feedback'
17117             });
17118             
17119         }
17120         
17121         if (this.after) {
17122             
17123             inputblock.cls += (this.before) ? '' : ' input-group';
17124             
17125             inputblock.cn.push({
17126                 tag :'span',
17127                 cls : 'input-group-addon input-group-append input-group-text',
17128                 html : this.after
17129             });
17130         }
17131
17132         
17133         var ibwrap = inputblock;
17134         
17135         if(this.multiple){
17136             ibwrap = {
17137                 tag: 'ul',
17138                 cls: 'roo-select2-choices',
17139                 cn:[
17140                     {
17141                         tag: 'li',
17142                         cls: 'roo-select2-search-field',
17143                         cn: [
17144
17145                             inputblock
17146                         ]
17147                     }
17148                 ]
17149             };
17150         
17151             
17152         }
17153         
17154         var combobox = {
17155             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17156             cn: [
17157                 {
17158                     tag: 'input',
17159                     type : 'hidden',
17160                     cls: 'form-hidden-field'
17161                 },
17162                 ibwrap
17163             ]
17164         };
17165         
17166         if(!this.multiple && this.showToggleBtn){
17167             
17168             var caret = {
17169                 cls: 'caret'
17170             };
17171             
17172             if (this.caret != false) {
17173                 caret = {
17174                      tag: 'i',
17175                      cls: 'fa fa-' + this.caret
17176                 };
17177                 
17178             }
17179             
17180             combobox.cn.push({
17181                 tag :'span',
17182                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17183                 cn : [
17184                     Roo.bootstrap.version == 3 ? caret : '',
17185                     {
17186                         tag: 'span',
17187                         cls: 'combobox-clear',
17188                         cn  : [
17189                             {
17190                                 tag : 'i',
17191                                 cls: 'icon-remove'
17192                             }
17193                         ]
17194                     }
17195                 ]
17196
17197             })
17198         }
17199         
17200         if(this.multiple){
17201             combobox.cls += ' roo-select2-container-multi';
17202         }
17203         
17204         var align = this.labelAlign || this.parentLabelAlign();
17205         
17206         if (align ==='left' && this.fieldLabel.length) {
17207
17208             cfg.cn = [
17209                 {
17210                    tag : 'i',
17211                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17212                    tooltip : 'This field is required'
17213                 },
17214                 {
17215                     tag: 'label',
17216                     cls : 'control-label col-form-label',
17217                     html : this.fieldLabel
17218
17219                 },
17220                 {
17221                     cls : 'roo-combobox-wrap ', 
17222                     cn: [
17223                         combobox
17224                     ]
17225                 }
17226             ];
17227             
17228             var labelCfg = cfg.cn[1];
17229             var contentCfg = cfg.cn[2];
17230             
17231
17232             if(this.indicatorpos == 'right'){
17233                 cfg.cn = [
17234                     {
17235                         tag: 'label',
17236                         'for' :  id,
17237                         cls : 'control-label col-form-label',
17238                         cn : [
17239                             {
17240                                 tag : 'span',
17241                                 html : this.fieldLabel
17242                             },
17243                             {
17244                                 tag : 'i',
17245                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17246                                 tooltip : 'This field is required'
17247                             }
17248                         ]
17249                     },
17250                     {
17251                         cls : "roo-combobox-wrap ",
17252                         cn: [
17253                             combobox
17254                         ]
17255                     }
17256
17257                 ];
17258                 
17259                 labelCfg = cfg.cn[0];
17260                 contentCfg = cfg.cn[1];
17261             }
17262             
17263            
17264             
17265             if(this.labelWidth > 12){
17266                 labelCfg.style = "width: " + this.labelWidth + 'px';
17267             }
17268            
17269             if(this.labelWidth < 13 && this.labelmd == 0){
17270                 this.labelmd = this.labelWidth;
17271             }
17272             
17273             if(this.labellg > 0){
17274                 labelCfg.cls += ' col-lg-' + this.labellg;
17275                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17276             }
17277             
17278             if(this.labelmd > 0){
17279                 labelCfg.cls += ' col-md-' + this.labelmd;
17280                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17281             }
17282             
17283             if(this.labelsm > 0){
17284                 labelCfg.cls += ' col-sm-' + this.labelsm;
17285                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17286             }
17287             
17288             if(this.labelxs > 0){
17289                 labelCfg.cls += ' col-xs-' + this.labelxs;
17290                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17291             }
17292                 
17293                 
17294         } else if ( this.fieldLabel.length) {
17295             cfg.cn = [
17296                 {
17297                    tag : 'i',
17298                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17299                    tooltip : 'This field is required'
17300                 },
17301                 {
17302                     tag: 'label',
17303                     cls : 'control-label',
17304                     html : this.fieldLabel
17305
17306                 },
17307                 {
17308                     cls : '', 
17309                     cn: [
17310                         combobox
17311                     ]
17312                 }
17313             ];
17314             
17315             if(this.indicatorpos == 'right'){
17316                 cfg.cn = [
17317                     {
17318                         tag: 'label',
17319                         cls : 'control-label',
17320                         html : this.fieldLabel,
17321                         cn : [
17322                             {
17323                                tag : 'i',
17324                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17325                                tooltip : 'This field is required'
17326                             }
17327                         ]
17328                     },
17329                     {
17330                         cls : '', 
17331                         cn: [
17332                             combobox
17333                         ]
17334                     }
17335                 ];
17336             }
17337         } else {
17338             cfg.cn = combobox;    
17339         }
17340         
17341         
17342         var settings = this;
17343         
17344         ['xs','sm','md','lg'].map(function(size){
17345             if (settings[size]) {
17346                 cfg.cls += ' col-' + size + '-' + settings[size];
17347             }
17348         });
17349         
17350         return cfg;
17351     },
17352     
17353     initTouchView : function()
17354     {
17355         this.renderTouchView();
17356         
17357         this.touchViewEl.on('scroll', function(){
17358             this.el.dom.scrollTop = 0;
17359         }, this);
17360         
17361         this.originalValue = this.getValue();
17362         
17363         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17364         
17365         this.inputEl().on("click", this.showTouchView, this);
17366         if (this.triggerEl) {
17367             this.triggerEl.on("click", this.showTouchView, this);
17368         }
17369         
17370         
17371         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17372         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17373         
17374         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17375         
17376         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17377         this.store.on('load', this.onTouchViewLoad, this);
17378         this.store.on('loadexception', this.onTouchViewLoadException, this);
17379         
17380         if(this.hiddenName){
17381             
17382             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17383             
17384             this.hiddenField.dom.value =
17385                 this.hiddenValue !== undefined ? this.hiddenValue :
17386                 this.value !== undefined ? this.value : '';
17387         
17388             this.el.dom.removeAttribute('name');
17389             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17390         }
17391         
17392         if(this.multiple){
17393             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17394             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17395         }
17396         
17397         if(this.removable && !this.multiple){
17398             var close = this.closeTriggerEl();
17399             if(close){
17400                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17401                 close.on('click', this.removeBtnClick, this, close);
17402             }
17403         }
17404         /*
17405          * fix the bug in Safari iOS8
17406          */
17407         this.inputEl().on("focus", function(e){
17408             document.activeElement.blur();
17409         }, this);
17410         
17411         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17412         
17413         return;
17414         
17415         
17416     },
17417     
17418     renderTouchView : function()
17419     {
17420         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17421         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17422         
17423         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17424         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17425         
17426         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17427         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17428         this.touchViewBodyEl.setStyle('overflow', 'auto');
17429         
17430         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17431         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17432         
17433         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17434         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17435         
17436     },
17437     
17438     showTouchView : function()
17439     {
17440         if(this.disabled){
17441             return;
17442         }
17443         
17444         this.touchViewHeaderEl.hide();
17445
17446         if(this.modalTitle.length){
17447             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17448             this.touchViewHeaderEl.show();
17449         }
17450
17451         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17452         this.touchViewEl.show();
17453
17454         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17455         
17456         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17457         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17458
17459         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17460
17461         if(this.modalTitle.length){
17462             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17463         }
17464         
17465         this.touchViewBodyEl.setHeight(bodyHeight);
17466
17467         if(this.animate){
17468             var _this = this;
17469             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17470         }else{
17471             this.touchViewEl.addClass(['in','show']);
17472         }
17473         
17474         if(this._touchViewMask){
17475             Roo.get(document.body).addClass("x-body-masked");
17476             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17477             this._touchViewMask.setStyle('z-index', 10000);
17478             this._touchViewMask.addClass('show');
17479         }
17480         
17481         this.doTouchViewQuery();
17482         
17483     },
17484     
17485     hideTouchView : function()
17486     {
17487         this.touchViewEl.removeClass(['in','show']);
17488
17489         if(this.animate){
17490             var _this = this;
17491             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17492         }else{
17493             this.touchViewEl.setStyle('display', 'none');
17494         }
17495         
17496         if(this._touchViewMask){
17497             this._touchViewMask.removeClass('show');
17498             Roo.get(document.body).removeClass("x-body-masked");
17499         }
17500     },
17501     
17502     setTouchViewValue : function()
17503     {
17504         if(this.multiple){
17505             this.clearItem();
17506         
17507             var _this = this;
17508
17509             Roo.each(this.tickItems, function(o){
17510                 this.addItem(o);
17511             }, this);
17512         }
17513         
17514         this.hideTouchView();
17515     },
17516     
17517     doTouchViewQuery : function()
17518     {
17519         var qe = {
17520             query: '',
17521             forceAll: true,
17522             combo: this,
17523             cancel:false
17524         };
17525         
17526         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17527             return false;
17528         }
17529         
17530         if(!this.alwaysQuery || this.mode == 'local'){
17531             this.onTouchViewLoad();
17532             return;
17533         }
17534         
17535         this.store.load();
17536     },
17537     
17538     onTouchViewBeforeLoad : function(combo,opts)
17539     {
17540         return;
17541     },
17542
17543     // private
17544     onTouchViewLoad : function()
17545     {
17546         if(this.store.getCount() < 1){
17547             this.onTouchViewEmptyResults();
17548             return;
17549         }
17550         
17551         this.clearTouchView();
17552         
17553         var rawValue = this.getRawValue();
17554         
17555         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17556         
17557         this.tickItems = [];
17558         
17559         this.store.data.each(function(d, rowIndex){
17560             var row = this.touchViewListGroup.createChild(template);
17561             
17562             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17563                 row.addClass(d.data.cls);
17564             }
17565             
17566             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17567                 var cfg = {
17568                     data : d.data,
17569                     html : d.data[this.displayField]
17570                 };
17571                 
17572                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17573                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17574                 }
17575             }
17576             row.removeClass('selected');
17577             if(!this.multiple && this.valueField &&
17578                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17579             {
17580                 // radio buttons..
17581                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17582                 row.addClass('selected');
17583             }
17584             
17585             if(this.multiple && this.valueField &&
17586                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17587             {
17588                 
17589                 // checkboxes...
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 this.tickItems.push(d.data);
17592             }
17593             
17594             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17595             
17596         }, this);
17597         
17598         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17599         
17600         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17601
17602         if(this.modalTitle.length){
17603             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17604         }
17605
17606         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17607         
17608         if(this.mobile_restrict_height && listHeight < bodyHeight){
17609             this.touchViewBodyEl.setHeight(listHeight);
17610         }
17611         
17612         var _this = this;
17613         
17614         if(firstChecked && listHeight > bodyHeight){
17615             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17616         }
17617         
17618     },
17619     
17620     onTouchViewLoadException : function()
17621     {
17622         this.hideTouchView();
17623     },
17624     
17625     onTouchViewEmptyResults : function()
17626     {
17627         this.clearTouchView();
17628         
17629         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17630         
17631         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17632         
17633     },
17634     
17635     clearTouchView : function()
17636     {
17637         this.touchViewListGroup.dom.innerHTML = '';
17638     },
17639     
17640     onTouchViewClick : function(e, el, o)
17641     {
17642         e.preventDefault();
17643         
17644         var row = o.row;
17645         var rowIndex = o.rowIndex;
17646         
17647         var r = this.store.getAt(rowIndex);
17648         
17649         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17650             
17651             if(!this.multiple){
17652                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17653                     c.dom.removeAttribute('checked');
17654                 }, this);
17655
17656                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17657
17658                 this.setFromData(r.data);
17659
17660                 var close = this.closeTriggerEl();
17661
17662                 if(close){
17663                     close.show();
17664                 }
17665
17666                 this.hideTouchView();
17667
17668                 this.fireEvent('select', this, r, rowIndex);
17669
17670                 return;
17671             }
17672
17673             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17674                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17675                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17676                 return;
17677             }
17678
17679             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17680             this.addItem(r.data);
17681             this.tickItems.push(r.data);
17682         }
17683     },
17684     
17685     getAutoCreateNativeIOS : function()
17686     {
17687         var cfg = {
17688             cls: 'form-group' //input-group,
17689         };
17690         
17691         var combobox =  {
17692             tag: 'select',
17693             cls : 'roo-ios-select'
17694         };
17695         
17696         if (this.name) {
17697             combobox.name = this.name;
17698         }
17699         
17700         if (this.disabled) {
17701             combobox.disabled = true;
17702         }
17703         
17704         var settings = this;
17705         
17706         ['xs','sm','md','lg'].map(function(size){
17707             if (settings[size]) {
17708                 cfg.cls += ' col-' + size + '-' + settings[size];
17709             }
17710         });
17711         
17712         cfg.cn = combobox;
17713         
17714         return cfg;
17715         
17716     },
17717     
17718     initIOSView : function()
17719     {
17720         this.store.on('load', this.onIOSViewLoad, this);
17721         
17722         return;
17723     },
17724     
17725     onIOSViewLoad : function()
17726     {
17727         if(this.store.getCount() < 1){
17728             return;
17729         }
17730         
17731         this.clearIOSView();
17732         
17733         if(this.allowBlank) {
17734             
17735             var default_text = '-- SELECT --';
17736             
17737             if(this.placeholder.length){
17738                 default_text = this.placeholder;
17739             }
17740             
17741             if(this.emptyTitle.length){
17742                 default_text += ' - ' + this.emptyTitle + ' -';
17743             }
17744             
17745             var opt = this.inputEl().createChild({
17746                 tag: 'option',
17747                 value : 0,
17748                 html : default_text
17749             });
17750             
17751             var o = {};
17752             o[this.valueField] = 0;
17753             o[this.displayField] = default_text;
17754             
17755             this.ios_options.push({
17756                 data : o,
17757                 el : opt
17758             });
17759             
17760         }
17761         
17762         this.store.data.each(function(d, rowIndex){
17763             
17764             var html = '';
17765             
17766             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17767                 html = d.data[this.displayField];
17768             }
17769             
17770             var value = '';
17771             
17772             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17773                 value = d.data[this.valueField];
17774             }
17775             
17776             var option = {
17777                 tag: 'option',
17778                 value : value,
17779                 html : html
17780             };
17781             
17782             if(this.value == d.data[this.valueField]){
17783                 option['selected'] = true;
17784             }
17785             
17786             var opt = this.inputEl().createChild(option);
17787             
17788             this.ios_options.push({
17789                 data : d.data,
17790                 el : opt
17791             });
17792             
17793         }, this);
17794         
17795         this.inputEl().on('change', function(){
17796            this.fireEvent('select', this);
17797         }, this);
17798         
17799     },
17800     
17801     clearIOSView: function()
17802     {
17803         this.inputEl().dom.innerHTML = '';
17804         
17805         this.ios_options = [];
17806     },
17807     
17808     setIOSValue: function(v)
17809     {
17810         this.value = v;
17811         
17812         if(!this.ios_options){
17813             return;
17814         }
17815         
17816         Roo.each(this.ios_options, function(opts){
17817            
17818            opts.el.dom.removeAttribute('selected');
17819            
17820            if(opts.data[this.valueField] != v){
17821                return;
17822            }
17823            
17824            opts.el.dom.setAttribute('selected', true);
17825            
17826         }, this);
17827     }
17828
17829     /** 
17830     * @cfg {Boolean} grow 
17831     * @hide 
17832     */
17833     /** 
17834     * @cfg {Number} growMin 
17835     * @hide 
17836     */
17837     /** 
17838     * @cfg {Number} growMax 
17839     * @hide 
17840     */
17841     /**
17842      * @hide
17843      * @method autoSize
17844      */
17845 });
17846
17847 Roo.apply(Roo.bootstrap.ComboBox,  {
17848     
17849     header : {
17850         tag: 'div',
17851         cls: 'modal-header',
17852         cn: [
17853             {
17854                 tag: 'h4',
17855                 cls: 'modal-title'
17856             }
17857         ]
17858     },
17859     
17860     body : {
17861         tag: 'div',
17862         cls: 'modal-body',
17863         cn: [
17864             {
17865                 tag: 'ul',
17866                 cls: 'list-group'
17867             }
17868         ]
17869     },
17870     
17871     listItemRadio : {
17872         tag: 'li',
17873         cls: 'list-group-item',
17874         cn: [
17875             {
17876                 tag: 'span',
17877                 cls: 'roo-combobox-list-group-item-value'
17878             },
17879             {
17880                 tag: 'div',
17881                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17882                 cn: [
17883                     {
17884                         tag: 'input',
17885                         type: 'radio'
17886                     },
17887                     {
17888                         tag: 'label'
17889                     }
17890                 ]
17891             }
17892         ]
17893     },
17894     
17895     listItemCheckbox : {
17896         tag: 'li',
17897         cls: 'list-group-item',
17898         cn: [
17899             {
17900                 tag: 'span',
17901                 cls: 'roo-combobox-list-group-item-value'
17902             },
17903             {
17904                 tag: 'div',
17905                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17906                 cn: [
17907                     {
17908                         tag: 'input',
17909                         type: 'checkbox'
17910                     },
17911                     {
17912                         tag: 'label'
17913                     }
17914                 ]
17915             }
17916         ]
17917     },
17918     
17919     emptyResult : {
17920         tag: 'div',
17921         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17922     },
17923     
17924     footer : {
17925         tag: 'div',
17926         cls: 'modal-footer',
17927         cn: [
17928             {
17929                 tag: 'div',
17930                 cls: 'row',
17931                 cn: [
17932                     {
17933                         tag: 'div',
17934                         cls: 'col-xs-6 text-left',
17935                         cn: {
17936                             tag: 'button',
17937                             cls: 'btn btn-danger roo-touch-view-cancel',
17938                             html: 'Cancel'
17939                         }
17940                     },
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-right',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-success roo-touch-view-ok',
17947                             html: 'OK'
17948                         }
17949                     }
17950                 ]
17951             }
17952         ]
17953         
17954     }
17955 });
17956
17957 Roo.apply(Roo.bootstrap.ComboBox,  {
17958     
17959     touchViewTemplate : {
17960         tag: 'div',
17961         cls: 'modal fade roo-combobox-touch-view',
17962         cn: [
17963             {
17964                 tag: 'div',
17965                 cls: 'modal-dialog',
17966                 style : 'position:fixed', // we have to fix position....
17967                 cn: [
17968                     {
17969                         tag: 'div',
17970                         cls: 'modal-content',
17971                         cn: [
17972                             Roo.bootstrap.ComboBox.header,
17973                             Roo.bootstrap.ComboBox.body,
17974                             Roo.bootstrap.ComboBox.footer
17975                         ]
17976                     }
17977                 ]
17978             }
17979         ]
17980     }
17981 });/*
17982  * Based on:
17983  * Ext JS Library 1.1.1
17984  * Copyright(c) 2006-2007, Ext JS, LLC.
17985  *
17986  * Originally Released Under LGPL - original licence link has changed is not relivant.
17987  *
17988  * Fork - LGPL
17989  * <script type="text/javascript">
17990  */
17991
17992 /**
17993  * @class Roo.View
17994  * @extends Roo.util.Observable
17995  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17996  * This class also supports single and multi selection modes. <br>
17997  * Create a data model bound view:
17998  <pre><code>
17999  var store = new Roo.data.Store(...);
18000
18001  var view = new Roo.View({
18002     el : "my-element",
18003     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18004  
18005     singleSelect: true,
18006     selectedClass: "ydataview-selected",
18007     store: store
18008  });
18009
18010  // listen for node click?
18011  view.on("click", function(vw, index, node, e){
18012  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18013  });
18014
18015  // load XML data
18016  dataModel.load("foobar.xml");
18017  </code></pre>
18018  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18019  * <br><br>
18020  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18021  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18022  * 
18023  * Note: old style constructor is still suported (container, template, config)
18024  * 
18025  * @constructor
18026  * Create a new View
18027  * @param {Object} config The config object
18028  * 
18029  */
18030 Roo.View = function(config, depreciated_tpl, depreciated_config){
18031     
18032     this.parent = false;
18033     
18034     if (typeof(depreciated_tpl) == 'undefined') {
18035         // new way.. - universal constructor.
18036         Roo.apply(this, config);
18037         this.el  = Roo.get(this.el);
18038     } else {
18039         // old format..
18040         this.el  = Roo.get(config);
18041         this.tpl = depreciated_tpl;
18042         Roo.apply(this, depreciated_config);
18043     }
18044     this.wrapEl  = this.el.wrap().wrap();
18045     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18046     
18047     
18048     if(typeof(this.tpl) == "string"){
18049         this.tpl = new Roo.Template(this.tpl);
18050     } else {
18051         // support xtype ctors..
18052         this.tpl = new Roo.factory(this.tpl, Roo);
18053     }
18054     
18055     
18056     this.tpl.compile();
18057     
18058     /** @private */
18059     this.addEvents({
18060         /**
18061          * @event beforeclick
18062          * Fires before a click is processed. Returns false to cancel the default action.
18063          * @param {Roo.View} this
18064          * @param {Number} index The index of the target node
18065          * @param {HTMLElement} node The target node
18066          * @param {Roo.EventObject} e The raw event object
18067          */
18068             "beforeclick" : true,
18069         /**
18070          * @event click
18071          * Fires when a template node is clicked.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "click" : true,
18078         /**
18079          * @event dblclick
18080          * Fires when a template node is double clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "dblclick" : true,
18087         /**
18088          * @event contextmenu
18089          * Fires when a template node is right clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "contextmenu" : true,
18096         /**
18097          * @event selectionchange
18098          * Fires when the selected nodes change.
18099          * @param {Roo.View} this
18100          * @param {Array} selections Array of the selected nodes
18101          */
18102             "selectionchange" : true,
18103     
18104         /**
18105          * @event beforeselect
18106          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18107          * @param {Roo.View} this
18108          * @param {HTMLElement} node The node to be selected
18109          * @param {Array} selections Array of currently selected nodes
18110          */
18111             "beforeselect" : true,
18112         /**
18113          * @event preparedata
18114          * Fires on every row to render, to allow you to change the data.
18115          * @param {Roo.View} this
18116          * @param {Object} data to be rendered (change this)
18117          */
18118           "preparedata" : true
18119           
18120           
18121         });
18122
18123
18124
18125     this.el.on({
18126         "click": this.onClick,
18127         "dblclick": this.onDblClick,
18128         "contextmenu": this.onContextMenu,
18129         scope:this
18130     });
18131
18132     this.selections = [];
18133     this.nodes = [];
18134     this.cmp = new Roo.CompositeElementLite([]);
18135     if(this.store){
18136         this.store = Roo.factory(this.store, Roo.data);
18137         this.setStore(this.store, true);
18138     }
18139     
18140     if ( this.footer && this.footer.xtype) {
18141            
18142          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18143         
18144         this.footer.dataSource = this.store;
18145         this.footer.container = fctr;
18146         this.footer = Roo.factory(this.footer, Roo);
18147         fctr.insertFirst(this.el);
18148         
18149         // this is a bit insane - as the paging toolbar seems to detach the el..
18150 //        dom.parentNode.parentNode.parentNode
18151          // they get detached?
18152     }
18153     
18154     
18155     Roo.View.superclass.constructor.call(this);
18156     
18157     
18158 };
18159
18160 Roo.extend(Roo.View, Roo.util.Observable, {
18161     
18162      /**
18163      * @cfg {Roo.data.Store} store Data store to load data from.
18164      */
18165     store : false,
18166     
18167     /**
18168      * @cfg {String|Roo.Element} el The container element.
18169      */
18170     el : '',
18171     
18172     /**
18173      * @cfg {String|Roo.Template} tpl The template used by this View 
18174      */
18175     tpl : false,
18176     /**
18177      * @cfg {String} dataName the named area of the template to use as the data area
18178      *                          Works with domtemplates roo-name="name"
18179      */
18180     dataName: false,
18181     /**
18182      * @cfg {String} selectedClass The css class to add to selected nodes
18183      */
18184     selectedClass : "x-view-selected",
18185      /**
18186      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18187      */
18188     emptyText : "",
18189     
18190     /**
18191      * @cfg {String} text to display on mask (default Loading)
18192      */
18193     mask : false,
18194     /**
18195      * @cfg {Boolean} multiSelect Allow multiple selection
18196      */
18197     multiSelect : false,
18198     /**
18199      * @cfg {Boolean} singleSelect Allow single selection
18200      */
18201     singleSelect:  false,
18202     
18203     /**
18204      * @cfg {Boolean} toggleSelect - selecting 
18205      */
18206     toggleSelect : false,
18207     
18208     /**
18209      * @cfg {Boolean} tickable - selecting 
18210      */
18211     tickable : false,
18212     
18213     /**
18214      * Returns the element this view is bound to.
18215      * @return {Roo.Element}
18216      */
18217     getEl : function(){
18218         return this.wrapEl;
18219     },
18220     
18221     
18222
18223     /**
18224      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18225      */
18226     refresh : function(){
18227         //Roo.log('refresh');
18228         var t = this.tpl;
18229         
18230         // if we are using something like 'domtemplate', then
18231         // the what gets used is:
18232         // t.applySubtemplate(NAME, data, wrapping data..)
18233         // the outer template then get' applied with
18234         //     the store 'extra data'
18235         // and the body get's added to the
18236         //      roo-name="data" node?
18237         //      <span class='roo-tpl-{name}'></span> ?????
18238         
18239         
18240         
18241         this.clearSelections();
18242         this.el.update("");
18243         var html = [];
18244         var records = this.store.getRange();
18245         if(records.length < 1) {
18246             
18247             // is this valid??  = should it render a template??
18248             
18249             this.el.update(this.emptyText);
18250             return;
18251         }
18252         var el = this.el;
18253         if (this.dataName) {
18254             this.el.update(t.apply(this.store.meta)); //????
18255             el = this.el.child('.roo-tpl-' + this.dataName);
18256         }
18257         
18258         for(var i = 0, len = records.length; i < len; i++){
18259             var data = this.prepareData(records[i].data, i, records[i]);
18260             this.fireEvent("preparedata", this, data, i, records[i]);
18261             
18262             var d = Roo.apply({}, data);
18263             
18264             if(this.tickable){
18265                 Roo.apply(d, {'roo-id' : Roo.id()});
18266                 
18267                 var _this = this;
18268             
18269                 Roo.each(this.parent.item, function(item){
18270                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18271                         return;
18272                     }
18273                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18274                 });
18275             }
18276             
18277             html[html.length] = Roo.util.Format.trim(
18278                 this.dataName ?
18279                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18280                     t.apply(d)
18281             );
18282         }
18283         
18284         
18285         
18286         el.update(html.join(""));
18287         this.nodes = el.dom.childNodes;
18288         this.updateIndexes(0);
18289     },
18290     
18291
18292     /**
18293      * Function to override to reformat the data that is sent to
18294      * the template for each node.
18295      * DEPRICATED - use the preparedata event handler.
18296      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18297      * a JSON object for an UpdateManager bound view).
18298      */
18299     prepareData : function(data, index, record)
18300     {
18301         this.fireEvent("preparedata", this, data, index, record);
18302         return data;
18303     },
18304
18305     onUpdate : function(ds, record){
18306         // Roo.log('on update');   
18307         this.clearSelections();
18308         var index = this.store.indexOf(record);
18309         var n = this.nodes[index];
18310         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18311         n.parentNode.removeChild(n);
18312         this.updateIndexes(index, index);
18313     },
18314
18315     
18316     
18317 // --------- FIXME     
18318     onAdd : function(ds, records, index)
18319     {
18320         //Roo.log(['on Add', ds, records, index] );        
18321         this.clearSelections();
18322         if(this.nodes.length == 0){
18323             this.refresh();
18324             return;
18325         }
18326         var n = this.nodes[index];
18327         for(var i = 0, len = records.length; i < len; i++){
18328             var d = this.prepareData(records[i].data, i, records[i]);
18329             if(n){
18330                 this.tpl.insertBefore(n, d);
18331             }else{
18332                 
18333                 this.tpl.append(this.el, d);
18334             }
18335         }
18336         this.updateIndexes(index);
18337     },
18338
18339     onRemove : function(ds, record, index){
18340        // Roo.log('onRemove');
18341         this.clearSelections();
18342         var el = this.dataName  ?
18343             this.el.child('.roo-tpl-' + this.dataName) :
18344             this.el; 
18345         
18346         el.dom.removeChild(this.nodes[index]);
18347         this.updateIndexes(index);
18348     },
18349
18350     /**
18351      * Refresh an individual node.
18352      * @param {Number} index
18353      */
18354     refreshNode : function(index){
18355         this.onUpdate(this.store, this.store.getAt(index));
18356     },
18357
18358     updateIndexes : function(startIndex, endIndex){
18359         var ns = this.nodes;
18360         startIndex = startIndex || 0;
18361         endIndex = endIndex || ns.length - 1;
18362         for(var i = startIndex; i <= endIndex; i++){
18363             ns[i].nodeIndex = i;
18364         }
18365     },
18366
18367     /**
18368      * Changes the data store this view uses and refresh the view.
18369      * @param {Store} store
18370      */
18371     setStore : function(store, initial){
18372         if(!initial && this.store){
18373             this.store.un("datachanged", this.refresh);
18374             this.store.un("add", this.onAdd);
18375             this.store.un("remove", this.onRemove);
18376             this.store.un("update", this.onUpdate);
18377             this.store.un("clear", this.refresh);
18378             this.store.un("beforeload", this.onBeforeLoad);
18379             this.store.un("load", this.onLoad);
18380             this.store.un("loadexception", this.onLoad);
18381         }
18382         if(store){
18383           
18384             store.on("datachanged", this.refresh, this);
18385             store.on("add", this.onAdd, this);
18386             store.on("remove", this.onRemove, this);
18387             store.on("update", this.onUpdate, this);
18388             store.on("clear", this.refresh, this);
18389             store.on("beforeload", this.onBeforeLoad, this);
18390             store.on("load", this.onLoad, this);
18391             store.on("loadexception", this.onLoad, this);
18392         }
18393         
18394         if(store){
18395             this.refresh();
18396         }
18397     },
18398     /**
18399      * onbeforeLoad - masks the loading area.
18400      *
18401      */
18402     onBeforeLoad : function(store,opts)
18403     {
18404          //Roo.log('onBeforeLoad');   
18405         if (!opts.add) {
18406             this.el.update("");
18407         }
18408         this.el.mask(this.mask ? this.mask : "Loading" ); 
18409     },
18410     onLoad : function ()
18411     {
18412         this.el.unmask();
18413     },
18414     
18415
18416     /**
18417      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18418      * @param {HTMLElement} node
18419      * @return {HTMLElement} The template node
18420      */
18421     findItemFromChild : function(node){
18422         var el = this.dataName  ?
18423             this.el.child('.roo-tpl-' + this.dataName,true) :
18424             this.el.dom; 
18425         
18426         if(!node || node.parentNode == el){
18427                     return node;
18428             }
18429             var p = node.parentNode;
18430             while(p && p != el){
18431             if(p.parentNode == el){
18432                 return p;
18433             }
18434             p = p.parentNode;
18435         }
18436             return null;
18437     },
18438
18439     /** @ignore */
18440     onClick : function(e){
18441         var item = this.findItemFromChild(e.getTarget());
18442         if(item){
18443             var index = this.indexOf(item);
18444             if(this.onItemClick(item, index, e) !== false){
18445                 this.fireEvent("click", this, index, item, e);
18446             }
18447         }else{
18448             this.clearSelections();
18449         }
18450     },
18451
18452     /** @ignore */
18453     onContextMenu : function(e){
18454         var item = this.findItemFromChild(e.getTarget());
18455         if(item){
18456             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18457         }
18458     },
18459
18460     /** @ignore */
18461     onDblClick : function(e){
18462         var item = this.findItemFromChild(e.getTarget());
18463         if(item){
18464             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18465         }
18466     },
18467
18468     onItemClick : function(item, index, e)
18469     {
18470         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18471             return false;
18472         }
18473         if (this.toggleSelect) {
18474             var m = this.isSelected(item) ? 'unselect' : 'select';
18475             //Roo.log(m);
18476             var _t = this;
18477             _t[m](item, true, false);
18478             return true;
18479         }
18480         if(this.multiSelect || this.singleSelect){
18481             if(this.multiSelect && e.shiftKey && this.lastSelection){
18482                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18483             }else{
18484                 this.select(item, this.multiSelect && e.ctrlKey);
18485                 this.lastSelection = item;
18486             }
18487             
18488             if(!this.tickable){
18489                 e.preventDefault();
18490             }
18491             
18492         }
18493         return true;
18494     },
18495
18496     /**
18497      * Get the number of selected nodes.
18498      * @return {Number}
18499      */
18500     getSelectionCount : function(){
18501         return this.selections.length;
18502     },
18503
18504     /**
18505      * Get the currently selected nodes.
18506      * @return {Array} An array of HTMLElements
18507      */
18508     getSelectedNodes : function(){
18509         return this.selections;
18510     },
18511
18512     /**
18513      * Get the indexes of the selected nodes.
18514      * @return {Array}
18515      */
18516     getSelectedIndexes : function(){
18517         var indexes = [], s = this.selections;
18518         for(var i = 0, len = s.length; i < len; i++){
18519             indexes.push(s[i].nodeIndex);
18520         }
18521         return indexes;
18522     },
18523
18524     /**
18525      * Clear all selections
18526      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18527      */
18528     clearSelections : function(suppressEvent){
18529         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18530             this.cmp.elements = this.selections;
18531             this.cmp.removeClass(this.selectedClass);
18532             this.selections = [];
18533             if(!suppressEvent){
18534                 this.fireEvent("selectionchange", this, this.selections);
18535             }
18536         }
18537     },
18538
18539     /**
18540      * Returns true if the passed node is selected
18541      * @param {HTMLElement/Number} node The node or node index
18542      * @return {Boolean}
18543      */
18544     isSelected : function(node){
18545         var s = this.selections;
18546         if(s.length < 1){
18547             return false;
18548         }
18549         node = this.getNode(node);
18550         return s.indexOf(node) !== -1;
18551     },
18552
18553     /**
18554      * Selects nodes.
18555      * @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
18556      * @param {Boolean} keepExisting (optional) true to keep existing selections
18557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18558      */
18559     select : function(nodeInfo, keepExisting, suppressEvent){
18560         if(nodeInfo instanceof Array){
18561             if(!keepExisting){
18562                 this.clearSelections(true);
18563             }
18564             for(var i = 0, len = nodeInfo.length; i < len; i++){
18565                 this.select(nodeInfo[i], true, true);
18566             }
18567             return;
18568         } 
18569         var node = this.getNode(nodeInfo);
18570         if(!node || this.isSelected(node)){
18571             return; // already selected.
18572         }
18573         if(!keepExisting){
18574             this.clearSelections(true);
18575         }
18576         
18577         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18578             Roo.fly(node).addClass(this.selectedClass);
18579             this.selections.push(node);
18580             if(!suppressEvent){
18581                 this.fireEvent("selectionchange", this, this.selections);
18582             }
18583         }
18584         
18585         
18586     },
18587       /**
18588      * Unselects nodes.
18589      * @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
18590      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18592      */
18593     unselect : function(nodeInfo, keepExisting, suppressEvent)
18594     {
18595         if(nodeInfo instanceof Array){
18596             Roo.each(this.selections, function(s) {
18597                 this.unselect(s, nodeInfo);
18598             }, this);
18599             return;
18600         }
18601         var node = this.getNode(nodeInfo);
18602         if(!node || !this.isSelected(node)){
18603             //Roo.log("not selected");
18604             return; // not selected.
18605         }
18606         // fireevent???
18607         var ns = [];
18608         Roo.each(this.selections, function(s) {
18609             if (s == node ) {
18610                 Roo.fly(node).removeClass(this.selectedClass);
18611
18612                 return;
18613             }
18614             ns.push(s);
18615         },this);
18616         
18617         this.selections= ns;
18618         this.fireEvent("selectionchange", this, this.selections);
18619     },
18620
18621     /**
18622      * Gets a template node.
18623      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18624      * @return {HTMLElement} The node or null if it wasn't found
18625      */
18626     getNode : function(nodeInfo){
18627         if(typeof nodeInfo == "string"){
18628             return document.getElementById(nodeInfo);
18629         }else if(typeof nodeInfo == "number"){
18630             return this.nodes[nodeInfo];
18631         }
18632         return nodeInfo;
18633     },
18634
18635     /**
18636      * Gets a range template nodes.
18637      * @param {Number} startIndex
18638      * @param {Number} endIndex
18639      * @return {Array} An array of nodes
18640      */
18641     getNodes : function(start, end){
18642         var ns = this.nodes;
18643         start = start || 0;
18644         end = typeof end == "undefined" ? ns.length - 1 : end;
18645         var nodes = [];
18646         if(start <= end){
18647             for(var i = start; i <= end; i++){
18648                 nodes.push(ns[i]);
18649             }
18650         } else{
18651             for(var i = start; i >= end; i--){
18652                 nodes.push(ns[i]);
18653             }
18654         }
18655         return nodes;
18656     },
18657
18658     /**
18659      * Finds the index of the passed node
18660      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18661      * @return {Number} The index of the node or -1
18662      */
18663     indexOf : function(node){
18664         node = this.getNode(node);
18665         if(typeof node.nodeIndex == "number"){
18666             return node.nodeIndex;
18667         }
18668         var ns = this.nodes;
18669         for(var i = 0, len = ns.length; i < len; i++){
18670             if(ns[i] == node){
18671                 return i;
18672             }
18673         }
18674         return -1;
18675     }
18676 });
18677 /*
18678  * - LGPL
18679  *
18680  * based on jquery fullcalendar
18681  * 
18682  */
18683
18684 Roo.bootstrap = Roo.bootstrap || {};
18685 /**
18686  * @class Roo.bootstrap.Calendar
18687  * @extends Roo.bootstrap.Component
18688  * Bootstrap Calendar class
18689  * @cfg {Boolean} loadMask (true|false) default false
18690  * @cfg {Object} header generate the user specific header of the calendar, default false
18691
18692  * @constructor
18693  * Create a new Container
18694  * @param {Object} config The config object
18695  */
18696
18697
18698
18699 Roo.bootstrap.Calendar = function(config){
18700     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18701      this.addEvents({
18702         /**
18703              * @event select
18704              * Fires when a date is selected
18705              * @param {DatePicker} this
18706              * @param {Date} date The selected date
18707              */
18708         'select': true,
18709         /**
18710              * @event monthchange
18711              * Fires when the displayed month changes 
18712              * @param {DatePicker} this
18713              * @param {Date} date The selected month
18714              */
18715         'monthchange': true,
18716         /**
18717              * @event evententer
18718              * Fires when mouse over an event
18719              * @param {Calendar} this
18720              * @param {event} Event
18721              */
18722         'evententer': true,
18723         /**
18724              * @event eventleave
18725              * Fires when the mouse leaves an
18726              * @param {Calendar} this
18727              * @param {event}
18728              */
18729         'eventleave': true,
18730         /**
18731              * @event eventclick
18732              * Fires when the mouse click an
18733              * @param {Calendar} this
18734              * @param {event}
18735              */
18736         'eventclick': true
18737         
18738     });
18739
18740 };
18741
18742 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18743     
18744      /**
18745      * @cfg {Number} startDay
18746      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18747      */
18748     startDay : 0,
18749     
18750     loadMask : false,
18751     
18752     header : false,
18753       
18754     getAutoCreate : function(){
18755         
18756         
18757         var fc_button = function(name, corner, style, content ) {
18758             return Roo.apply({},{
18759                 tag : 'span',
18760                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18761                          (corner.length ?
18762                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18763                             ''
18764                         ),
18765                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18766                 unselectable: 'on'
18767             });
18768         };
18769         
18770         var header = {};
18771         
18772         if(!this.header){
18773             header = {
18774                 tag : 'table',
18775                 cls : 'fc-header',
18776                 style : 'width:100%',
18777                 cn : [
18778                     {
18779                         tag: 'tr',
18780                         cn : [
18781                             {
18782                                 tag : 'td',
18783                                 cls : 'fc-header-left',
18784                                 cn : [
18785                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18786                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18787                                     { tag: 'span', cls: 'fc-header-space' },
18788                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18789
18790
18791                                 ]
18792                             },
18793
18794                             {
18795                                 tag : 'td',
18796                                 cls : 'fc-header-center',
18797                                 cn : [
18798                                     {
18799                                         tag: 'span',
18800                                         cls: 'fc-header-title',
18801                                         cn : {
18802                                             tag: 'H2',
18803                                             html : 'month / year'
18804                                         }
18805                                     }
18806
18807                                 ]
18808                             },
18809                             {
18810                                 tag : 'td',
18811                                 cls : 'fc-header-right',
18812                                 cn : [
18813                               /*      fc_button('month', 'left', '', 'month' ),
18814                                     fc_button('week', '', '', 'week' ),
18815                                     fc_button('day', 'right', '', 'day' )
18816                                 */    
18817
18818                                 ]
18819                             }
18820
18821                         ]
18822                     }
18823                 ]
18824             };
18825         }
18826         
18827         header = this.header;
18828         
18829        
18830         var cal_heads = function() {
18831             var ret = [];
18832             // fixme - handle this.
18833             
18834             for (var i =0; i < Date.dayNames.length; i++) {
18835                 var d = Date.dayNames[i];
18836                 ret.push({
18837                     tag: 'th',
18838                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18839                     html : d.substring(0,3)
18840                 });
18841                 
18842             }
18843             ret[0].cls += ' fc-first';
18844             ret[6].cls += ' fc-last';
18845             return ret;
18846         };
18847         var cal_cell = function(n) {
18848             return  {
18849                 tag: 'td',
18850                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18851                 cn : [
18852                     {
18853                         cn : [
18854                             {
18855                                 cls: 'fc-day-number',
18856                                 html: 'D'
18857                             },
18858                             {
18859                                 cls: 'fc-day-content',
18860                              
18861                                 cn : [
18862                                      {
18863                                         style: 'position: relative;' // height: 17px;
18864                                     }
18865                                 ]
18866                             }
18867                             
18868                             
18869                         ]
18870                     }
18871                 ]
18872                 
18873             }
18874         };
18875         var cal_rows = function() {
18876             
18877             var ret = [];
18878             for (var r = 0; r < 6; r++) {
18879                 var row= {
18880                     tag : 'tr',
18881                     cls : 'fc-week',
18882                     cn : []
18883                 };
18884                 
18885                 for (var i =0; i < Date.dayNames.length; i++) {
18886                     var d = Date.dayNames[i];
18887                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18888
18889                 }
18890                 row.cn[0].cls+=' fc-first';
18891                 row.cn[0].cn[0].style = 'min-height:90px';
18892                 row.cn[6].cls+=' fc-last';
18893                 ret.push(row);
18894                 
18895             }
18896             ret[0].cls += ' fc-first';
18897             ret[4].cls += ' fc-prev-last';
18898             ret[5].cls += ' fc-last';
18899             return ret;
18900             
18901         };
18902         
18903         var cal_table = {
18904             tag: 'table',
18905             cls: 'fc-border-separate',
18906             style : 'width:100%',
18907             cellspacing  : 0,
18908             cn : [
18909                 { 
18910                     tag: 'thead',
18911                     cn : [
18912                         { 
18913                             tag: 'tr',
18914                             cls : 'fc-first fc-last',
18915                             cn : cal_heads()
18916                         }
18917                     ]
18918                 },
18919                 { 
18920                     tag: 'tbody',
18921                     cn : cal_rows()
18922                 }
18923                   
18924             ]
18925         };
18926          
18927          var cfg = {
18928             cls : 'fc fc-ltr',
18929             cn : [
18930                 header,
18931                 {
18932                     cls : 'fc-content',
18933                     style : "position: relative;",
18934                     cn : [
18935                         {
18936                             cls : 'fc-view fc-view-month fc-grid',
18937                             style : 'position: relative',
18938                             unselectable : 'on',
18939                             cn : [
18940                                 {
18941                                     cls : 'fc-event-container',
18942                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18943                                 },
18944                                 cal_table
18945                             ]
18946                         }
18947                     ]
18948     
18949                 }
18950            ] 
18951             
18952         };
18953         
18954          
18955         
18956         return cfg;
18957     },
18958     
18959     
18960     initEvents : function()
18961     {
18962         if(!this.store){
18963             throw "can not find store for calendar";
18964         }
18965         
18966         var mark = {
18967             tag: "div",
18968             cls:"x-dlg-mask",
18969             style: "text-align:center",
18970             cn: [
18971                 {
18972                     tag: "div",
18973                     style: "background-color:white;width:50%;margin:250 auto",
18974                     cn: [
18975                         {
18976                             tag: "img",
18977                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18978                         },
18979                         {
18980                             tag: "span",
18981                             html: "Loading"
18982                         }
18983                         
18984                     ]
18985                 }
18986             ]
18987         };
18988         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18989         
18990         var size = this.el.select('.fc-content', true).first().getSize();
18991         this.maskEl.setSize(size.width, size.height);
18992         this.maskEl.enableDisplayMode("block");
18993         if(!this.loadMask){
18994             this.maskEl.hide();
18995         }
18996         
18997         this.store = Roo.factory(this.store, Roo.data);
18998         this.store.on('load', this.onLoad, this);
18999         this.store.on('beforeload', this.onBeforeLoad, this);
19000         
19001         this.resize();
19002         
19003         this.cells = this.el.select('.fc-day',true);
19004         //Roo.log(this.cells);
19005         this.textNodes = this.el.query('.fc-day-number');
19006         this.cells.addClassOnOver('fc-state-hover');
19007         
19008         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19009         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19010         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19011         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19012         
19013         this.on('monthchange', this.onMonthChange, this);
19014         
19015         this.update(new Date().clearTime());
19016     },
19017     
19018     resize : function() {
19019         var sz  = this.el.getSize();
19020         
19021         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19022         this.el.select('.fc-day-content div',true).setHeight(34);
19023     },
19024     
19025     
19026     // private
19027     showPrevMonth : function(e){
19028         this.update(this.activeDate.add("mo", -1));
19029     },
19030     showToday : function(e){
19031         this.update(new Date().clearTime());
19032     },
19033     // private
19034     showNextMonth : function(e){
19035         this.update(this.activeDate.add("mo", 1));
19036     },
19037
19038     // private
19039     showPrevYear : function(){
19040         this.update(this.activeDate.add("y", -1));
19041     },
19042
19043     // private
19044     showNextYear : function(){
19045         this.update(this.activeDate.add("y", 1));
19046     },
19047
19048     
19049    // private
19050     update : function(date)
19051     {
19052         var vd = this.activeDate;
19053         this.activeDate = date;
19054 //        if(vd && this.el){
19055 //            var t = date.getTime();
19056 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19057 //                Roo.log('using add remove');
19058 //                
19059 //                this.fireEvent('monthchange', this, date);
19060 //                
19061 //                this.cells.removeClass("fc-state-highlight");
19062 //                this.cells.each(function(c){
19063 //                   if(c.dateValue == t){
19064 //                       c.addClass("fc-state-highlight");
19065 //                       setTimeout(function(){
19066 //                            try{c.dom.firstChild.focus();}catch(e){}
19067 //                       }, 50);
19068 //                       return false;
19069 //                   }
19070 //                   return true;
19071 //                });
19072 //                return;
19073 //            }
19074 //        }
19075         
19076         var days = date.getDaysInMonth();
19077         
19078         var firstOfMonth = date.getFirstDateOfMonth();
19079         var startingPos = firstOfMonth.getDay()-this.startDay;
19080         
19081         if(startingPos < this.startDay){
19082             startingPos += 7;
19083         }
19084         
19085         var pm = date.add(Date.MONTH, -1);
19086         var prevStart = pm.getDaysInMonth()-startingPos;
19087 //        
19088         this.cells = this.el.select('.fc-day',true);
19089         this.textNodes = this.el.query('.fc-day-number');
19090         this.cells.addClassOnOver('fc-state-hover');
19091         
19092         var cells = this.cells.elements;
19093         var textEls = this.textNodes;
19094         
19095         Roo.each(cells, function(cell){
19096             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19097         });
19098         
19099         days += startingPos;
19100
19101         // convert everything to numbers so it's fast
19102         var day = 86400000;
19103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19104         //Roo.log(d);
19105         //Roo.log(pm);
19106         //Roo.log(prevStart);
19107         
19108         var today = new Date().clearTime().getTime();
19109         var sel = date.clearTime().getTime();
19110         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19111         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19112         var ddMatch = this.disabledDatesRE;
19113         var ddText = this.disabledDatesText;
19114         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19115         var ddaysText = this.disabledDaysText;
19116         var format = this.format;
19117         
19118         var setCellClass = function(cal, cell){
19119             cell.row = 0;
19120             cell.events = [];
19121             cell.more = [];
19122             //Roo.log('set Cell Class');
19123             cell.title = "";
19124             var t = d.getTime();
19125             
19126             //Roo.log(d);
19127             
19128             cell.dateValue = t;
19129             if(t == today){
19130                 cell.className += " fc-today";
19131                 cell.className += " fc-state-highlight";
19132                 cell.title = cal.todayText;
19133             }
19134             if(t == sel){
19135                 // disable highlight in other month..
19136                 //cell.className += " fc-state-highlight";
19137                 
19138             }
19139             // disabling
19140             if(t < min) {
19141                 cell.className = " fc-state-disabled";
19142                 cell.title = cal.minText;
19143                 return;
19144             }
19145             if(t > max) {
19146                 cell.className = " fc-state-disabled";
19147                 cell.title = cal.maxText;
19148                 return;
19149             }
19150             if(ddays){
19151                 if(ddays.indexOf(d.getDay()) != -1){
19152                     cell.title = ddaysText;
19153                     cell.className = " fc-state-disabled";
19154                 }
19155             }
19156             if(ddMatch && format){
19157                 var fvalue = d.dateFormat(format);
19158                 if(ddMatch.test(fvalue)){
19159                     cell.title = ddText.replace("%0", fvalue);
19160                     cell.className = " fc-state-disabled";
19161                 }
19162             }
19163             
19164             if (!cell.initialClassName) {
19165                 cell.initialClassName = cell.dom.className;
19166             }
19167             
19168             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19169         };
19170
19171         var i = 0;
19172         
19173         for(; i < startingPos; i++) {
19174             textEls[i].innerHTML = (++prevStart);
19175             d.setDate(d.getDate()+1);
19176             
19177             cells[i].className = "fc-past fc-other-month";
19178             setCellClass(this, cells[i]);
19179         }
19180         
19181         var intDay = 0;
19182         
19183         for(; i < days; i++){
19184             intDay = i - startingPos + 1;
19185             textEls[i].innerHTML = (intDay);
19186             d.setDate(d.getDate()+1);
19187             
19188             cells[i].className = ''; // "x-date-active";
19189             setCellClass(this, cells[i]);
19190         }
19191         var extraDays = 0;
19192         
19193         for(; i < 42; i++) {
19194             textEls[i].innerHTML = (++extraDays);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = "fc-future fc-other-month";
19198             setCellClass(this, cells[i]);
19199         }
19200         
19201         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19202         
19203         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19204         
19205         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19206         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19207         
19208         if(totalRows != 6){
19209             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19210             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19211         }
19212         
19213         this.fireEvent('monthchange', this, date);
19214         
19215         
19216         /*
19217         if(!this.internalRender){
19218             var main = this.el.dom.firstChild;
19219             var w = main.offsetWidth;
19220             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19221             Roo.fly(main).setWidth(w);
19222             this.internalRender = true;
19223             // opera does not respect the auto grow header center column
19224             // then, after it gets a width opera refuses to recalculate
19225             // without a second pass
19226             if(Roo.isOpera && !this.secondPass){
19227                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19228                 this.secondPass = true;
19229                 this.update.defer(10, this, [date]);
19230             }
19231         }
19232         */
19233         
19234     },
19235     
19236     findCell : function(dt) {
19237         dt = dt.clearTime().getTime();
19238         var ret = false;
19239         this.cells.each(function(c){
19240             //Roo.log("check " +c.dateValue + '?=' + dt);
19241             if(c.dateValue == dt){
19242                 ret = c;
19243                 return false;
19244             }
19245             return true;
19246         });
19247         
19248         return ret;
19249     },
19250     
19251     findCells : function(ev) {
19252         var s = ev.start.clone().clearTime().getTime();
19253        // Roo.log(s);
19254         var e= ev.end.clone().clearTime().getTime();
19255        // Roo.log(e);
19256         var ret = [];
19257         this.cells.each(function(c){
19258              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19259             
19260             if(c.dateValue > e){
19261                 return ;
19262             }
19263             if(c.dateValue < s){
19264                 return ;
19265             }
19266             ret.push(c);
19267         });
19268         
19269         return ret;    
19270     },
19271     
19272 //    findBestRow: function(cells)
19273 //    {
19274 //        var ret = 0;
19275 //        
19276 //        for (var i =0 ; i < cells.length;i++) {
19277 //            ret  = Math.max(cells[i].rows || 0,ret);
19278 //        }
19279 //        return ret;
19280 //        
19281 //    },
19282     
19283     
19284     addItem : function(ev)
19285     {
19286         // look for vertical location slot in
19287         var cells = this.findCells(ev);
19288         
19289 //        ev.row = this.findBestRow(cells);
19290         
19291         // work out the location.
19292         
19293         var crow = false;
19294         var rows = [];
19295         for(var i =0; i < cells.length; i++) {
19296             
19297             cells[i].row = cells[0].row;
19298             
19299             if(i == 0){
19300                 cells[i].row = cells[i].row + 1;
19301             }
19302             
19303             if (!crow) {
19304                 crow = {
19305                     start : cells[i],
19306                     end :  cells[i]
19307                 };
19308                 continue;
19309             }
19310             if (crow.start.getY() == cells[i].getY()) {
19311                 // on same row.
19312                 crow.end = cells[i];
19313                 continue;
19314             }
19315             // different row.
19316             rows.push(crow);
19317             crow = {
19318                 start: cells[i],
19319                 end : cells[i]
19320             };
19321             
19322         }
19323         
19324         rows.push(crow);
19325         ev.els = [];
19326         ev.rows = rows;
19327         ev.cells = cells;
19328         
19329         cells[0].events.push(ev);
19330         
19331         this.calevents.push(ev);
19332     },
19333     
19334     clearEvents: function() {
19335         
19336         if(!this.calevents){
19337             return;
19338         }
19339         
19340         Roo.each(this.cells.elements, function(c){
19341             c.row = 0;
19342             c.events = [];
19343             c.more = [];
19344         });
19345         
19346         Roo.each(this.calevents, function(e) {
19347             Roo.each(e.els, function(el) {
19348                 el.un('mouseenter' ,this.onEventEnter, this);
19349                 el.un('mouseleave' ,this.onEventLeave, this);
19350                 el.remove();
19351             },this);
19352         },this);
19353         
19354         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19355             e.remove();
19356         });
19357         
19358     },
19359     
19360     renderEvents: function()
19361     {   
19362         var _this = this;
19363         
19364         this.cells.each(function(c) {
19365             
19366             if(c.row < 5){
19367                 return;
19368             }
19369             
19370             var ev = c.events;
19371             
19372             var r = 4;
19373             if(c.row != c.events.length){
19374                 r = 4 - (4 - (c.row - c.events.length));
19375             }
19376             
19377             c.events = ev.slice(0, r);
19378             c.more = ev.slice(r);
19379             
19380             if(c.more.length && c.more.length == 1){
19381                 c.events.push(c.more.pop());
19382             }
19383             
19384             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19385             
19386         });
19387             
19388         this.cells.each(function(c) {
19389             
19390             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19391             
19392             
19393             for (var e = 0; e < c.events.length; e++){
19394                 var ev = c.events[e];
19395                 var rows = ev.rows;
19396                 
19397                 for(var i = 0; i < rows.length; i++) {
19398                 
19399                     // how many rows should it span..
19400
19401                     var  cfg = {
19402                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19403                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19404
19405                         unselectable : "on",
19406                         cn : [
19407                             {
19408                                 cls: 'fc-event-inner',
19409                                 cn : [
19410     //                                {
19411     //                                  tag:'span',
19412     //                                  cls: 'fc-event-time',
19413     //                                  html : cells.length > 1 ? '' : ev.time
19414     //                                },
19415                                     {
19416                                       tag:'span',
19417                                       cls: 'fc-event-title',
19418                                       html : String.format('{0}', ev.title)
19419                                     }
19420
19421
19422                                 ]
19423                             },
19424                             {
19425                                 cls: 'ui-resizable-handle ui-resizable-e',
19426                                 html : '&nbsp;&nbsp;&nbsp'
19427                             }
19428
19429                         ]
19430                     };
19431
19432                     if (i == 0) {
19433                         cfg.cls += ' fc-event-start';
19434                     }
19435                     if ((i+1) == rows.length) {
19436                         cfg.cls += ' fc-event-end';
19437                     }
19438
19439                     var ctr = _this.el.select('.fc-event-container',true).first();
19440                     var cg = ctr.createChild(cfg);
19441
19442                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19443                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19444
19445                     var r = (c.more.length) ? 1 : 0;
19446                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19447                     cg.setWidth(ebox.right - sbox.x -2);
19448
19449                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19450                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19451                     cg.on('click', _this.onEventClick, _this, ev);
19452
19453                     ev.els.push(cg);
19454                     
19455                 }
19456                 
19457             }
19458             
19459             
19460             if(c.more.length){
19461                 var  cfg = {
19462                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19463                     style : 'position: absolute',
19464                     unselectable : "on",
19465                     cn : [
19466                         {
19467                             cls: 'fc-event-inner',
19468                             cn : [
19469                                 {
19470                                   tag:'span',
19471                                   cls: 'fc-event-title',
19472                                   html : 'More'
19473                                 }
19474
19475
19476                             ]
19477                         },
19478                         {
19479                             cls: 'ui-resizable-handle ui-resizable-e',
19480                             html : '&nbsp;&nbsp;&nbsp'
19481                         }
19482
19483                     ]
19484                 };
19485
19486                 var ctr = _this.el.select('.fc-event-container',true).first();
19487                 var cg = ctr.createChild(cfg);
19488
19489                 var sbox = c.select('.fc-day-content',true).first().getBox();
19490                 var ebox = c.select('.fc-day-content',true).first().getBox();
19491                 //Roo.log(cg);
19492                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19493                 cg.setWidth(ebox.right - sbox.x -2);
19494
19495                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19496                 
19497             }
19498             
19499         });
19500         
19501         
19502         
19503     },
19504     
19505     onEventEnter: function (e, el,event,d) {
19506         this.fireEvent('evententer', this, el, event);
19507     },
19508     
19509     onEventLeave: function (e, el,event,d) {
19510         this.fireEvent('eventleave', this, el, event);
19511     },
19512     
19513     onEventClick: function (e, el,event,d) {
19514         this.fireEvent('eventclick', this, el, event);
19515     },
19516     
19517     onMonthChange: function () {
19518         this.store.load();
19519     },
19520     
19521     onMoreEventClick: function(e, el, more)
19522     {
19523         var _this = this;
19524         
19525         this.calpopover.placement = 'right';
19526         this.calpopover.setTitle('More');
19527         
19528         this.calpopover.setContent('');
19529         
19530         var ctr = this.calpopover.el.select('.popover-content', true).first();
19531         
19532         Roo.each(more, function(m){
19533             var cfg = {
19534                 cls : 'fc-event-hori fc-event-draggable',
19535                 html : m.title
19536             };
19537             var cg = ctr.createChild(cfg);
19538             
19539             cg.on('click', _this.onEventClick, _this, m);
19540         });
19541         
19542         this.calpopover.show(el);
19543         
19544         
19545     },
19546     
19547     onLoad: function () 
19548     {   
19549         this.calevents = [];
19550         var cal = this;
19551         
19552         if(this.store.getCount() > 0){
19553             this.store.data.each(function(d){
19554                cal.addItem({
19555                     id : d.data.id,
19556                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19557                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19558                     time : d.data.start_time,
19559                     title : d.data.title,
19560                     description : d.data.description,
19561                     venue : d.data.venue
19562                 });
19563             });
19564         }
19565         
19566         this.renderEvents();
19567         
19568         if(this.calevents.length && this.loadMask){
19569             this.maskEl.hide();
19570         }
19571     },
19572     
19573     onBeforeLoad: function()
19574     {
19575         this.clearEvents();
19576         if(this.loadMask){
19577             this.maskEl.show();
19578         }
19579     }
19580 });
19581
19582  
19583  /*
19584  * - LGPL
19585  *
19586  * element
19587  * 
19588  */
19589
19590 /**
19591  * @class Roo.bootstrap.Popover
19592  * @extends Roo.bootstrap.Component
19593  * Bootstrap Popover class
19594  * @cfg {String} html contents of the popover   (or false to use children..)
19595  * @cfg {String} title of popover (or false to hide)
19596  * @cfg {String} placement how it is placed
19597  * @cfg {String} trigger click || hover (or false to trigger manually)
19598  * @cfg {String} over what (parent or false to trigger manually.)
19599  * @cfg {Number} delay - delay before showing
19600  
19601  * @constructor
19602  * Create a new Popover
19603  * @param {Object} config The config object
19604  */
19605
19606 Roo.bootstrap.Popover = function(config){
19607     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19608     
19609     this.addEvents({
19610         // raw events
19611          /**
19612          * @event show
19613          * After the popover show
19614          * 
19615          * @param {Roo.bootstrap.Popover} this
19616          */
19617         "show" : true,
19618         /**
19619          * @event hide
19620          * After the popover hide
19621          * 
19622          * @param {Roo.bootstrap.Popover} this
19623          */
19624         "hide" : true
19625     });
19626 };
19627
19628 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19629     
19630     title: 'Fill in a title',
19631     html: false,
19632     
19633     placement : 'right',
19634     trigger : 'hover', // hover
19635     
19636     delay : 0,
19637     
19638     over: 'parent',
19639     
19640     can_build_overlaid : false,
19641     
19642     getChildContainer : function()
19643     {
19644         return this.el.select('.popover-content',true).first();
19645     },
19646     
19647     getAutoCreate : function(){
19648          
19649         var cfg = {
19650            cls : 'popover roo-dynamic',
19651            style: 'display:block',
19652            cn : [
19653                 {
19654                     cls : 'arrow'
19655                 },
19656                 {
19657                     cls : 'popover-inner',
19658                     cn : [
19659                         {
19660                             tag: 'h3',
19661                             cls: 'popover-title popover-header',
19662                             html : this.title
19663                         },
19664                         {
19665                             cls : 'popover-content popover-body',
19666                             html : this.html
19667                         }
19668                     ]
19669                     
19670                 }
19671            ]
19672         };
19673         
19674         return cfg;
19675     },
19676     setTitle: function(str)
19677     {
19678         this.title = str;
19679         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19680     },
19681     setContent: function(str)
19682     {
19683         this.html = str;
19684         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19685     },
19686     // as it get's added to the bottom of the page.
19687     onRender : function(ct, position)
19688     {
19689         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19690         if(!this.el){
19691             var cfg = Roo.apply({},  this.getAutoCreate());
19692             cfg.id = Roo.id();
19693             
19694             if (this.cls) {
19695                 cfg.cls += ' ' + this.cls;
19696             }
19697             if (this.style) {
19698                 cfg.style = this.style;
19699             }
19700             //Roo.log("adding to ");
19701             this.el = Roo.get(document.body).createChild(cfg, position);
19702 //            Roo.log(this.el);
19703         }
19704         this.initEvents();
19705     },
19706     
19707     initEvents : function()
19708     {
19709         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19710         this.el.enableDisplayMode('block');
19711         this.el.hide();
19712         if (this.over === false) {
19713             return; 
19714         }
19715         if (this.triggers === false) {
19716             return;
19717         }
19718         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19719         var triggers = this.trigger ? this.trigger.split(' ') : [];
19720         Roo.each(triggers, function(trigger) {
19721         
19722             if (trigger == 'click') {
19723                 on_el.on('click', this.toggle, this);
19724             } else if (trigger != 'manual') {
19725                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19726                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19727       
19728                 on_el.on(eventIn  ,this.enter, this);
19729                 on_el.on(eventOut, this.leave, this);
19730             }
19731         }, this);
19732         
19733     },
19734     
19735     
19736     // private
19737     timeout : null,
19738     hoverState : null,
19739     
19740     toggle : function () {
19741         this.hoverState == 'in' ? this.leave() : this.enter();
19742     },
19743     
19744     enter : function () {
19745         
19746         clearTimeout(this.timeout);
19747     
19748         this.hoverState = 'in';
19749     
19750         if (!this.delay || !this.delay.show) {
19751             this.show();
19752             return;
19753         }
19754         var _t = this;
19755         this.timeout = setTimeout(function () {
19756             if (_t.hoverState == 'in') {
19757                 _t.show();
19758             }
19759         }, this.delay.show)
19760     },
19761     
19762     leave : function() {
19763         clearTimeout(this.timeout);
19764     
19765         this.hoverState = 'out';
19766     
19767         if (!this.delay || !this.delay.hide) {
19768             this.hide();
19769             return;
19770         }
19771         var _t = this;
19772         this.timeout = setTimeout(function () {
19773             if (_t.hoverState == 'out') {
19774                 _t.hide();
19775             }
19776         }, this.delay.hide)
19777     },
19778     
19779     show : function (on_el)
19780     {
19781         if (!on_el) {
19782             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19783         }
19784         
19785         // set content.
19786         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19787         if (this.html !== false) {
19788             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19789         }
19790         this.el.removeClass([
19791             'fade','top','bottom', 'left', 'right','in',
19792             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19793         ]);
19794         if (!this.title.length) {
19795             this.el.select('.popover-title',true).hide();
19796         }
19797         
19798         var placement = typeof this.placement == 'function' ?
19799             this.placement.call(this, this.el, on_el) :
19800             this.placement;
19801             
19802         var autoToken = /\s?auto?\s?/i;
19803         var autoPlace = autoToken.test(placement);
19804         if (autoPlace) {
19805             placement = placement.replace(autoToken, '') || 'top';
19806         }
19807         
19808         //this.el.detach()
19809         //this.el.setXY([0,0]);
19810         this.el.show();
19811         this.el.dom.style.display='block';
19812         this.el.addClass(placement);
19813         
19814         //this.el.appendTo(on_el);
19815         
19816         var p = this.getPosition();
19817         var box = this.el.getBox();
19818         
19819         if (autoPlace) {
19820             // fixme..
19821         }
19822         var align = Roo.bootstrap.Popover.alignment[placement];
19823         
19824 //        Roo.log(align);
19825         this.el.alignTo(on_el, align[0],align[1]);
19826         //var arrow = this.el.select('.arrow',true).first();
19827         //arrow.set(align[2], 
19828         
19829         this.el.addClass('in');
19830         
19831         
19832         if (this.el.hasClass('fade')) {
19833             // fade it?
19834         }
19835         
19836         this.hoverState = 'in';
19837         
19838         this.fireEvent('show', this);
19839         
19840     },
19841     hide : function()
19842     {
19843         this.el.setXY([0,0]);
19844         this.el.removeClass('in');
19845         this.el.hide();
19846         this.hoverState = null;
19847         
19848         this.fireEvent('hide', this);
19849     }
19850     
19851 });
19852
19853 Roo.bootstrap.Popover.alignment = {
19854     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19855     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19856     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19857     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19858 };
19859
19860  /*
19861  * - LGPL
19862  *
19863  * Progress
19864  * 
19865  */
19866
19867 /**
19868  * @class Roo.bootstrap.Progress
19869  * @extends Roo.bootstrap.Component
19870  * Bootstrap Progress class
19871  * @cfg {Boolean} striped striped of the progress bar
19872  * @cfg {Boolean} active animated of the progress bar
19873  * 
19874  * 
19875  * @constructor
19876  * Create a new Progress
19877  * @param {Object} config The config object
19878  */
19879
19880 Roo.bootstrap.Progress = function(config){
19881     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19882 };
19883
19884 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19885     
19886     striped : false,
19887     active: false,
19888     
19889     getAutoCreate : function(){
19890         var cfg = {
19891             tag: 'div',
19892             cls: 'progress'
19893         };
19894         
19895         
19896         if(this.striped){
19897             cfg.cls += ' progress-striped';
19898         }
19899       
19900         if(this.active){
19901             cfg.cls += ' active';
19902         }
19903         
19904         
19905         return cfg;
19906     }
19907    
19908 });
19909
19910  
19911
19912  /*
19913  * - LGPL
19914  *
19915  * ProgressBar
19916  * 
19917  */
19918
19919 /**
19920  * @class Roo.bootstrap.ProgressBar
19921  * @extends Roo.bootstrap.Component
19922  * Bootstrap ProgressBar class
19923  * @cfg {Number} aria_valuenow aria-value now
19924  * @cfg {Number} aria_valuemin aria-value min
19925  * @cfg {Number} aria_valuemax aria-value max
19926  * @cfg {String} label label for the progress bar
19927  * @cfg {String} panel (success | info | warning | danger )
19928  * @cfg {String} role role of the progress bar
19929  * @cfg {String} sr_only text
19930  * 
19931  * 
19932  * @constructor
19933  * Create a new ProgressBar
19934  * @param {Object} config The config object
19935  */
19936
19937 Roo.bootstrap.ProgressBar = function(config){
19938     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19939 };
19940
19941 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19942     
19943     aria_valuenow : 0,
19944     aria_valuemin : 0,
19945     aria_valuemax : 100,
19946     label : false,
19947     panel : false,
19948     role : false,
19949     sr_only: false,
19950     
19951     getAutoCreate : function()
19952     {
19953         
19954         var cfg = {
19955             tag: 'div',
19956             cls: 'progress-bar',
19957             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19958         };
19959         
19960         if(this.sr_only){
19961             cfg.cn = {
19962                 tag: 'span',
19963                 cls: 'sr-only',
19964                 html: this.sr_only
19965             }
19966         }
19967         
19968         if(this.role){
19969             cfg.role = this.role;
19970         }
19971         
19972         if(this.aria_valuenow){
19973             cfg['aria-valuenow'] = this.aria_valuenow;
19974         }
19975         
19976         if(this.aria_valuemin){
19977             cfg['aria-valuemin'] = this.aria_valuemin;
19978         }
19979         
19980         if(this.aria_valuemax){
19981             cfg['aria-valuemax'] = this.aria_valuemax;
19982         }
19983         
19984         if(this.label && !this.sr_only){
19985             cfg.html = this.label;
19986         }
19987         
19988         if(this.panel){
19989             cfg.cls += ' progress-bar-' + this.panel;
19990         }
19991         
19992         return cfg;
19993     },
19994     
19995     update : function(aria_valuenow)
19996     {
19997         this.aria_valuenow = aria_valuenow;
19998         
19999         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20000     }
20001    
20002 });
20003
20004  
20005
20006  /*
20007  * - LGPL
20008  *
20009  * column
20010  * 
20011  */
20012
20013 /**
20014  * @class Roo.bootstrap.TabGroup
20015  * @extends Roo.bootstrap.Column
20016  * Bootstrap Column class
20017  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20018  * @cfg {Boolean} carousel true to make the group behave like a carousel
20019  * @cfg {Boolean} bullets show bullets for the panels
20020  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20021  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20022  * @cfg {Boolean} showarrow (true|false) show arrow default true
20023  * 
20024  * @constructor
20025  * Create a new TabGroup
20026  * @param {Object} config The config object
20027  */
20028
20029 Roo.bootstrap.TabGroup = function(config){
20030     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20031     if (!this.navId) {
20032         this.navId = Roo.id();
20033     }
20034     this.tabs = [];
20035     Roo.bootstrap.TabGroup.register(this);
20036     
20037 };
20038
20039 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20040     
20041     carousel : false,
20042     transition : false,
20043     bullets : 0,
20044     timer : 0,
20045     autoslide : false,
20046     slideFn : false,
20047     slideOnTouch : false,
20048     showarrow : true,
20049     
20050     getAutoCreate : function()
20051     {
20052         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20053         
20054         cfg.cls += ' tab-content';
20055         
20056         if (this.carousel) {
20057             cfg.cls += ' carousel slide';
20058             
20059             cfg.cn = [{
20060                cls : 'carousel-inner',
20061                cn : []
20062             }];
20063         
20064             if(this.bullets  && !Roo.isTouch){
20065                 
20066                 var bullets = {
20067                     cls : 'carousel-bullets',
20068                     cn : []
20069                 };
20070                
20071                 if(this.bullets_cls){
20072                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20073                 }
20074                 
20075                 bullets.cn.push({
20076                     cls : 'clear'
20077                 });
20078                 
20079                 cfg.cn[0].cn.push(bullets);
20080             }
20081             
20082             if(this.showarrow){
20083                 cfg.cn[0].cn.push({
20084                     tag : 'div',
20085                     class : 'carousel-arrow',
20086                     cn : [
20087                         {
20088                             tag : 'div',
20089                             class : 'carousel-prev',
20090                             cn : [
20091                                 {
20092                                     tag : 'i',
20093                                     class : 'fa fa-chevron-left'
20094                                 }
20095                             ]
20096                         },
20097                         {
20098                             tag : 'div',
20099                             class : 'carousel-next',
20100                             cn : [
20101                                 {
20102                                     tag : 'i',
20103                                     class : 'fa fa-chevron-right'
20104                                 }
20105                             ]
20106                         }
20107                     ]
20108                 });
20109             }
20110             
20111         }
20112         
20113         return cfg;
20114     },
20115     
20116     initEvents:  function()
20117     {
20118 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20119 //            this.el.on("touchstart", this.onTouchStart, this);
20120 //        }
20121         
20122         if(this.autoslide){
20123             var _this = this;
20124             
20125             this.slideFn = window.setInterval(function() {
20126                 _this.showPanelNext();
20127             }, this.timer);
20128         }
20129         
20130         if(this.showarrow){
20131             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20132             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20133         }
20134         
20135         
20136     },
20137     
20138 //    onTouchStart : function(e, el, o)
20139 //    {
20140 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20141 //            return;
20142 //        }
20143 //        
20144 //        this.showPanelNext();
20145 //    },
20146     
20147     
20148     getChildContainer : function()
20149     {
20150         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20151     },
20152     
20153     /**
20154     * register a Navigation item
20155     * @param {Roo.bootstrap.NavItem} the navitem to add
20156     */
20157     register : function(item)
20158     {
20159         this.tabs.push( item);
20160         item.navId = this.navId; // not really needed..
20161         this.addBullet();
20162     
20163     },
20164     
20165     getActivePanel : function()
20166     {
20167         var r = false;
20168         Roo.each(this.tabs, function(t) {
20169             if (t.active) {
20170                 r = t;
20171                 return false;
20172             }
20173             return null;
20174         });
20175         return r;
20176         
20177     },
20178     getPanelByName : function(n)
20179     {
20180         var r = false;
20181         Roo.each(this.tabs, function(t) {
20182             if (t.tabId == n) {
20183                 r = t;
20184                 return false;
20185             }
20186             return null;
20187         });
20188         return r;
20189     },
20190     indexOfPanel : function(p)
20191     {
20192         var r = false;
20193         Roo.each(this.tabs, function(t,i) {
20194             if (t.tabId == p.tabId) {
20195                 r = i;
20196                 return false;
20197             }
20198             return null;
20199         });
20200         return r;
20201     },
20202     /**
20203      * show a specific panel
20204      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20205      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20206      */
20207     showPanel : function (pan)
20208     {
20209         if(this.transition || typeof(pan) == 'undefined'){
20210             Roo.log("waiting for the transitionend");
20211             return false;
20212         }
20213         
20214         if (typeof(pan) == 'number') {
20215             pan = this.tabs[pan];
20216         }
20217         
20218         if (typeof(pan) == 'string') {
20219             pan = this.getPanelByName(pan);
20220         }
20221         
20222         var cur = this.getActivePanel();
20223         
20224         if(!pan || !cur){
20225             Roo.log('pan or acitve pan is undefined');
20226             return false;
20227         }
20228         
20229         if (pan.tabId == this.getActivePanel().tabId) {
20230             return true;
20231         }
20232         
20233         if (false === cur.fireEvent('beforedeactivate')) {
20234             return false;
20235         }
20236         
20237         if(this.bullets > 0 && !Roo.isTouch){
20238             this.setActiveBullet(this.indexOfPanel(pan));
20239         }
20240         
20241         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20242             
20243             //class="carousel-item carousel-item-next carousel-item-left"
20244             
20245             this.transition = true;
20246             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20247             var lr = dir == 'next' ? 'left' : 'right';
20248             pan.el.addClass(dir); // or prev
20249             pan.el.addClass('carousel-item-' + dir); // or prev
20250             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20251             cur.el.addClass(lr); // or right
20252             pan.el.addClass(lr);
20253             cur.el.addClass('carousel-item-' +lr); // or right
20254             pan.el.addClass('carousel-item-' +lr);
20255             
20256             
20257             var _this = this;
20258             cur.el.on('transitionend', function() {
20259                 Roo.log("trans end?");
20260                 
20261                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20262                 pan.setActive(true);
20263                 
20264                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20265                 cur.setActive(false);
20266                 
20267                 _this.transition = false;
20268                 
20269             }, this, { single:  true } );
20270             
20271             return true;
20272         }
20273         
20274         cur.setActive(false);
20275         pan.setActive(true);
20276         
20277         return true;
20278         
20279     },
20280     showPanelNext : function()
20281     {
20282         var i = this.indexOfPanel(this.getActivePanel());
20283         
20284         if (i >= this.tabs.length - 1 && !this.autoslide) {
20285             return;
20286         }
20287         
20288         if (i >= this.tabs.length - 1 && this.autoslide) {
20289             i = -1;
20290         }
20291         
20292         this.showPanel(this.tabs[i+1]);
20293     },
20294     
20295     showPanelPrev : function()
20296     {
20297         var i = this.indexOfPanel(this.getActivePanel());
20298         
20299         if (i  < 1 && !this.autoslide) {
20300             return;
20301         }
20302         
20303         if (i < 1 && this.autoslide) {
20304             i = this.tabs.length;
20305         }
20306         
20307         this.showPanel(this.tabs[i-1]);
20308     },
20309     
20310     
20311     addBullet: function()
20312     {
20313         if(!this.bullets || Roo.isTouch){
20314             return;
20315         }
20316         var ctr = this.el.select('.carousel-bullets',true).first();
20317         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20318         var bullet = ctr.createChild({
20319             cls : 'bullet bullet-' + i
20320         },ctr.dom.lastChild);
20321         
20322         
20323         var _this = this;
20324         
20325         bullet.on('click', (function(e, el, o, ii, t){
20326
20327             e.preventDefault();
20328
20329             this.showPanel(ii);
20330
20331             if(this.autoslide && this.slideFn){
20332                 clearInterval(this.slideFn);
20333                 this.slideFn = window.setInterval(function() {
20334                     _this.showPanelNext();
20335                 }, this.timer);
20336             }
20337
20338         }).createDelegate(this, [i, bullet], true));
20339                 
20340         
20341     },
20342      
20343     setActiveBullet : function(i)
20344     {
20345         if(Roo.isTouch){
20346             return;
20347         }
20348         
20349         Roo.each(this.el.select('.bullet', true).elements, function(el){
20350             el.removeClass('selected');
20351         });
20352
20353         var bullet = this.el.select('.bullet-' + i, true).first();
20354         
20355         if(!bullet){
20356             return;
20357         }
20358         
20359         bullet.addClass('selected');
20360     }
20361     
20362     
20363   
20364 });
20365
20366  
20367
20368  
20369  
20370 Roo.apply(Roo.bootstrap.TabGroup, {
20371     
20372     groups: {},
20373      /**
20374     * register a Navigation Group
20375     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20376     */
20377     register : function(navgrp)
20378     {
20379         this.groups[navgrp.navId] = navgrp;
20380         
20381     },
20382     /**
20383     * fetch a Navigation Group based on the navigation ID
20384     * if one does not exist , it will get created.
20385     * @param {string} the navgroup to add
20386     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20387     */
20388     get: function(navId) {
20389         if (typeof(this.groups[navId]) == 'undefined') {
20390             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20391         }
20392         return this.groups[navId] ;
20393     }
20394     
20395     
20396     
20397 });
20398
20399  /*
20400  * - LGPL
20401  *
20402  * TabPanel
20403  * 
20404  */
20405
20406 /**
20407  * @class Roo.bootstrap.TabPanel
20408  * @extends Roo.bootstrap.Component
20409  * Bootstrap TabPanel class
20410  * @cfg {Boolean} active panel active
20411  * @cfg {String} html panel content
20412  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20413  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20414  * @cfg {String} href click to link..
20415  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20416  * 
20417  * 
20418  * @constructor
20419  * Create a new TabPanel
20420  * @param {Object} config The config object
20421  */
20422
20423 Roo.bootstrap.TabPanel = function(config){
20424     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20425     this.addEvents({
20426         /**
20427              * @event changed
20428              * Fires when the active status changes
20429              * @param {Roo.bootstrap.TabPanel} this
20430              * @param {Boolean} state the new state
20431             
20432          */
20433         'changed': true,
20434         /**
20435              * @event beforedeactivate
20436              * Fires before a tab is de-activated - can be used to do validation on a form.
20437              * @param {Roo.bootstrap.TabPanel} this
20438              * @return {Boolean} false if there is an error
20439             
20440          */
20441         'beforedeactivate': true
20442      });
20443     
20444     this.tabId = this.tabId || Roo.id();
20445   
20446 };
20447
20448 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20449     
20450     active: false,
20451     html: false,
20452     tabId: false,
20453     navId : false,
20454     href : '',
20455     touchSlide : false,
20456     getAutoCreate : function(){
20457         
20458         
20459         var cfg = {
20460             tag: 'div',
20461             // item is needed for carousel - not sure if it has any effect otherwise
20462             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20463             html: this.html || ''
20464         };
20465         
20466         if(this.active){
20467             cfg.cls += ' active';
20468         }
20469         
20470         if(this.tabId){
20471             cfg.tabId = this.tabId;
20472         }
20473         
20474         
20475         
20476         return cfg;
20477     },
20478     
20479     initEvents:  function()
20480     {
20481         var p = this.parent();
20482         
20483         this.navId = this.navId || p.navId;
20484         
20485         if (typeof(this.navId) != 'undefined') {
20486             // not really needed.. but just in case.. parent should be a NavGroup.
20487             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20488             
20489             tg.register(this);
20490             
20491             var i = tg.tabs.length - 1;
20492             
20493             if(this.active && tg.bullets > 0 && i < tg.bullets){
20494                 tg.setActiveBullet(i);
20495             }
20496         }
20497         
20498         this.el.on('click', this.onClick, this);
20499         
20500         if(Roo.isTouch && this.touchSlide){
20501             this.el.on("touchstart", this.onTouchStart, this);
20502             this.el.on("touchmove", this.onTouchMove, this);
20503             this.el.on("touchend", this.onTouchEnd, this);
20504         }
20505         
20506     },
20507     
20508     onRender : function(ct, position)
20509     {
20510         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20511     },
20512     
20513     setActive : function(state)
20514     {
20515         Roo.log("panel - set active " + this.tabId + "=" + state);
20516         
20517         this.active = state;
20518         if (!state) {
20519             this.el.removeClass('active');
20520             
20521         } else  if (!this.el.hasClass('active')) {
20522             this.el.addClass('active');
20523         }
20524         
20525         this.fireEvent('changed', this, state);
20526     },
20527     
20528     onClick : function(e)
20529     {
20530         e.preventDefault();
20531         
20532         if(!this.href.length){
20533             return;
20534         }
20535         
20536         window.location.href = this.href;
20537     },
20538     
20539     startX : 0,
20540     startY : 0,
20541     endX : 0,
20542     endY : 0,
20543     swiping : false,
20544     
20545     onTouchStart : function(e)
20546     {
20547         this.swiping = false;
20548         
20549         this.startX = e.browserEvent.touches[0].clientX;
20550         this.startY = e.browserEvent.touches[0].clientY;
20551     },
20552     
20553     onTouchMove : function(e)
20554     {
20555         this.swiping = true;
20556         
20557         this.endX = e.browserEvent.touches[0].clientX;
20558         this.endY = e.browserEvent.touches[0].clientY;
20559     },
20560     
20561     onTouchEnd : function(e)
20562     {
20563         if(!this.swiping){
20564             this.onClick(e);
20565             return;
20566         }
20567         
20568         var tabGroup = this.parent();
20569         
20570         if(this.endX > this.startX){ // swiping right
20571             tabGroup.showPanelPrev();
20572             return;
20573         }
20574         
20575         if(this.startX > this.endX){ // swiping left
20576             tabGroup.showPanelNext();
20577             return;
20578         }
20579     }
20580     
20581     
20582 });
20583  
20584
20585  
20586
20587  /*
20588  * - LGPL
20589  *
20590  * DateField
20591  * 
20592  */
20593
20594 /**
20595  * @class Roo.bootstrap.DateField
20596  * @extends Roo.bootstrap.Input
20597  * Bootstrap DateField class
20598  * @cfg {Number} weekStart default 0
20599  * @cfg {String} viewMode default empty, (months|years)
20600  * @cfg {String} minViewMode default empty, (months|years)
20601  * @cfg {Number} startDate default -Infinity
20602  * @cfg {Number} endDate default Infinity
20603  * @cfg {Boolean} todayHighlight default false
20604  * @cfg {Boolean} todayBtn default false
20605  * @cfg {Boolean} calendarWeeks default false
20606  * @cfg {Object} daysOfWeekDisabled default empty
20607  * @cfg {Boolean} singleMode default false (true | false)
20608  * 
20609  * @cfg {Boolean} keyboardNavigation default true
20610  * @cfg {String} language default en
20611  * 
20612  * @constructor
20613  * Create a new DateField
20614  * @param {Object} config The config object
20615  */
20616
20617 Roo.bootstrap.DateField = function(config){
20618     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20619      this.addEvents({
20620             /**
20621              * @event show
20622              * Fires when this field show.
20623              * @param {Roo.bootstrap.DateField} this
20624              * @param {Mixed} date The date value
20625              */
20626             show : true,
20627             /**
20628              * @event show
20629              * Fires when this field hide.
20630              * @param {Roo.bootstrap.DateField} this
20631              * @param {Mixed} date The date value
20632              */
20633             hide : true,
20634             /**
20635              * @event select
20636              * Fires when select a date.
20637              * @param {Roo.bootstrap.DateField} this
20638              * @param {Mixed} date The date value
20639              */
20640             select : true,
20641             /**
20642              * @event beforeselect
20643              * Fires when before select a date.
20644              * @param {Roo.bootstrap.DateField} this
20645              * @param {Mixed} date The date value
20646              */
20647             beforeselect : true
20648         });
20649 };
20650
20651 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20652     
20653     /**
20654      * @cfg {String} format
20655      * The default date format string which can be overriden for localization support.  The format must be
20656      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20657      */
20658     format : "m/d/y",
20659     /**
20660      * @cfg {String} altFormats
20661      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20662      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20663      */
20664     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20665     
20666     weekStart : 0,
20667     
20668     viewMode : '',
20669     
20670     minViewMode : '',
20671     
20672     todayHighlight : false,
20673     
20674     todayBtn: false,
20675     
20676     language: 'en',
20677     
20678     keyboardNavigation: true,
20679     
20680     calendarWeeks: false,
20681     
20682     startDate: -Infinity,
20683     
20684     endDate: Infinity,
20685     
20686     daysOfWeekDisabled: [],
20687     
20688     _events: [],
20689     
20690     singleMode : false,
20691     
20692     UTCDate: function()
20693     {
20694         return new Date(Date.UTC.apply(Date, arguments));
20695     },
20696     
20697     UTCToday: function()
20698     {
20699         var today = new Date();
20700         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20701     },
20702     
20703     getDate: function() {
20704             var d = this.getUTCDate();
20705             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20706     },
20707     
20708     getUTCDate: function() {
20709             return this.date;
20710     },
20711     
20712     setDate: function(d) {
20713             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20714     },
20715     
20716     setUTCDate: function(d) {
20717             this.date = d;
20718             this.setValue(this.formatDate(this.date));
20719     },
20720         
20721     onRender: function(ct, position)
20722     {
20723         
20724         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20725         
20726         this.language = this.language || 'en';
20727         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20728         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20729         
20730         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20731         this.format = this.format || 'm/d/y';
20732         this.isInline = false;
20733         this.isInput = true;
20734         this.component = this.el.select('.add-on', true).first() || false;
20735         this.component = (this.component && this.component.length === 0) ? false : this.component;
20736         this.hasInput = this.component && this.inputEl().length;
20737         
20738         if (typeof(this.minViewMode === 'string')) {
20739             switch (this.minViewMode) {
20740                 case 'months':
20741                     this.minViewMode = 1;
20742                     break;
20743                 case 'years':
20744                     this.minViewMode = 2;
20745                     break;
20746                 default:
20747                     this.minViewMode = 0;
20748                     break;
20749             }
20750         }
20751         
20752         if (typeof(this.viewMode === 'string')) {
20753             switch (this.viewMode) {
20754                 case 'months':
20755                     this.viewMode = 1;
20756                     break;
20757                 case 'years':
20758                     this.viewMode = 2;
20759                     break;
20760                 default:
20761                     this.viewMode = 0;
20762                     break;
20763             }
20764         }
20765                 
20766         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20767         
20768 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20769         
20770         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20771         
20772         this.picker().on('mousedown', this.onMousedown, this);
20773         this.picker().on('click', this.onClick, this);
20774         
20775         this.picker().addClass('datepicker-dropdown');
20776         
20777         this.startViewMode = this.viewMode;
20778         
20779         if(this.singleMode){
20780             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20781                 v.setVisibilityMode(Roo.Element.DISPLAY);
20782                 v.hide();
20783             });
20784             
20785             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20786                 v.setStyle('width', '189px');
20787             });
20788         }
20789         
20790         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20791             if(!this.calendarWeeks){
20792                 v.remove();
20793                 return;
20794             }
20795             
20796             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20797             v.attr('colspan', function(i, val){
20798                 return parseInt(val) + 1;
20799             });
20800         });
20801                         
20802         
20803         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20804         
20805         this.setStartDate(this.startDate);
20806         this.setEndDate(this.endDate);
20807         
20808         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20809         
20810         this.fillDow();
20811         this.fillMonths();
20812         this.update();
20813         this.showMode();
20814         
20815         if(this.isInline) {
20816             this.showPopup();
20817         }
20818     },
20819     
20820     picker : function()
20821     {
20822         return this.pickerEl;
20823 //        return this.el.select('.datepicker', true).first();
20824     },
20825     
20826     fillDow: function()
20827     {
20828         var dowCnt = this.weekStart;
20829         
20830         var dow = {
20831             tag: 'tr',
20832             cn: [
20833                 
20834             ]
20835         };
20836         
20837         if(this.calendarWeeks){
20838             dow.cn.push({
20839                 tag: 'th',
20840                 cls: 'cw',
20841                 html: '&nbsp;'
20842             })
20843         }
20844         
20845         while (dowCnt < this.weekStart + 7) {
20846             dow.cn.push({
20847                 tag: 'th',
20848                 cls: 'dow',
20849                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20850             });
20851         }
20852         
20853         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20854     },
20855     
20856     fillMonths: function()
20857     {    
20858         var i = 0;
20859         var months = this.picker().select('>.datepicker-months td', true).first();
20860         
20861         months.dom.innerHTML = '';
20862         
20863         while (i < 12) {
20864             var month = {
20865                 tag: 'span',
20866                 cls: 'month',
20867                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20868             };
20869             
20870             months.createChild(month);
20871         }
20872         
20873     },
20874     
20875     update: function()
20876     {
20877         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;
20878         
20879         if (this.date < this.startDate) {
20880             this.viewDate = new Date(this.startDate);
20881         } else if (this.date > this.endDate) {
20882             this.viewDate = new Date(this.endDate);
20883         } else {
20884             this.viewDate = new Date(this.date);
20885         }
20886         
20887         this.fill();
20888     },
20889     
20890     fill: function() 
20891     {
20892         var d = new Date(this.viewDate),
20893                 year = d.getUTCFullYear(),
20894                 month = d.getUTCMonth(),
20895                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20896                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20897                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20898                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20899                 currentDate = this.date && this.date.valueOf(),
20900                 today = this.UTCToday();
20901         
20902         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20903         
20904 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20905         
20906 //        this.picker.select('>tfoot th.today').
20907 //                                              .text(dates[this.language].today)
20908 //                                              .toggle(this.todayBtn !== false);
20909     
20910         this.updateNavArrows();
20911         this.fillMonths();
20912                                                 
20913         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20914         
20915         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20916          
20917         prevMonth.setUTCDate(day);
20918         
20919         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20920         
20921         var nextMonth = new Date(prevMonth);
20922         
20923         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20924         
20925         nextMonth = nextMonth.valueOf();
20926         
20927         var fillMonths = false;
20928         
20929         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20930         
20931         while(prevMonth.valueOf() <= nextMonth) {
20932             var clsName = '';
20933             
20934             if (prevMonth.getUTCDay() === this.weekStart) {
20935                 if(fillMonths){
20936                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20937                 }
20938                     
20939                 fillMonths = {
20940                     tag: 'tr',
20941                     cn: []
20942                 };
20943                 
20944                 if(this.calendarWeeks){
20945                     // ISO 8601: First week contains first thursday.
20946                     // ISO also states week starts on Monday, but we can be more abstract here.
20947                     var
20948                     // Start of current week: based on weekstart/current date
20949                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20950                     // Thursday of this week
20951                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20952                     // First Thursday of year, year from thursday
20953                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20954                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20955                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20956                     
20957                     fillMonths.cn.push({
20958                         tag: 'td',
20959                         cls: 'cw',
20960                         html: calWeek
20961                     });
20962                 }
20963             }
20964             
20965             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20966                 clsName += ' old';
20967             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20968                 clsName += ' new';
20969             }
20970             if (this.todayHighlight &&
20971                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20972                 prevMonth.getUTCMonth() == today.getMonth() &&
20973                 prevMonth.getUTCDate() == today.getDate()) {
20974                 clsName += ' today';
20975             }
20976             
20977             if (currentDate && prevMonth.valueOf() === currentDate) {
20978                 clsName += ' active';
20979             }
20980             
20981             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20982                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20983                     clsName += ' disabled';
20984             }
20985             
20986             fillMonths.cn.push({
20987                 tag: 'td',
20988                 cls: 'day ' + clsName,
20989                 html: prevMonth.getDate()
20990             });
20991             
20992             prevMonth.setDate(prevMonth.getDate()+1);
20993         }
20994           
20995         var currentYear = this.date && this.date.getUTCFullYear();
20996         var currentMonth = this.date && this.date.getUTCMonth();
20997         
20998         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20999         
21000         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21001             v.removeClass('active');
21002             
21003             if(currentYear === year && k === currentMonth){
21004                 v.addClass('active');
21005             }
21006             
21007             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21008                 v.addClass('disabled');
21009             }
21010             
21011         });
21012         
21013         
21014         year = parseInt(year/10, 10) * 10;
21015         
21016         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21017         
21018         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21019         
21020         year -= 1;
21021         for (var i = -1; i < 11; i++) {
21022             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21023                 tag: 'span',
21024                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21025                 html: year
21026             });
21027             
21028             year += 1;
21029         }
21030     },
21031     
21032     showMode: function(dir) 
21033     {
21034         if (dir) {
21035             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21036         }
21037         
21038         Roo.each(this.picker().select('>div',true).elements, function(v){
21039             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21040             v.hide();
21041         });
21042         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21043     },
21044     
21045     place: function()
21046     {
21047         if(this.isInline) {
21048             return;
21049         }
21050         
21051         this.picker().removeClass(['bottom', 'top']);
21052         
21053         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21054             /*
21055              * place to the top of element!
21056              *
21057              */
21058             
21059             this.picker().addClass('top');
21060             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21061             
21062             return;
21063         }
21064         
21065         this.picker().addClass('bottom');
21066         
21067         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21068     },
21069     
21070     parseDate : function(value)
21071     {
21072         if(!value || value instanceof Date){
21073             return value;
21074         }
21075         var v = Date.parseDate(value, this.format);
21076         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21077             v = Date.parseDate(value, 'Y-m-d');
21078         }
21079         if(!v && this.altFormats){
21080             if(!this.altFormatsArray){
21081                 this.altFormatsArray = this.altFormats.split("|");
21082             }
21083             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21084                 v = Date.parseDate(value, this.altFormatsArray[i]);
21085             }
21086         }
21087         return v;
21088     },
21089     
21090     formatDate : function(date, fmt)
21091     {   
21092         return (!date || !(date instanceof Date)) ?
21093         date : date.dateFormat(fmt || this.format);
21094     },
21095     
21096     onFocus : function()
21097     {
21098         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21099         this.showPopup();
21100     },
21101     
21102     onBlur : function()
21103     {
21104         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21105         
21106         var d = this.inputEl().getValue();
21107         
21108         this.setValue(d);
21109                 
21110         this.hidePopup();
21111     },
21112     
21113     showPopup : function()
21114     {
21115         this.picker().show();
21116         this.update();
21117         this.place();
21118         
21119         this.fireEvent('showpopup', this, this.date);
21120     },
21121     
21122     hidePopup : function()
21123     {
21124         if(this.isInline) {
21125             return;
21126         }
21127         this.picker().hide();
21128         this.viewMode = this.startViewMode;
21129         this.showMode();
21130         
21131         this.fireEvent('hidepopup', this, this.date);
21132         
21133     },
21134     
21135     onMousedown: function(e)
21136     {
21137         e.stopPropagation();
21138         e.preventDefault();
21139     },
21140     
21141     keyup: function(e)
21142     {
21143         Roo.bootstrap.DateField.superclass.keyup.call(this);
21144         this.update();
21145     },
21146
21147     setValue: function(v)
21148     {
21149         if(this.fireEvent('beforeselect', this, v) !== false){
21150             var d = new Date(this.parseDate(v) ).clearTime();
21151         
21152             if(isNaN(d.getTime())){
21153                 this.date = this.viewDate = '';
21154                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21155                 return;
21156             }
21157
21158             v = this.formatDate(d);
21159
21160             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21161
21162             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21163
21164             this.update();
21165
21166             this.fireEvent('select', this, this.date);
21167         }
21168     },
21169     
21170     getValue: function()
21171     {
21172         return this.formatDate(this.date);
21173     },
21174     
21175     fireKey: function(e)
21176     {
21177         if (!this.picker().isVisible()){
21178             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21179                 this.showPopup();
21180             }
21181             return;
21182         }
21183         
21184         var dateChanged = false,
21185         dir, day, month,
21186         newDate, newViewDate;
21187         
21188         switch(e.keyCode){
21189             case 27: // escape
21190                 this.hidePopup();
21191                 e.preventDefault();
21192                 break;
21193             case 37: // left
21194             case 39: // right
21195                 if (!this.keyboardNavigation) {
21196                     break;
21197                 }
21198                 dir = e.keyCode == 37 ? -1 : 1;
21199                 
21200                 if (e.ctrlKey){
21201                     newDate = this.moveYear(this.date, dir);
21202                     newViewDate = this.moveYear(this.viewDate, dir);
21203                 } else if (e.shiftKey){
21204                     newDate = this.moveMonth(this.date, dir);
21205                     newViewDate = this.moveMonth(this.viewDate, dir);
21206                 } else {
21207                     newDate = new Date(this.date);
21208                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21209                     newViewDate = new Date(this.viewDate);
21210                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21211                 }
21212                 if (this.dateWithinRange(newDate)){
21213                     this.date = newDate;
21214                     this.viewDate = newViewDate;
21215                     this.setValue(this.formatDate(this.date));
21216 //                    this.update();
21217                     e.preventDefault();
21218                     dateChanged = true;
21219                 }
21220                 break;
21221             case 38: // up
21222             case 40: // down
21223                 if (!this.keyboardNavigation) {
21224                     break;
21225                 }
21226                 dir = e.keyCode == 38 ? -1 : 1;
21227                 if (e.ctrlKey){
21228                     newDate = this.moveYear(this.date, dir);
21229                     newViewDate = this.moveYear(this.viewDate, dir);
21230                 } else if (e.shiftKey){
21231                     newDate = this.moveMonth(this.date, dir);
21232                     newViewDate = this.moveMonth(this.viewDate, dir);
21233                 } else {
21234                     newDate = new Date(this.date);
21235                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21236                     newViewDate = new Date(this.viewDate);
21237                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21238                 }
21239                 if (this.dateWithinRange(newDate)){
21240                     this.date = newDate;
21241                     this.viewDate = newViewDate;
21242                     this.setValue(this.formatDate(this.date));
21243 //                    this.update();
21244                     e.preventDefault();
21245                     dateChanged = true;
21246                 }
21247                 break;
21248             case 13: // enter
21249                 this.setValue(this.formatDate(this.date));
21250                 this.hidePopup();
21251                 e.preventDefault();
21252                 break;
21253             case 9: // tab
21254                 this.setValue(this.formatDate(this.date));
21255                 this.hidePopup();
21256                 break;
21257             case 16: // shift
21258             case 17: // ctrl
21259             case 18: // alt
21260                 break;
21261             default :
21262                 this.hidePopup();
21263                 
21264         }
21265     },
21266     
21267     
21268     onClick: function(e) 
21269     {
21270         e.stopPropagation();
21271         e.preventDefault();
21272         
21273         var target = e.getTarget();
21274         
21275         if(target.nodeName.toLowerCase() === 'i'){
21276             target = Roo.get(target).dom.parentNode;
21277         }
21278         
21279         var nodeName = target.nodeName;
21280         var className = target.className;
21281         var html = target.innerHTML;
21282         //Roo.log(nodeName);
21283         
21284         switch(nodeName.toLowerCase()) {
21285             case 'th':
21286                 switch(className) {
21287                     case 'switch':
21288                         this.showMode(1);
21289                         break;
21290                     case 'prev':
21291                     case 'next':
21292                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21293                         switch(this.viewMode){
21294                                 case 0:
21295                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21296                                         break;
21297                                 case 1:
21298                                 case 2:
21299                                         this.viewDate = this.moveYear(this.viewDate, dir);
21300                                         break;
21301                         }
21302                         this.fill();
21303                         break;
21304                     case 'today':
21305                         var date = new Date();
21306                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21307 //                        this.fill()
21308                         this.setValue(this.formatDate(this.date));
21309                         
21310                         this.hidePopup();
21311                         break;
21312                 }
21313                 break;
21314             case 'span':
21315                 if (className.indexOf('disabled') < 0) {
21316                     this.viewDate.setUTCDate(1);
21317                     if (className.indexOf('month') > -1) {
21318                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21319                     } else {
21320                         var year = parseInt(html, 10) || 0;
21321                         this.viewDate.setUTCFullYear(year);
21322                         
21323                     }
21324                     
21325                     if(this.singleMode){
21326                         this.setValue(this.formatDate(this.viewDate));
21327                         this.hidePopup();
21328                         return;
21329                     }
21330                     
21331                     this.showMode(-1);
21332                     this.fill();
21333                 }
21334                 break;
21335                 
21336             case 'td':
21337                 //Roo.log(className);
21338                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21339                     var day = parseInt(html, 10) || 1;
21340                     var year = this.viewDate.getUTCFullYear(),
21341                         month = this.viewDate.getUTCMonth();
21342
21343                     if (className.indexOf('old') > -1) {
21344                         if(month === 0 ){
21345                             month = 11;
21346                             year -= 1;
21347                         }else{
21348                             month -= 1;
21349                         }
21350                     } else if (className.indexOf('new') > -1) {
21351                         if (month == 11) {
21352                             month = 0;
21353                             year += 1;
21354                         } else {
21355                             month += 1;
21356                         }
21357                     }
21358                     //Roo.log([year,month,day]);
21359                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21360                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21361 //                    this.fill();
21362                     //Roo.log(this.formatDate(this.date));
21363                     this.setValue(this.formatDate(this.date));
21364                     this.hidePopup();
21365                 }
21366                 break;
21367         }
21368     },
21369     
21370     setStartDate: function(startDate)
21371     {
21372         this.startDate = startDate || -Infinity;
21373         if (this.startDate !== -Infinity) {
21374             this.startDate = this.parseDate(this.startDate);
21375         }
21376         this.update();
21377         this.updateNavArrows();
21378     },
21379
21380     setEndDate: function(endDate)
21381     {
21382         this.endDate = endDate || Infinity;
21383         if (this.endDate !== Infinity) {
21384             this.endDate = this.parseDate(this.endDate);
21385         }
21386         this.update();
21387         this.updateNavArrows();
21388     },
21389     
21390     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21391     {
21392         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21393         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21394             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21395         }
21396         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21397             return parseInt(d, 10);
21398         });
21399         this.update();
21400         this.updateNavArrows();
21401     },
21402     
21403     updateNavArrows: function() 
21404     {
21405         if(this.singleMode){
21406             return;
21407         }
21408         
21409         var d = new Date(this.viewDate),
21410         year = d.getUTCFullYear(),
21411         month = d.getUTCMonth();
21412         
21413         Roo.each(this.picker().select('.prev', true).elements, function(v){
21414             v.show();
21415             switch (this.viewMode) {
21416                 case 0:
21417
21418                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21419                         v.hide();
21420                     }
21421                     break;
21422                 case 1:
21423                 case 2:
21424                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21425                         v.hide();
21426                     }
21427                     break;
21428             }
21429         });
21430         
21431         Roo.each(this.picker().select('.next', true).elements, function(v){
21432             v.show();
21433             switch (this.viewMode) {
21434                 case 0:
21435
21436                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21437                         v.hide();
21438                     }
21439                     break;
21440                 case 1:
21441                 case 2:
21442                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21443                         v.hide();
21444                     }
21445                     break;
21446             }
21447         })
21448     },
21449     
21450     moveMonth: function(date, dir)
21451     {
21452         if (!dir) {
21453             return date;
21454         }
21455         var new_date = new Date(date.valueOf()),
21456         day = new_date.getUTCDate(),
21457         month = new_date.getUTCMonth(),
21458         mag = Math.abs(dir),
21459         new_month, test;
21460         dir = dir > 0 ? 1 : -1;
21461         if (mag == 1){
21462             test = dir == -1
21463             // If going back one month, make sure month is not current month
21464             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21465             ? function(){
21466                 return new_date.getUTCMonth() == month;
21467             }
21468             // If going forward one month, make sure month is as expected
21469             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21470             : function(){
21471                 return new_date.getUTCMonth() != new_month;
21472             };
21473             new_month = month + dir;
21474             new_date.setUTCMonth(new_month);
21475             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21476             if (new_month < 0 || new_month > 11) {
21477                 new_month = (new_month + 12) % 12;
21478             }
21479         } else {
21480             // For magnitudes >1, move one month at a time...
21481             for (var i=0; i<mag; i++) {
21482                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21483                 new_date = this.moveMonth(new_date, dir);
21484             }
21485             // ...then reset the day, keeping it in the new month
21486             new_month = new_date.getUTCMonth();
21487             new_date.setUTCDate(day);
21488             test = function(){
21489                 return new_month != new_date.getUTCMonth();
21490             };
21491         }
21492         // Common date-resetting loop -- if date is beyond end of month, make it
21493         // end of month
21494         while (test()){
21495             new_date.setUTCDate(--day);
21496             new_date.setUTCMonth(new_month);
21497         }
21498         return new_date;
21499     },
21500
21501     moveYear: function(date, dir)
21502     {
21503         return this.moveMonth(date, dir*12);
21504     },
21505
21506     dateWithinRange: function(date)
21507     {
21508         return date >= this.startDate && date <= this.endDate;
21509     },
21510
21511     
21512     remove: function() 
21513     {
21514         this.picker().remove();
21515     },
21516     
21517     validateValue : function(value)
21518     {
21519         if(this.getVisibilityEl().hasClass('hidden')){
21520             return true;
21521         }
21522         
21523         if(value.length < 1)  {
21524             if(this.allowBlank){
21525                 return true;
21526             }
21527             return false;
21528         }
21529         
21530         if(value.length < this.minLength){
21531             return false;
21532         }
21533         if(value.length > this.maxLength){
21534             return false;
21535         }
21536         if(this.vtype){
21537             var vt = Roo.form.VTypes;
21538             if(!vt[this.vtype](value, this)){
21539                 return false;
21540             }
21541         }
21542         if(typeof this.validator == "function"){
21543             var msg = this.validator(value);
21544             if(msg !== true){
21545                 return false;
21546             }
21547         }
21548         
21549         if(this.regex && !this.regex.test(value)){
21550             return false;
21551         }
21552         
21553         if(typeof(this.parseDate(value)) == 'undefined'){
21554             return false;
21555         }
21556         
21557         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21558             return false;
21559         }      
21560         
21561         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21562             return false;
21563         } 
21564         
21565         
21566         return true;
21567     },
21568     
21569     reset : function()
21570     {
21571         this.date = this.viewDate = '';
21572         
21573         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21574     }
21575    
21576 });
21577
21578 Roo.apply(Roo.bootstrap.DateField,  {
21579     
21580     head : {
21581         tag: 'thead',
21582         cn: [
21583         {
21584             tag: 'tr',
21585             cn: [
21586             {
21587                 tag: 'th',
21588                 cls: 'prev',
21589                 html: '<i class="fa fa-arrow-left"/>'
21590             },
21591             {
21592                 tag: 'th',
21593                 cls: 'switch',
21594                 colspan: '5'
21595             },
21596             {
21597                 tag: 'th',
21598                 cls: 'next',
21599                 html: '<i class="fa fa-arrow-right"/>'
21600             }
21601
21602             ]
21603         }
21604         ]
21605     },
21606     
21607     content : {
21608         tag: 'tbody',
21609         cn: [
21610         {
21611             tag: 'tr',
21612             cn: [
21613             {
21614                 tag: 'td',
21615                 colspan: '7'
21616             }
21617             ]
21618         }
21619         ]
21620     },
21621     
21622     footer : {
21623         tag: 'tfoot',
21624         cn: [
21625         {
21626             tag: 'tr',
21627             cn: [
21628             {
21629                 tag: 'th',
21630                 colspan: '7',
21631                 cls: 'today'
21632             }
21633                     
21634             ]
21635         }
21636         ]
21637     },
21638     
21639     dates:{
21640         en: {
21641             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21642             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21643             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21644             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21645             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21646             today: "Today"
21647         }
21648     },
21649     
21650     modes: [
21651     {
21652         clsName: 'days',
21653         navFnc: 'Month',
21654         navStep: 1
21655     },
21656     {
21657         clsName: 'months',
21658         navFnc: 'FullYear',
21659         navStep: 1
21660     },
21661     {
21662         clsName: 'years',
21663         navFnc: 'FullYear',
21664         navStep: 10
21665     }]
21666 });
21667
21668 Roo.apply(Roo.bootstrap.DateField,  {
21669   
21670     template : {
21671         tag: 'div',
21672         cls: 'datepicker dropdown-menu roo-dynamic',
21673         cn: [
21674         {
21675             tag: 'div',
21676             cls: 'datepicker-days',
21677             cn: [
21678             {
21679                 tag: 'table',
21680                 cls: 'table-condensed',
21681                 cn:[
21682                 Roo.bootstrap.DateField.head,
21683                 {
21684                     tag: 'tbody'
21685                 },
21686                 Roo.bootstrap.DateField.footer
21687                 ]
21688             }
21689             ]
21690         },
21691         {
21692             tag: 'div',
21693             cls: 'datepicker-months',
21694             cn: [
21695             {
21696                 tag: 'table',
21697                 cls: 'table-condensed',
21698                 cn:[
21699                 Roo.bootstrap.DateField.head,
21700                 Roo.bootstrap.DateField.content,
21701                 Roo.bootstrap.DateField.footer
21702                 ]
21703             }
21704             ]
21705         },
21706         {
21707             tag: 'div',
21708             cls: 'datepicker-years',
21709             cn: [
21710             {
21711                 tag: 'table',
21712                 cls: 'table-condensed',
21713                 cn:[
21714                 Roo.bootstrap.DateField.head,
21715                 Roo.bootstrap.DateField.content,
21716                 Roo.bootstrap.DateField.footer
21717                 ]
21718             }
21719             ]
21720         }
21721         ]
21722     }
21723 });
21724
21725  
21726
21727  /*
21728  * - LGPL
21729  *
21730  * TimeField
21731  * 
21732  */
21733
21734 /**
21735  * @class Roo.bootstrap.TimeField
21736  * @extends Roo.bootstrap.Input
21737  * Bootstrap DateField class
21738  * 
21739  * 
21740  * @constructor
21741  * Create a new TimeField
21742  * @param {Object} config The config object
21743  */
21744
21745 Roo.bootstrap.TimeField = function(config){
21746     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21747     this.addEvents({
21748             /**
21749              * @event show
21750              * Fires when this field show.
21751              * @param {Roo.bootstrap.DateField} thisthis
21752              * @param {Mixed} date The date value
21753              */
21754             show : true,
21755             /**
21756              * @event show
21757              * Fires when this field hide.
21758              * @param {Roo.bootstrap.DateField} this
21759              * @param {Mixed} date The date value
21760              */
21761             hide : true,
21762             /**
21763              * @event select
21764              * Fires when select a date.
21765              * @param {Roo.bootstrap.DateField} this
21766              * @param {Mixed} date The date value
21767              */
21768             select : true
21769         });
21770 };
21771
21772 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21773     
21774     /**
21775      * @cfg {String} format
21776      * The default time format string which can be overriden for localization support.  The format must be
21777      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21778      */
21779     format : "H:i",
21780        
21781     onRender: function(ct, position)
21782     {
21783         
21784         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21785                 
21786         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21787         
21788         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21789         
21790         this.pop = this.picker().select('>.datepicker-time',true).first();
21791         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21792         
21793         this.picker().on('mousedown', this.onMousedown, this);
21794         this.picker().on('click', this.onClick, this);
21795         
21796         this.picker().addClass('datepicker-dropdown');
21797     
21798         this.fillTime();
21799         this.update();
21800             
21801         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21802         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21803         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21804         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21805         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21806         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21807
21808     },
21809     
21810     fireKey: function(e){
21811         if (!this.picker().isVisible()){
21812             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21813                 this.show();
21814             }
21815             return;
21816         }
21817
21818         e.preventDefault();
21819         
21820         switch(e.keyCode){
21821             case 27: // escape
21822                 this.hide();
21823                 break;
21824             case 37: // left
21825             case 39: // right
21826                 this.onTogglePeriod();
21827                 break;
21828             case 38: // up
21829                 this.onIncrementMinutes();
21830                 break;
21831             case 40: // down
21832                 this.onDecrementMinutes();
21833                 break;
21834             case 13: // enter
21835             case 9: // tab
21836                 this.setTime();
21837                 break;
21838         }
21839     },
21840     
21841     onClick: function(e) {
21842         e.stopPropagation();
21843         e.preventDefault();
21844     },
21845     
21846     picker : function()
21847     {
21848         return this.el.select('.datepicker', true).first();
21849     },
21850     
21851     fillTime: function()
21852     {    
21853         var time = this.pop.select('tbody', true).first();
21854         
21855         time.dom.innerHTML = '';
21856         
21857         time.createChild({
21858             tag: 'tr',
21859             cn: [
21860                 {
21861                     tag: 'td',
21862                     cn: [
21863                         {
21864                             tag: 'a',
21865                             href: '#',
21866                             cls: 'btn',
21867                             cn: [
21868                                 {
21869                                     tag: 'span',
21870                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21871                                 }
21872                             ]
21873                         } 
21874                     ]
21875                 },
21876                 {
21877                     tag: 'td',
21878                     cls: 'separator'
21879                 },
21880                 {
21881                     tag: 'td',
21882                     cn: [
21883                         {
21884                             tag: 'a',
21885                             href: '#',
21886                             cls: 'btn',
21887                             cn: [
21888                                 {
21889                                     tag: 'span',
21890                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21891                                 }
21892                             ]
21893                         }
21894                     ]
21895                 },
21896                 {
21897                     tag: 'td',
21898                     cls: 'separator'
21899                 }
21900             ]
21901         });
21902         
21903         time.createChild({
21904             tag: 'tr',
21905             cn: [
21906                 {
21907                     tag: 'td',
21908                     cn: [
21909                         {
21910                             tag: 'span',
21911                             cls: 'timepicker-hour',
21912                             html: '00'
21913                         }  
21914                     ]
21915                 },
21916                 {
21917                     tag: 'td',
21918                     cls: 'separator',
21919                     html: ':'
21920                 },
21921                 {
21922                     tag: 'td',
21923                     cn: [
21924                         {
21925                             tag: 'span',
21926                             cls: 'timepicker-minute',
21927                             html: '00'
21928                         }  
21929                     ]
21930                 },
21931                 {
21932                     tag: 'td',
21933                     cls: 'separator'
21934                 },
21935                 {
21936                     tag: 'td',
21937                     cn: [
21938                         {
21939                             tag: 'button',
21940                             type: 'button',
21941                             cls: 'btn btn-primary period',
21942                             html: 'AM'
21943                             
21944                         }
21945                     ]
21946                 }
21947             ]
21948         });
21949         
21950         time.createChild({
21951             tag: 'tr',
21952             cn: [
21953                 {
21954                     tag: 'td',
21955                     cn: [
21956                         {
21957                             tag: 'a',
21958                             href: '#',
21959                             cls: 'btn',
21960                             cn: [
21961                                 {
21962                                     tag: 'span',
21963                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21964                                 }
21965                             ]
21966                         }
21967                     ]
21968                 },
21969                 {
21970                     tag: 'td',
21971                     cls: 'separator'
21972                 },
21973                 {
21974                     tag: 'td',
21975                     cn: [
21976                         {
21977                             tag: 'a',
21978                             href: '#',
21979                             cls: 'btn',
21980                             cn: [
21981                                 {
21982                                     tag: 'span',
21983                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21984                                 }
21985                             ]
21986                         }
21987                     ]
21988                 },
21989                 {
21990                     tag: 'td',
21991                     cls: 'separator'
21992                 }
21993             ]
21994         });
21995         
21996     },
21997     
21998     update: function()
21999     {
22000         
22001         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22002         
22003         this.fill();
22004     },
22005     
22006     fill: function() 
22007     {
22008         var hours = this.time.getHours();
22009         var minutes = this.time.getMinutes();
22010         var period = 'AM';
22011         
22012         if(hours > 11){
22013             period = 'PM';
22014         }
22015         
22016         if(hours == 0){
22017             hours = 12;
22018         }
22019         
22020         
22021         if(hours > 12){
22022             hours = hours - 12;
22023         }
22024         
22025         if(hours < 10){
22026             hours = '0' + hours;
22027         }
22028         
22029         if(minutes < 10){
22030             minutes = '0' + minutes;
22031         }
22032         
22033         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22034         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22035         this.pop.select('button', true).first().dom.innerHTML = period;
22036         
22037     },
22038     
22039     place: function()
22040     {   
22041         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22042         
22043         var cls = ['bottom'];
22044         
22045         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22046             cls.pop();
22047             cls.push('top');
22048         }
22049         
22050         cls.push('right');
22051         
22052         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22053             cls.pop();
22054             cls.push('left');
22055         }
22056         
22057         this.picker().addClass(cls.join('-'));
22058         
22059         var _this = this;
22060         
22061         Roo.each(cls, function(c){
22062             if(c == 'bottom'){
22063                 _this.picker().setTop(_this.inputEl().getHeight());
22064                 return;
22065             }
22066             if(c == 'top'){
22067                 _this.picker().setTop(0 - _this.picker().getHeight());
22068                 return;
22069             }
22070             
22071             if(c == 'left'){
22072                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22073                 return;
22074             }
22075             if(c == 'right'){
22076                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22077                 return;
22078             }
22079         });
22080         
22081     },
22082   
22083     onFocus : function()
22084     {
22085         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22086         this.show();
22087     },
22088     
22089     onBlur : function()
22090     {
22091         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22092         this.hide();
22093     },
22094     
22095     show : function()
22096     {
22097         this.picker().show();
22098         this.pop.show();
22099         this.update();
22100         this.place();
22101         
22102         this.fireEvent('show', this, this.date);
22103     },
22104     
22105     hide : function()
22106     {
22107         this.picker().hide();
22108         this.pop.hide();
22109         
22110         this.fireEvent('hide', this, this.date);
22111     },
22112     
22113     setTime : function()
22114     {
22115         this.hide();
22116         this.setValue(this.time.format(this.format));
22117         
22118         this.fireEvent('select', this, this.date);
22119         
22120         
22121     },
22122     
22123     onMousedown: function(e){
22124         e.stopPropagation();
22125         e.preventDefault();
22126     },
22127     
22128     onIncrementHours: function()
22129     {
22130         Roo.log('onIncrementHours');
22131         this.time = this.time.add(Date.HOUR, 1);
22132         this.update();
22133         
22134     },
22135     
22136     onDecrementHours: function()
22137     {
22138         Roo.log('onDecrementHours');
22139         this.time = this.time.add(Date.HOUR, -1);
22140         this.update();
22141     },
22142     
22143     onIncrementMinutes: function()
22144     {
22145         Roo.log('onIncrementMinutes');
22146         this.time = this.time.add(Date.MINUTE, 1);
22147         this.update();
22148     },
22149     
22150     onDecrementMinutes: function()
22151     {
22152         Roo.log('onDecrementMinutes');
22153         this.time = this.time.add(Date.MINUTE, -1);
22154         this.update();
22155     },
22156     
22157     onTogglePeriod: function()
22158     {
22159         Roo.log('onTogglePeriod');
22160         this.time = this.time.add(Date.HOUR, 12);
22161         this.update();
22162     }
22163     
22164    
22165 });
22166
22167 Roo.apply(Roo.bootstrap.TimeField,  {
22168     
22169     content : {
22170         tag: 'tbody',
22171         cn: [
22172             {
22173                 tag: 'tr',
22174                 cn: [
22175                 {
22176                     tag: 'td',
22177                     colspan: '7'
22178                 }
22179                 ]
22180             }
22181         ]
22182     },
22183     
22184     footer : {
22185         tag: 'tfoot',
22186         cn: [
22187             {
22188                 tag: 'tr',
22189                 cn: [
22190                 {
22191                     tag: 'th',
22192                     colspan: '7',
22193                     cls: '',
22194                     cn: [
22195                         {
22196                             tag: 'button',
22197                             cls: 'btn btn-info ok',
22198                             html: 'OK'
22199                         }
22200                     ]
22201                 }
22202
22203                 ]
22204             }
22205         ]
22206     }
22207 });
22208
22209 Roo.apply(Roo.bootstrap.TimeField,  {
22210   
22211     template : {
22212         tag: 'div',
22213         cls: 'datepicker dropdown-menu',
22214         cn: [
22215             {
22216                 tag: 'div',
22217                 cls: 'datepicker-time',
22218                 cn: [
22219                 {
22220                     tag: 'table',
22221                     cls: 'table-condensed',
22222                     cn:[
22223                     Roo.bootstrap.TimeField.content,
22224                     Roo.bootstrap.TimeField.footer
22225                     ]
22226                 }
22227                 ]
22228             }
22229         ]
22230     }
22231 });
22232
22233  
22234
22235  /*
22236  * - LGPL
22237  *
22238  * MonthField
22239  * 
22240  */
22241
22242 /**
22243  * @class Roo.bootstrap.MonthField
22244  * @extends Roo.bootstrap.Input
22245  * Bootstrap MonthField class
22246  * 
22247  * @cfg {String} language default en
22248  * 
22249  * @constructor
22250  * Create a new MonthField
22251  * @param {Object} config The config object
22252  */
22253
22254 Roo.bootstrap.MonthField = function(config){
22255     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22256     
22257     this.addEvents({
22258         /**
22259          * @event show
22260          * Fires when this field show.
22261          * @param {Roo.bootstrap.MonthField} this
22262          * @param {Mixed} date The date value
22263          */
22264         show : true,
22265         /**
22266          * @event show
22267          * Fires when this field hide.
22268          * @param {Roo.bootstrap.MonthField} this
22269          * @param {Mixed} date The date value
22270          */
22271         hide : true,
22272         /**
22273          * @event select
22274          * Fires when select a date.
22275          * @param {Roo.bootstrap.MonthField} this
22276          * @param {String} oldvalue The old value
22277          * @param {String} newvalue The new value
22278          */
22279         select : true
22280     });
22281 };
22282
22283 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22284     
22285     onRender: function(ct, position)
22286     {
22287         
22288         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22289         
22290         this.language = this.language || 'en';
22291         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22292         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22293         
22294         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22295         this.isInline = false;
22296         this.isInput = true;
22297         this.component = this.el.select('.add-on', true).first() || false;
22298         this.component = (this.component && this.component.length === 0) ? false : this.component;
22299         this.hasInput = this.component && this.inputEL().length;
22300         
22301         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22302         
22303         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22304         
22305         this.picker().on('mousedown', this.onMousedown, this);
22306         this.picker().on('click', this.onClick, this);
22307         
22308         this.picker().addClass('datepicker-dropdown');
22309         
22310         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22311             v.setStyle('width', '189px');
22312         });
22313         
22314         this.fillMonths();
22315         
22316         this.update();
22317         
22318         if(this.isInline) {
22319             this.show();
22320         }
22321         
22322     },
22323     
22324     setValue: function(v, suppressEvent)
22325     {   
22326         var o = this.getValue();
22327         
22328         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22329         
22330         this.update();
22331
22332         if(suppressEvent !== true){
22333             this.fireEvent('select', this, o, v);
22334         }
22335         
22336     },
22337     
22338     getValue: function()
22339     {
22340         return this.value;
22341     },
22342     
22343     onClick: function(e) 
22344     {
22345         e.stopPropagation();
22346         e.preventDefault();
22347         
22348         var target = e.getTarget();
22349         
22350         if(target.nodeName.toLowerCase() === 'i'){
22351             target = Roo.get(target).dom.parentNode;
22352         }
22353         
22354         var nodeName = target.nodeName;
22355         var className = target.className;
22356         var html = target.innerHTML;
22357         
22358         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22359             return;
22360         }
22361         
22362         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22363         
22364         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22365         
22366         this.hide();
22367                         
22368     },
22369     
22370     picker : function()
22371     {
22372         return this.pickerEl;
22373     },
22374     
22375     fillMonths: function()
22376     {    
22377         var i = 0;
22378         var months = this.picker().select('>.datepicker-months td', true).first();
22379         
22380         months.dom.innerHTML = '';
22381         
22382         while (i < 12) {
22383             var month = {
22384                 tag: 'span',
22385                 cls: 'month',
22386                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22387             };
22388             
22389             months.createChild(month);
22390         }
22391         
22392     },
22393     
22394     update: function()
22395     {
22396         var _this = this;
22397         
22398         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22399             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22400         }
22401         
22402         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22403             e.removeClass('active');
22404             
22405             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22406                 e.addClass('active');
22407             }
22408         })
22409     },
22410     
22411     place: function()
22412     {
22413         if(this.isInline) {
22414             return;
22415         }
22416         
22417         this.picker().removeClass(['bottom', 'top']);
22418         
22419         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22420             /*
22421              * place to the top of element!
22422              *
22423              */
22424             
22425             this.picker().addClass('top');
22426             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22427             
22428             return;
22429         }
22430         
22431         this.picker().addClass('bottom');
22432         
22433         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22434     },
22435     
22436     onFocus : function()
22437     {
22438         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22439         this.show();
22440     },
22441     
22442     onBlur : function()
22443     {
22444         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22445         
22446         var d = this.inputEl().getValue();
22447         
22448         this.setValue(d);
22449                 
22450         this.hide();
22451     },
22452     
22453     show : function()
22454     {
22455         this.picker().show();
22456         this.picker().select('>.datepicker-months', true).first().show();
22457         this.update();
22458         this.place();
22459         
22460         this.fireEvent('show', this, this.date);
22461     },
22462     
22463     hide : function()
22464     {
22465         if(this.isInline) {
22466             return;
22467         }
22468         this.picker().hide();
22469         this.fireEvent('hide', this, this.date);
22470         
22471     },
22472     
22473     onMousedown: function(e)
22474     {
22475         e.stopPropagation();
22476         e.preventDefault();
22477     },
22478     
22479     keyup: function(e)
22480     {
22481         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22482         this.update();
22483     },
22484
22485     fireKey: function(e)
22486     {
22487         if (!this.picker().isVisible()){
22488             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22489                 this.show();
22490             }
22491             return;
22492         }
22493         
22494         var dir;
22495         
22496         switch(e.keyCode){
22497             case 27: // escape
22498                 this.hide();
22499                 e.preventDefault();
22500                 break;
22501             case 37: // left
22502             case 39: // right
22503                 dir = e.keyCode == 37 ? -1 : 1;
22504                 
22505                 this.vIndex = this.vIndex + dir;
22506                 
22507                 if(this.vIndex < 0){
22508                     this.vIndex = 0;
22509                 }
22510                 
22511                 if(this.vIndex > 11){
22512                     this.vIndex = 11;
22513                 }
22514                 
22515                 if(isNaN(this.vIndex)){
22516                     this.vIndex = 0;
22517                 }
22518                 
22519                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22520                 
22521                 break;
22522             case 38: // up
22523             case 40: // down
22524                 
22525                 dir = e.keyCode == 38 ? -1 : 1;
22526                 
22527                 this.vIndex = this.vIndex + dir * 4;
22528                 
22529                 if(this.vIndex < 0){
22530                     this.vIndex = 0;
22531                 }
22532                 
22533                 if(this.vIndex > 11){
22534                     this.vIndex = 11;
22535                 }
22536                 
22537                 if(isNaN(this.vIndex)){
22538                     this.vIndex = 0;
22539                 }
22540                 
22541                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22542                 break;
22543                 
22544             case 13: // enter
22545                 
22546                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22547                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22548                 }
22549                 
22550                 this.hide();
22551                 e.preventDefault();
22552                 break;
22553             case 9: // tab
22554                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22555                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22556                 }
22557                 this.hide();
22558                 break;
22559             case 16: // shift
22560             case 17: // ctrl
22561             case 18: // alt
22562                 break;
22563             default :
22564                 this.hide();
22565                 
22566         }
22567     },
22568     
22569     remove: function() 
22570     {
22571         this.picker().remove();
22572     }
22573    
22574 });
22575
22576 Roo.apply(Roo.bootstrap.MonthField,  {
22577     
22578     content : {
22579         tag: 'tbody',
22580         cn: [
22581         {
22582             tag: 'tr',
22583             cn: [
22584             {
22585                 tag: 'td',
22586                 colspan: '7'
22587             }
22588             ]
22589         }
22590         ]
22591     },
22592     
22593     dates:{
22594         en: {
22595             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22596             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22597         }
22598     }
22599 });
22600
22601 Roo.apply(Roo.bootstrap.MonthField,  {
22602   
22603     template : {
22604         tag: 'div',
22605         cls: 'datepicker dropdown-menu roo-dynamic',
22606         cn: [
22607             {
22608                 tag: 'div',
22609                 cls: 'datepicker-months',
22610                 cn: [
22611                 {
22612                     tag: 'table',
22613                     cls: 'table-condensed',
22614                     cn:[
22615                         Roo.bootstrap.DateField.content
22616                     ]
22617                 }
22618                 ]
22619             }
22620         ]
22621     }
22622 });
22623
22624  
22625
22626  
22627  /*
22628  * - LGPL
22629  *
22630  * CheckBox
22631  * 
22632  */
22633
22634 /**
22635  * @class Roo.bootstrap.CheckBox
22636  * @extends Roo.bootstrap.Input
22637  * Bootstrap CheckBox class
22638  * 
22639  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22640  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22641  * @cfg {String} boxLabel The text that appears beside the checkbox
22642  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22643  * @cfg {Boolean} checked initnal the element
22644  * @cfg {Boolean} inline inline the element (default false)
22645  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22646  * @cfg {String} tooltip label tooltip
22647  * 
22648  * @constructor
22649  * Create a new CheckBox
22650  * @param {Object} config The config object
22651  */
22652
22653 Roo.bootstrap.CheckBox = function(config){
22654     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22655    
22656     this.addEvents({
22657         /**
22658         * @event check
22659         * Fires when the element is checked or unchecked.
22660         * @param {Roo.bootstrap.CheckBox} this This input
22661         * @param {Boolean} checked The new checked value
22662         */
22663        check : true,
22664        /**
22665         * @event click
22666         * Fires when the element is click.
22667         * @param {Roo.bootstrap.CheckBox} this This input
22668         */
22669        click : true
22670     });
22671     
22672 };
22673
22674 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22675   
22676     inputType: 'checkbox',
22677     inputValue: 1,
22678     valueOff: 0,
22679     boxLabel: false,
22680     checked: false,
22681     weight : false,
22682     inline: false,
22683     tooltip : '',
22684     
22685     // checkbox success does not make any sense really.. 
22686     invalidClass : "",
22687     validClass : "",
22688     
22689     
22690     getAutoCreate : function()
22691     {
22692         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22693         
22694         var id = Roo.id();
22695         
22696         var cfg = {};
22697         
22698         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22699         
22700         if(this.inline){
22701             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22702         }
22703         
22704         var input =  {
22705             tag: 'input',
22706             id : id,
22707             type : this.inputType,
22708             value : this.inputValue,
22709             cls : 'roo-' + this.inputType, //'form-box',
22710             placeholder : this.placeholder || ''
22711             
22712         };
22713         
22714         if(this.inputType != 'radio'){
22715             var hidden =  {
22716                 tag: 'input',
22717                 type : 'hidden',
22718                 cls : 'roo-hidden-value',
22719                 value : this.checked ? this.inputValue : this.valueOff
22720             };
22721         }
22722         
22723             
22724         if (this.weight) { // Validity check?
22725             cfg.cls += " " + this.inputType + "-" + this.weight;
22726         }
22727         
22728         if (this.disabled) {
22729             input.disabled=true;
22730         }
22731         
22732         if(this.checked){
22733             input.checked = this.checked;
22734         }
22735         
22736         if (this.name) {
22737             
22738             input.name = this.name;
22739             
22740             if(this.inputType != 'radio'){
22741                 hidden.name = this.name;
22742                 input.name = '_hidden_' + this.name;
22743             }
22744         }
22745         
22746         if (this.size) {
22747             input.cls += ' input-' + this.size;
22748         }
22749         
22750         var settings=this;
22751         
22752         ['xs','sm','md','lg'].map(function(size){
22753             if (settings[size]) {
22754                 cfg.cls += ' col-' + size + '-' + settings[size];
22755             }
22756         });
22757         
22758         var inputblock = input;
22759          
22760         if (this.before || this.after) {
22761             
22762             inputblock = {
22763                 cls : 'input-group',
22764                 cn :  [] 
22765             };
22766             
22767             if (this.before) {
22768                 inputblock.cn.push({
22769                     tag :'span',
22770                     cls : 'input-group-addon',
22771                     html : this.before
22772                 });
22773             }
22774             
22775             inputblock.cn.push(input);
22776             
22777             if(this.inputType != 'radio'){
22778                 inputblock.cn.push(hidden);
22779             }
22780             
22781             if (this.after) {
22782                 inputblock.cn.push({
22783                     tag :'span',
22784                     cls : 'input-group-addon',
22785                     html : this.after
22786                 });
22787             }
22788             
22789         }
22790         var boxLabelCfg = false;
22791         
22792         if(this.boxLabel){
22793            
22794             boxLabelCfg = {
22795                 tag: 'label',
22796                 //'for': id, // box label is handled by onclick - so no for...
22797                 cls: 'box-label',
22798                 html: this.boxLabel
22799             };
22800             if(this.tooltip){
22801                 boxLabelCfg.tooltip = this.tooltip;
22802             }
22803              
22804         }
22805         
22806         
22807         if (align ==='left' && this.fieldLabel.length) {
22808 //                Roo.log("left and has label");
22809             cfg.cn = [
22810                 {
22811                     tag: 'label',
22812                     'for' :  id,
22813                     cls : 'control-label',
22814                     html : this.fieldLabel
22815                 },
22816                 {
22817                     cls : "", 
22818                     cn: [
22819                         inputblock
22820                     ]
22821                 }
22822             ];
22823             
22824             if (boxLabelCfg) {
22825                 cfg.cn[1].cn.push(boxLabelCfg);
22826             }
22827             
22828             if(this.labelWidth > 12){
22829                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22830             }
22831             
22832             if(this.labelWidth < 13 && this.labelmd == 0){
22833                 this.labelmd = this.labelWidth;
22834             }
22835             
22836             if(this.labellg > 0){
22837                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22838                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22839             }
22840             
22841             if(this.labelmd > 0){
22842                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22843                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22844             }
22845             
22846             if(this.labelsm > 0){
22847                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22848                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22849             }
22850             
22851             if(this.labelxs > 0){
22852                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22853                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22854             }
22855             
22856         } else if ( this.fieldLabel.length) {
22857 //                Roo.log(" label");
22858                 cfg.cn = [
22859                    
22860                     {
22861                         tag: this.boxLabel ? 'span' : 'label',
22862                         'for': id,
22863                         cls: 'control-label box-input-label',
22864                         //cls : 'input-group-addon',
22865                         html : this.fieldLabel
22866                     },
22867                     
22868                     inputblock
22869                     
22870                 ];
22871                 if (boxLabelCfg) {
22872                     cfg.cn.push(boxLabelCfg);
22873                 }
22874
22875         } else {
22876             
22877 //                Roo.log(" no label && no align");
22878                 cfg.cn = [  inputblock ] ;
22879                 if (boxLabelCfg) {
22880                     cfg.cn.push(boxLabelCfg);
22881                 }
22882
22883                 
22884         }
22885         
22886        
22887         
22888         if(this.inputType != 'radio'){
22889             cfg.cn.push(hidden);
22890         }
22891         
22892         return cfg;
22893         
22894     },
22895     
22896     /**
22897      * return the real input element.
22898      */
22899     inputEl: function ()
22900     {
22901         return this.el.select('input.roo-' + this.inputType,true).first();
22902     },
22903     hiddenEl: function ()
22904     {
22905         return this.el.select('input.roo-hidden-value',true).first();
22906     },
22907     
22908     labelEl: function()
22909     {
22910         return this.el.select('label.control-label',true).first();
22911     },
22912     /* depricated... */
22913     
22914     label: function()
22915     {
22916         return this.labelEl();
22917     },
22918     
22919     boxLabelEl: function()
22920     {
22921         return this.el.select('label.box-label',true).first();
22922     },
22923     
22924     initEvents : function()
22925     {
22926 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22927         
22928         this.inputEl().on('click', this.onClick,  this);
22929         
22930         if (this.boxLabel) { 
22931             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22932         }
22933         
22934         this.startValue = this.getValue();
22935         
22936         if(this.groupId){
22937             Roo.bootstrap.CheckBox.register(this);
22938         }
22939     },
22940     
22941     onClick : function(e)
22942     {   
22943         if(this.fireEvent('click', this, e) !== false){
22944             this.setChecked(!this.checked);
22945         }
22946         
22947     },
22948     
22949     setChecked : function(state,suppressEvent)
22950     {
22951         this.startValue = this.getValue();
22952
22953         if(this.inputType == 'radio'){
22954             
22955             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22956                 e.dom.checked = false;
22957             });
22958             
22959             this.inputEl().dom.checked = true;
22960             
22961             this.inputEl().dom.value = this.inputValue;
22962             
22963             if(suppressEvent !== true){
22964                 this.fireEvent('check', this, true);
22965             }
22966             
22967             this.validate();
22968             
22969             return;
22970         }
22971         
22972         this.checked = state;
22973         
22974         this.inputEl().dom.checked = state;
22975         
22976         
22977         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22978         
22979         if(suppressEvent !== true){
22980             this.fireEvent('check', this, state);
22981         }
22982         
22983         this.validate();
22984     },
22985     
22986     getValue : function()
22987     {
22988         if(this.inputType == 'radio'){
22989             return this.getGroupValue();
22990         }
22991         
22992         return this.hiddenEl().dom.value;
22993         
22994     },
22995     
22996     getGroupValue : function()
22997     {
22998         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22999             return '';
23000         }
23001         
23002         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23003     },
23004     
23005     setValue : function(v,suppressEvent)
23006     {
23007         if(this.inputType == 'radio'){
23008             this.setGroupValue(v, suppressEvent);
23009             return;
23010         }
23011         
23012         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23013         
23014         this.validate();
23015     },
23016     
23017     setGroupValue : function(v, suppressEvent)
23018     {
23019         this.startValue = this.getValue();
23020         
23021         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23022             e.dom.checked = false;
23023             
23024             if(e.dom.value == v){
23025                 e.dom.checked = true;
23026             }
23027         });
23028         
23029         if(suppressEvent !== true){
23030             this.fireEvent('check', this, true);
23031         }
23032
23033         this.validate();
23034         
23035         return;
23036     },
23037     
23038     validate : function()
23039     {
23040         if(this.getVisibilityEl().hasClass('hidden')){
23041             return true;
23042         }
23043         
23044         if(
23045                 this.disabled || 
23046                 (this.inputType == 'radio' && this.validateRadio()) ||
23047                 (this.inputType == 'checkbox' && this.validateCheckbox())
23048         ){
23049             this.markValid();
23050             return true;
23051         }
23052         
23053         this.markInvalid();
23054         return false;
23055     },
23056     
23057     validateRadio : function()
23058     {
23059         if(this.getVisibilityEl().hasClass('hidden')){
23060             return true;
23061         }
23062         
23063         if(this.allowBlank){
23064             return true;
23065         }
23066         
23067         var valid = false;
23068         
23069         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23070             if(!e.dom.checked){
23071                 return;
23072             }
23073             
23074             valid = true;
23075             
23076             return false;
23077         });
23078         
23079         return valid;
23080     },
23081     
23082     validateCheckbox : function()
23083     {
23084         if(!this.groupId){
23085             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23086             //return (this.getValue() == this.inputValue) ? true : false;
23087         }
23088         
23089         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23090         
23091         if(!group){
23092             return false;
23093         }
23094         
23095         var r = false;
23096         
23097         for(var i in group){
23098             if(group[i].el.isVisible(true)){
23099                 r = false;
23100                 break;
23101             }
23102             
23103             r = true;
23104         }
23105         
23106         for(var i in group){
23107             if(r){
23108                 break;
23109             }
23110             
23111             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23112         }
23113         
23114         return r;
23115     },
23116     
23117     /**
23118      * Mark this field as valid
23119      */
23120     markValid : function()
23121     {
23122         var _this = this;
23123         
23124         this.fireEvent('valid', this);
23125         
23126         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23127         
23128         if(this.groupId){
23129             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23130         }
23131         
23132         if(label){
23133             label.markValid();
23134         }
23135
23136         if(this.inputType == 'radio'){
23137             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23138                 var fg = e.findParent('.form-group', false, true);
23139                 if (Roo.bootstrap.version == 3) {
23140                     fg.removeClass([_this.invalidClass, _this.validClass]);
23141                     fg.addClass(_this.validClass);
23142                 } else {
23143                     fg.removeClass(['is-valid', 'is-invalid']);
23144                     fg.addClass('is-valid');
23145                 }
23146             });
23147             
23148             return;
23149         }
23150
23151         if(!this.groupId){
23152             var fg = this.el.findParent('.form-group', false, true);
23153             if (Roo.bootstrap.version == 3) {
23154                 fg.removeClass([this.invalidClass, this.validClass]);
23155                 fg.addClass(this.validClass);
23156             } else {
23157                 fg.removeClass(['is-valid', 'is-invalid']);
23158                 fg.addClass('is-valid');
23159             }
23160             return;
23161         }
23162         
23163         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23164         
23165         if(!group){
23166             return;
23167         }
23168         
23169         for(var i in group){
23170             var fg = group[i].el.findParent('.form-group', false, true);
23171             if (Roo.bootstrap.version == 3) {
23172                 fg.removeClass([this.invalidClass, this.validClass]);
23173                 fg.addClass(this.validClass);
23174             } else {
23175                 fg.removeClass(['is-valid', 'is-invalid']);
23176                 fg.addClass('is-valid');
23177             }
23178         }
23179     },
23180     
23181      /**
23182      * Mark this field as invalid
23183      * @param {String} msg The validation message
23184      */
23185     markInvalid : function(msg)
23186     {
23187         if(this.allowBlank){
23188             return;
23189         }
23190         
23191         var _this = this;
23192         
23193         this.fireEvent('invalid', this, msg);
23194         
23195         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23196         
23197         if(this.groupId){
23198             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23199         }
23200         
23201         if(label){
23202             label.markInvalid();
23203         }
23204             
23205         if(this.inputType == 'radio'){
23206             
23207             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23208                 var fg = e.findParent('.form-group', false, true);
23209                 if (Roo.bootstrap.version == 3) {
23210                     fg.removeClass([_this.invalidClass, _this.validClass]);
23211                     fg.addClass(_this.invalidClass);
23212                 } else {
23213                     fg.removeClass(['is-invalid', 'is-valid']);
23214                     fg.addClass('is-invalid');
23215                 }
23216             });
23217             
23218             return;
23219         }
23220         
23221         if(!this.groupId){
23222             var fg = this.el.findParent('.form-group', false, true);
23223             if (Roo.bootstrap.version == 3) {
23224                 fg.removeClass([_this.invalidClass, _this.validClass]);
23225                 fg.addClass(_this.invalidClass);
23226             } else {
23227                 fg.removeClass(['is-invalid', 'is-valid']);
23228                 fg.addClass('is-invalid');
23229             }
23230             return;
23231         }
23232         
23233         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23234         
23235         if(!group){
23236             return;
23237         }
23238         
23239         for(var i in group){
23240             var fg = group[i].el.findParent('.form-group', false, true);
23241             if (Roo.bootstrap.version == 3) {
23242                 fg.removeClass([_this.invalidClass, _this.validClass]);
23243                 fg.addClass(_this.invalidClass);
23244             } else {
23245                 fg.removeClass(['is-invalid', 'is-valid']);
23246                 fg.addClass('is-invalid');
23247             }
23248         }
23249         
23250     },
23251     
23252     clearInvalid : function()
23253     {
23254         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23255         
23256         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23257         
23258         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23259         
23260         if (label && label.iconEl) {
23261             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23262             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23263         }
23264     },
23265     
23266     disable : function()
23267     {
23268         if(this.inputType != 'radio'){
23269             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23270             return;
23271         }
23272         
23273         var _this = this;
23274         
23275         if(this.rendered){
23276             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23277                 _this.getActionEl().addClass(this.disabledClass);
23278                 e.dom.disabled = true;
23279             });
23280         }
23281         
23282         this.disabled = true;
23283         this.fireEvent("disable", this);
23284         return this;
23285     },
23286
23287     enable : function()
23288     {
23289         if(this.inputType != 'radio'){
23290             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23291             return;
23292         }
23293         
23294         var _this = this;
23295         
23296         if(this.rendered){
23297             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23298                 _this.getActionEl().removeClass(this.disabledClass);
23299                 e.dom.disabled = false;
23300             });
23301         }
23302         
23303         this.disabled = false;
23304         this.fireEvent("enable", this);
23305         return this;
23306     },
23307     
23308     setBoxLabel : function(v)
23309     {
23310         this.boxLabel = v;
23311         
23312         if(this.rendered){
23313             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23314         }
23315     }
23316
23317 });
23318
23319 Roo.apply(Roo.bootstrap.CheckBox, {
23320     
23321     groups: {},
23322     
23323      /**
23324     * register a CheckBox Group
23325     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23326     */
23327     register : function(checkbox)
23328     {
23329         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23330             this.groups[checkbox.groupId] = {};
23331         }
23332         
23333         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23334             return;
23335         }
23336         
23337         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23338         
23339     },
23340     /**
23341     * fetch a CheckBox Group based on the group ID
23342     * @param {string} the group ID
23343     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23344     */
23345     get: function(groupId) {
23346         if (typeof(this.groups[groupId]) == 'undefined') {
23347             return false;
23348         }
23349         
23350         return this.groups[groupId] ;
23351     }
23352     
23353     
23354 });
23355 /*
23356  * - LGPL
23357  *
23358  * RadioItem
23359  * 
23360  */
23361
23362 /**
23363  * @class Roo.bootstrap.Radio
23364  * @extends Roo.bootstrap.Component
23365  * Bootstrap Radio class
23366  * @cfg {String} boxLabel - the label associated
23367  * @cfg {String} value - the value of radio
23368  * 
23369  * @constructor
23370  * Create a new Radio
23371  * @param {Object} config The config object
23372  */
23373 Roo.bootstrap.Radio = function(config){
23374     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23375     
23376 };
23377
23378 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23379     
23380     boxLabel : '',
23381     
23382     value : '',
23383     
23384     getAutoCreate : function()
23385     {
23386         var cfg = {
23387             tag : 'div',
23388             cls : 'form-group radio',
23389             cn : [
23390                 {
23391                     tag : 'label',
23392                     cls : 'box-label',
23393                     html : this.boxLabel
23394                 }
23395             ]
23396         };
23397         
23398         return cfg;
23399     },
23400     
23401     initEvents : function() 
23402     {
23403         this.parent().register(this);
23404         
23405         this.el.on('click', this.onClick, this);
23406         
23407     },
23408     
23409     onClick : function(e)
23410     {
23411         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23412             this.setChecked(true);
23413         }
23414     },
23415     
23416     setChecked : function(state, suppressEvent)
23417     {
23418         this.parent().setValue(this.value, suppressEvent);
23419         
23420     },
23421     
23422     setBoxLabel : function(v)
23423     {
23424         this.boxLabel = v;
23425         
23426         if(this.rendered){
23427             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23428         }
23429     }
23430     
23431 });
23432  
23433
23434  /*
23435  * - LGPL
23436  *
23437  * Input
23438  * 
23439  */
23440
23441 /**
23442  * @class Roo.bootstrap.SecurePass
23443  * @extends Roo.bootstrap.Input
23444  * Bootstrap SecurePass class
23445  *
23446  * 
23447  * @constructor
23448  * Create a new SecurePass
23449  * @param {Object} config The config object
23450  */
23451  
23452 Roo.bootstrap.SecurePass = function (config) {
23453     // these go here, so the translation tool can replace them..
23454     this.errors = {
23455         PwdEmpty: "Please type a password, and then retype it to confirm.",
23456         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23457         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23458         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23459         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23460         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23461         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23462         TooWeak: "Your password is Too Weak."
23463     },
23464     this.meterLabel = "Password strength:";
23465     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23466     this.meterClass = [
23467         "roo-password-meter-tooweak", 
23468         "roo-password-meter-weak", 
23469         "roo-password-meter-medium", 
23470         "roo-password-meter-strong", 
23471         "roo-password-meter-grey"
23472     ];
23473     
23474     this.errors = {};
23475     
23476     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23477 }
23478
23479 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23480     /**
23481      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23482      * {
23483      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23484      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23485      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23486      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23487      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23488      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23489      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23490      * })
23491      */
23492     // private
23493     
23494     meterWidth: 300,
23495     errorMsg :'',    
23496     errors: false,
23497     imageRoot: '/',
23498     /**
23499      * @cfg {String/Object} Label for the strength meter (defaults to
23500      * 'Password strength:')
23501      */
23502     // private
23503     meterLabel: '',
23504     /**
23505      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23506      * ['Weak', 'Medium', 'Strong'])
23507      */
23508     // private    
23509     pwdStrengths: false,    
23510     // private
23511     strength: 0,
23512     // private
23513     _lastPwd: null,
23514     // private
23515     kCapitalLetter: 0,
23516     kSmallLetter: 1,
23517     kDigit: 2,
23518     kPunctuation: 3,
23519     
23520     insecure: false,
23521     // private
23522     initEvents: function ()
23523     {
23524         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23525
23526         if (this.el.is('input[type=password]') && Roo.isSafari) {
23527             this.el.on('keydown', this.SafariOnKeyDown, this);
23528         }
23529
23530         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23531     },
23532     // private
23533     onRender: function (ct, position)
23534     {
23535         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23536         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23537         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23538
23539         this.trigger.createChild({
23540                    cn: [
23541                     {
23542                     //id: 'PwdMeter',
23543                     tag: 'div',
23544                     cls: 'roo-password-meter-grey col-xs-12',
23545                     style: {
23546                         //width: 0,
23547                         //width: this.meterWidth + 'px'                                                
23548                         }
23549                     },
23550                     {                            
23551                          cls: 'roo-password-meter-text'                          
23552                     }
23553                 ]            
23554         });
23555
23556          
23557         if (this.hideTrigger) {
23558             this.trigger.setDisplayed(false);
23559         }
23560         this.setSize(this.width || '', this.height || '');
23561     },
23562     // private
23563     onDestroy: function ()
23564     {
23565         if (this.trigger) {
23566             this.trigger.removeAllListeners();
23567             this.trigger.remove();
23568         }
23569         if (this.wrap) {
23570             this.wrap.remove();
23571         }
23572         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23573     },
23574     // private
23575     checkStrength: function ()
23576     {
23577         var pwd = this.inputEl().getValue();
23578         if (pwd == this._lastPwd) {
23579             return;
23580         }
23581
23582         var strength;
23583         if (this.ClientSideStrongPassword(pwd)) {
23584             strength = 3;
23585         } else if (this.ClientSideMediumPassword(pwd)) {
23586             strength = 2;
23587         } else if (this.ClientSideWeakPassword(pwd)) {
23588             strength = 1;
23589         } else {
23590             strength = 0;
23591         }
23592         
23593         Roo.log('strength1: ' + strength);
23594         
23595         //var pm = this.trigger.child('div/div/div').dom;
23596         var pm = this.trigger.child('div/div');
23597         pm.removeClass(this.meterClass);
23598         pm.addClass(this.meterClass[strength]);
23599                 
23600         
23601         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23602                 
23603         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23604         
23605         this._lastPwd = pwd;
23606     },
23607     reset: function ()
23608     {
23609         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23610         
23611         this._lastPwd = '';
23612         
23613         var pm = this.trigger.child('div/div');
23614         pm.removeClass(this.meterClass);
23615         pm.addClass('roo-password-meter-grey');        
23616         
23617         
23618         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23619         
23620         pt.innerHTML = '';
23621         this.inputEl().dom.type='password';
23622     },
23623     // private
23624     validateValue: function (value)
23625     {
23626         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23627             return false;
23628         }
23629         if (value.length == 0) {
23630             if (this.allowBlank) {
23631                 this.clearInvalid();
23632                 return true;
23633             }
23634
23635             this.markInvalid(this.errors.PwdEmpty);
23636             this.errorMsg = this.errors.PwdEmpty;
23637             return false;
23638         }
23639         
23640         if(this.insecure){
23641             return true;
23642         }
23643         
23644         if (!value.match(/[\x21-\x7e]+/)) {
23645             this.markInvalid(this.errors.PwdBadChar);
23646             this.errorMsg = this.errors.PwdBadChar;
23647             return false;
23648         }
23649         if (value.length < 6) {
23650             this.markInvalid(this.errors.PwdShort);
23651             this.errorMsg = this.errors.PwdShort;
23652             return false;
23653         }
23654         if (value.length > 16) {
23655             this.markInvalid(this.errors.PwdLong);
23656             this.errorMsg = this.errors.PwdLong;
23657             return false;
23658         }
23659         var strength;
23660         if (this.ClientSideStrongPassword(value)) {
23661             strength = 3;
23662         } else if (this.ClientSideMediumPassword(value)) {
23663             strength = 2;
23664         } else if (this.ClientSideWeakPassword(value)) {
23665             strength = 1;
23666         } else {
23667             strength = 0;
23668         }
23669
23670         
23671         if (strength < 2) {
23672             //this.markInvalid(this.errors.TooWeak);
23673             this.errorMsg = this.errors.TooWeak;
23674             //return false;
23675         }
23676         
23677         
23678         console.log('strength2: ' + strength);
23679         
23680         //var pm = this.trigger.child('div/div/div').dom;
23681         
23682         var pm = this.trigger.child('div/div');
23683         pm.removeClass(this.meterClass);
23684         pm.addClass(this.meterClass[strength]);
23685                 
23686         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23687                 
23688         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23689         
23690         this.errorMsg = ''; 
23691         return true;
23692     },
23693     // private
23694     CharacterSetChecks: function (type)
23695     {
23696         this.type = type;
23697         this.fResult = false;
23698     },
23699     // private
23700     isctype: function (character, type)
23701     {
23702         switch (type) {  
23703             case this.kCapitalLetter:
23704                 if (character >= 'A' && character <= 'Z') {
23705                     return true;
23706                 }
23707                 break;
23708             
23709             case this.kSmallLetter:
23710                 if (character >= 'a' && character <= 'z') {
23711                     return true;
23712                 }
23713                 break;
23714             
23715             case this.kDigit:
23716                 if (character >= '0' && character <= '9') {
23717                     return true;
23718                 }
23719                 break;
23720             
23721             case this.kPunctuation:
23722                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23723                     return true;
23724                 }
23725                 break;
23726             
23727             default:
23728                 return false;
23729         }
23730
23731     },
23732     // private
23733     IsLongEnough: function (pwd, size)
23734     {
23735         return !(pwd == null || isNaN(size) || pwd.length < size);
23736     },
23737     // private
23738     SpansEnoughCharacterSets: function (word, nb)
23739     {
23740         if (!this.IsLongEnough(word, nb))
23741         {
23742             return false;
23743         }
23744
23745         var characterSetChecks = new Array(
23746             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23747             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23748         );
23749         
23750         for (var index = 0; index < word.length; ++index) {
23751             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23752                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23753                     characterSetChecks[nCharSet].fResult = true;
23754                     break;
23755                 }
23756             }
23757         }
23758
23759         var nCharSets = 0;
23760         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23761             if (characterSetChecks[nCharSet].fResult) {
23762                 ++nCharSets;
23763             }
23764         }
23765
23766         if (nCharSets < nb) {
23767             return false;
23768         }
23769         return true;
23770     },
23771     // private
23772     ClientSideStrongPassword: function (pwd)
23773     {
23774         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23775     },
23776     // private
23777     ClientSideMediumPassword: function (pwd)
23778     {
23779         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23780     },
23781     // private
23782     ClientSideWeakPassword: function (pwd)
23783     {
23784         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23785     }
23786           
23787 })//<script type="text/javascript">
23788
23789 /*
23790  * Based  Ext JS Library 1.1.1
23791  * Copyright(c) 2006-2007, Ext JS, LLC.
23792  * LGPL
23793  *
23794  */
23795  
23796 /**
23797  * @class Roo.HtmlEditorCore
23798  * @extends Roo.Component
23799  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23800  *
23801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23802  */
23803
23804 Roo.HtmlEditorCore = function(config){
23805     
23806     
23807     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23808     
23809     
23810     this.addEvents({
23811         /**
23812          * @event initialize
23813          * Fires when the editor is fully initialized (including the iframe)
23814          * @param {Roo.HtmlEditorCore} this
23815          */
23816         initialize: true,
23817         /**
23818          * @event activate
23819          * Fires when the editor is first receives the focus. Any insertion must wait
23820          * until after this event.
23821          * @param {Roo.HtmlEditorCore} this
23822          */
23823         activate: true,
23824          /**
23825          * @event beforesync
23826          * Fires before the textarea is updated with content from the editor iframe. Return false
23827          * to cancel the sync.
23828          * @param {Roo.HtmlEditorCore} this
23829          * @param {String} html
23830          */
23831         beforesync: true,
23832          /**
23833          * @event beforepush
23834          * Fires before the iframe editor is updated with content from the textarea. Return false
23835          * to cancel the push.
23836          * @param {Roo.HtmlEditorCore} this
23837          * @param {String} html
23838          */
23839         beforepush: true,
23840          /**
23841          * @event sync
23842          * Fires when the textarea is updated with content from the editor iframe.
23843          * @param {Roo.HtmlEditorCore} this
23844          * @param {String} html
23845          */
23846         sync: true,
23847          /**
23848          * @event push
23849          * Fires when the iframe editor is updated with content from the textarea.
23850          * @param {Roo.HtmlEditorCore} this
23851          * @param {String} html
23852          */
23853         push: true,
23854         
23855         /**
23856          * @event editorevent
23857          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23858          * @param {Roo.HtmlEditorCore} this
23859          */
23860         editorevent: true
23861         
23862     });
23863     
23864     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23865     
23866     // defaults : white / black...
23867     this.applyBlacklists();
23868     
23869     
23870     
23871 };
23872
23873
23874 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23875
23876
23877      /**
23878      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23879      */
23880     
23881     owner : false,
23882     
23883      /**
23884      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23885      *                        Roo.resizable.
23886      */
23887     resizable : false,
23888      /**
23889      * @cfg {Number} height (in pixels)
23890      */   
23891     height: 300,
23892    /**
23893      * @cfg {Number} width (in pixels)
23894      */   
23895     width: 500,
23896     
23897     /**
23898      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23899      * 
23900      */
23901     stylesheets: false,
23902     
23903     // id of frame..
23904     frameId: false,
23905     
23906     // private properties
23907     validationEvent : false,
23908     deferHeight: true,
23909     initialized : false,
23910     activated : false,
23911     sourceEditMode : false,
23912     onFocus : Roo.emptyFn,
23913     iframePad:3,
23914     hideMode:'offsets',
23915     
23916     clearUp: true,
23917     
23918     // blacklist + whitelisted elements..
23919     black: false,
23920     white: false,
23921      
23922     bodyCls : '',
23923
23924     /**
23925      * Protected method that will not generally be called directly. It
23926      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23927      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23928      */
23929     getDocMarkup : function(){
23930         // body styles..
23931         var st = '';
23932         
23933         // inherit styels from page...?? 
23934         if (this.stylesheets === false) {
23935             
23936             Roo.get(document.head).select('style').each(function(node) {
23937                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23938             });
23939             
23940             Roo.get(document.head).select('link').each(function(node) { 
23941                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23942             });
23943             
23944         } else if (!this.stylesheets.length) {
23945                 // simple..
23946                 st = '<style type="text/css">' +
23947                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23948                    '</style>';
23949         } else {
23950             for (var i in this.stylesheets) { 
23951                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23952             }
23953             
23954         }
23955         
23956         st +=  '<style type="text/css">' +
23957             'IMG { cursor: pointer } ' +
23958         '</style>';
23959
23960         var cls = 'roo-htmleditor-body';
23961         
23962         if(this.bodyCls.length){
23963             cls += ' ' + this.bodyCls;
23964         }
23965         
23966         return '<html><head>' + st  +
23967             //<style type="text/css">' +
23968             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23969             //'</style>' +
23970             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23971     },
23972
23973     // private
23974     onRender : function(ct, position)
23975     {
23976         var _t = this;
23977         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23978         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23979         
23980         
23981         this.el.dom.style.border = '0 none';
23982         this.el.dom.setAttribute('tabIndex', -1);
23983         this.el.addClass('x-hidden hide');
23984         
23985         
23986         
23987         if(Roo.isIE){ // fix IE 1px bogus margin
23988             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23989         }
23990        
23991         
23992         this.frameId = Roo.id();
23993         
23994          
23995         
23996         var iframe = this.owner.wrap.createChild({
23997             tag: 'iframe',
23998             cls: 'form-control', // bootstrap..
23999             id: this.frameId,
24000             name: this.frameId,
24001             frameBorder : 'no',
24002             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24003         }, this.el
24004         );
24005         
24006         
24007         this.iframe = iframe.dom;
24008
24009          this.assignDocWin();
24010         
24011         this.doc.designMode = 'on';
24012        
24013         this.doc.open();
24014         this.doc.write(this.getDocMarkup());
24015         this.doc.close();
24016
24017         
24018         var task = { // must defer to wait for browser to be ready
24019             run : function(){
24020                 //console.log("run task?" + this.doc.readyState);
24021                 this.assignDocWin();
24022                 if(this.doc.body || this.doc.readyState == 'complete'){
24023                     try {
24024                         this.doc.designMode="on";
24025                     } catch (e) {
24026                         return;
24027                     }
24028                     Roo.TaskMgr.stop(task);
24029                     this.initEditor.defer(10, this);
24030                 }
24031             },
24032             interval : 10,
24033             duration: 10000,
24034             scope: this
24035         };
24036         Roo.TaskMgr.start(task);
24037
24038     },
24039
24040     // private
24041     onResize : function(w, h)
24042     {
24043          Roo.log('resize: ' +w + ',' + h );
24044         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24045         if(!this.iframe){
24046             return;
24047         }
24048         if(typeof w == 'number'){
24049             
24050             this.iframe.style.width = w + 'px';
24051         }
24052         if(typeof h == 'number'){
24053             
24054             this.iframe.style.height = h + 'px';
24055             if(this.doc){
24056                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24057             }
24058         }
24059         
24060     },
24061
24062     /**
24063      * Toggles the editor between standard and source edit mode.
24064      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24065      */
24066     toggleSourceEdit : function(sourceEditMode){
24067         
24068         this.sourceEditMode = sourceEditMode === true;
24069         
24070         if(this.sourceEditMode){
24071  
24072             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24073             
24074         }else{
24075             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24076             //this.iframe.className = '';
24077             this.deferFocus();
24078         }
24079         //this.setSize(this.owner.wrap.getSize());
24080         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24081     },
24082
24083     
24084   
24085
24086     /**
24087      * Protected method that will not generally be called directly. If you need/want
24088      * custom HTML cleanup, this is the method you should override.
24089      * @param {String} html The HTML to be cleaned
24090      * return {String} The cleaned HTML
24091      */
24092     cleanHtml : function(html){
24093         html = String(html);
24094         if(html.length > 5){
24095             if(Roo.isSafari){ // strip safari nonsense
24096                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24097             }
24098         }
24099         if(html == '&nbsp;'){
24100             html = '';
24101         }
24102         return html;
24103     },
24104
24105     /**
24106      * HTML Editor -> Textarea
24107      * Protected method that will not generally be called directly. Syncs the contents
24108      * of the editor iframe with the textarea.
24109      */
24110     syncValue : function(){
24111         if(this.initialized){
24112             var bd = (this.doc.body || this.doc.documentElement);
24113             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24114             var html = bd.innerHTML;
24115             if(Roo.isSafari){
24116                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24117                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24118                 if(m && m[1]){
24119                     html = '<div style="'+m[0]+'">' + html + '</div>';
24120                 }
24121             }
24122             html = this.cleanHtml(html);
24123             // fix up the special chars.. normaly like back quotes in word...
24124             // however we do not want to do this with chinese..
24125             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24126                 
24127                 var cc = match.charCodeAt();
24128
24129                 // Get the character value, handling surrogate pairs
24130                 if (match.length == 2) {
24131                     // It's a surrogate pair, calculate the Unicode code point
24132                     var high = match.charCodeAt(0) - 0xD800;
24133                     var low  = match.charCodeAt(1) - 0xDC00;
24134                     cc = (high * 0x400) + low + 0x10000;
24135                 }  else if (
24136                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24137                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24138                     (cc >= 0xf900 && cc < 0xfb00 )
24139                 ) {
24140                         return match;
24141                 }  
24142          
24143                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24144                 return "&#" + cc + ";";
24145                 
24146                 
24147             });
24148             
24149             
24150              
24151             if(this.owner.fireEvent('beforesync', this, html) !== false){
24152                 this.el.dom.value = html;
24153                 this.owner.fireEvent('sync', this, html);
24154             }
24155         }
24156     },
24157
24158     /**
24159      * Protected method that will not generally be called directly. Pushes the value of the textarea
24160      * into the iframe editor.
24161      */
24162     pushValue : function(){
24163         if(this.initialized){
24164             var v = this.el.dom.value.trim();
24165             
24166 //            if(v.length < 1){
24167 //                v = '&#160;';
24168 //            }
24169             
24170             if(this.owner.fireEvent('beforepush', this, v) !== false){
24171                 var d = (this.doc.body || this.doc.documentElement);
24172                 d.innerHTML = v;
24173                 this.cleanUpPaste();
24174                 this.el.dom.value = d.innerHTML;
24175                 this.owner.fireEvent('push', this, v);
24176             }
24177         }
24178     },
24179
24180     // private
24181     deferFocus : function(){
24182         this.focus.defer(10, this);
24183     },
24184
24185     // doc'ed in Field
24186     focus : function(){
24187         if(this.win && !this.sourceEditMode){
24188             this.win.focus();
24189         }else{
24190             this.el.focus();
24191         }
24192     },
24193     
24194     assignDocWin: function()
24195     {
24196         var iframe = this.iframe;
24197         
24198          if(Roo.isIE){
24199             this.doc = iframe.contentWindow.document;
24200             this.win = iframe.contentWindow;
24201         } else {
24202 //            if (!Roo.get(this.frameId)) {
24203 //                return;
24204 //            }
24205 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24206 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24207             
24208             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24209                 return;
24210             }
24211             
24212             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24213             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24214         }
24215     },
24216     
24217     // private
24218     initEditor : function(){
24219         //console.log("INIT EDITOR");
24220         this.assignDocWin();
24221         
24222         
24223         
24224         this.doc.designMode="on";
24225         this.doc.open();
24226         this.doc.write(this.getDocMarkup());
24227         this.doc.close();
24228         
24229         var dbody = (this.doc.body || this.doc.documentElement);
24230         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24231         // this copies styles from the containing element into thsi one..
24232         // not sure why we need all of this..
24233         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24234         
24235         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24236         //ss['background-attachment'] = 'fixed'; // w3c
24237         dbody.bgProperties = 'fixed'; // ie
24238         //Roo.DomHelper.applyStyles(dbody, ss);
24239         Roo.EventManager.on(this.doc, {
24240             //'mousedown': this.onEditorEvent,
24241             'mouseup': this.onEditorEvent,
24242             'dblclick': this.onEditorEvent,
24243             'click': this.onEditorEvent,
24244             'keyup': this.onEditorEvent,
24245             buffer:100,
24246             scope: this
24247         });
24248         if(Roo.isGecko){
24249             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24250         }
24251         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24252             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24253         }
24254         this.initialized = true;
24255
24256         this.owner.fireEvent('initialize', this);
24257         this.pushValue();
24258     },
24259
24260     // private
24261     onDestroy : function(){
24262         
24263         
24264         
24265         if(this.rendered){
24266             
24267             //for (var i =0; i < this.toolbars.length;i++) {
24268             //    // fixme - ask toolbars for heights?
24269             //    this.toolbars[i].onDestroy();
24270            // }
24271             
24272             //this.wrap.dom.innerHTML = '';
24273             //this.wrap.remove();
24274         }
24275     },
24276
24277     // private
24278     onFirstFocus : function(){
24279         
24280         this.assignDocWin();
24281         
24282         
24283         this.activated = true;
24284          
24285     
24286         if(Roo.isGecko){ // prevent silly gecko errors
24287             this.win.focus();
24288             var s = this.win.getSelection();
24289             if(!s.focusNode || s.focusNode.nodeType != 3){
24290                 var r = s.getRangeAt(0);
24291                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24292                 r.collapse(true);
24293                 this.deferFocus();
24294             }
24295             try{
24296                 this.execCmd('useCSS', true);
24297                 this.execCmd('styleWithCSS', false);
24298             }catch(e){}
24299         }
24300         this.owner.fireEvent('activate', this);
24301     },
24302
24303     // private
24304     adjustFont: function(btn){
24305         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24306         //if(Roo.isSafari){ // safari
24307         //    adjust *= 2;
24308        // }
24309         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24310         if(Roo.isSafari){ // safari
24311             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24312             v =  (v < 10) ? 10 : v;
24313             v =  (v > 48) ? 48 : v;
24314             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24315             
24316         }
24317         
24318         
24319         v = Math.max(1, v+adjust);
24320         
24321         this.execCmd('FontSize', v  );
24322     },
24323
24324     onEditorEvent : function(e)
24325     {
24326         this.owner.fireEvent('editorevent', this, e);
24327       //  this.updateToolbar();
24328         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24329     },
24330
24331     insertTag : function(tg)
24332     {
24333         // could be a bit smarter... -> wrap the current selected tRoo..
24334         if (tg.toLowerCase() == 'span' ||
24335             tg.toLowerCase() == 'code' ||
24336             tg.toLowerCase() == 'sup' ||
24337             tg.toLowerCase() == 'sub' 
24338             ) {
24339             
24340             range = this.createRange(this.getSelection());
24341             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24342             wrappingNode.appendChild(range.extractContents());
24343             range.insertNode(wrappingNode);
24344
24345             return;
24346             
24347             
24348             
24349         }
24350         this.execCmd("formatblock",   tg);
24351         
24352     },
24353     
24354     insertText : function(txt)
24355     {
24356         
24357         
24358         var range = this.createRange();
24359         range.deleteContents();
24360                //alert(Sender.getAttribute('label'));
24361                
24362         range.insertNode(this.doc.createTextNode(txt));
24363     } ,
24364     
24365      
24366
24367     /**
24368      * Executes a Midas editor command on the editor document and performs necessary focus and
24369      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24370      * @param {String} cmd The Midas command
24371      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24372      */
24373     relayCmd : function(cmd, value){
24374         this.win.focus();
24375         this.execCmd(cmd, value);
24376         this.owner.fireEvent('editorevent', this);
24377         //this.updateToolbar();
24378         this.owner.deferFocus();
24379     },
24380
24381     /**
24382      * Executes a Midas editor command directly on the editor document.
24383      * For visual commands, you should use {@link #relayCmd} instead.
24384      * <b>This should only be called after the editor is initialized.</b>
24385      * @param {String} cmd The Midas command
24386      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24387      */
24388     execCmd : function(cmd, value){
24389         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24390         this.syncValue();
24391     },
24392  
24393  
24394    
24395     /**
24396      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24397      * to insert tRoo.
24398      * @param {String} text | dom node.. 
24399      */
24400     insertAtCursor : function(text)
24401     {
24402         
24403         if(!this.activated){
24404             return;
24405         }
24406         /*
24407         if(Roo.isIE){
24408             this.win.focus();
24409             var r = this.doc.selection.createRange();
24410             if(r){
24411                 r.collapse(true);
24412                 r.pasteHTML(text);
24413                 this.syncValue();
24414                 this.deferFocus();
24415             
24416             }
24417             return;
24418         }
24419         */
24420         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24421             this.win.focus();
24422             
24423             
24424             // from jquery ui (MIT licenced)
24425             var range, node;
24426             var win = this.win;
24427             
24428             if (win.getSelection && win.getSelection().getRangeAt) {
24429                 range = win.getSelection().getRangeAt(0);
24430                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24431                 range.insertNode(node);
24432             } else if (win.document.selection && win.document.selection.createRange) {
24433                 // no firefox support
24434                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24435                 win.document.selection.createRange().pasteHTML(txt);
24436             } else {
24437                 // no firefox support
24438                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24439                 this.execCmd('InsertHTML', txt);
24440             } 
24441             
24442             this.syncValue();
24443             
24444             this.deferFocus();
24445         }
24446     },
24447  // private
24448     mozKeyPress : function(e){
24449         if(e.ctrlKey){
24450             var c = e.getCharCode(), cmd;
24451           
24452             if(c > 0){
24453                 c = String.fromCharCode(c).toLowerCase();
24454                 switch(c){
24455                     case 'b':
24456                         cmd = 'bold';
24457                         break;
24458                     case 'i':
24459                         cmd = 'italic';
24460                         break;
24461                     
24462                     case 'u':
24463                         cmd = 'underline';
24464                         break;
24465                     
24466                     case 'v':
24467                         this.cleanUpPaste.defer(100, this);
24468                         return;
24469                         
24470                 }
24471                 if(cmd){
24472                     this.win.focus();
24473                     this.execCmd(cmd);
24474                     this.deferFocus();
24475                     e.preventDefault();
24476                 }
24477                 
24478             }
24479         }
24480     },
24481
24482     // private
24483     fixKeys : function(){ // load time branching for fastest keydown performance
24484         if(Roo.isIE){
24485             return function(e){
24486                 var k = e.getKey(), r;
24487                 if(k == e.TAB){
24488                     e.stopEvent();
24489                     r = this.doc.selection.createRange();
24490                     if(r){
24491                         r.collapse(true);
24492                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24493                         this.deferFocus();
24494                     }
24495                     return;
24496                 }
24497                 
24498                 if(k == e.ENTER){
24499                     r = this.doc.selection.createRange();
24500                     if(r){
24501                         var target = r.parentElement();
24502                         if(!target || target.tagName.toLowerCase() != 'li'){
24503                             e.stopEvent();
24504                             r.pasteHTML('<br />');
24505                             r.collapse(false);
24506                             r.select();
24507                         }
24508                     }
24509                 }
24510                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24511                     this.cleanUpPaste.defer(100, this);
24512                     return;
24513                 }
24514                 
24515                 
24516             };
24517         }else if(Roo.isOpera){
24518             return function(e){
24519                 var k = e.getKey();
24520                 if(k == e.TAB){
24521                     e.stopEvent();
24522                     this.win.focus();
24523                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24524                     this.deferFocus();
24525                 }
24526                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24527                     this.cleanUpPaste.defer(100, this);
24528                     return;
24529                 }
24530                 
24531             };
24532         }else if(Roo.isSafari){
24533             return function(e){
24534                 var k = e.getKey();
24535                 
24536                 if(k == e.TAB){
24537                     e.stopEvent();
24538                     this.execCmd('InsertText','\t');
24539                     this.deferFocus();
24540                     return;
24541                 }
24542                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24543                     this.cleanUpPaste.defer(100, this);
24544                     return;
24545                 }
24546                 
24547              };
24548         }
24549     }(),
24550     
24551     getAllAncestors: function()
24552     {
24553         var p = this.getSelectedNode();
24554         var a = [];
24555         if (!p) {
24556             a.push(p); // push blank onto stack..
24557             p = this.getParentElement();
24558         }
24559         
24560         
24561         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24562             a.push(p);
24563             p = p.parentNode;
24564         }
24565         a.push(this.doc.body);
24566         return a;
24567     },
24568     lastSel : false,
24569     lastSelNode : false,
24570     
24571     
24572     getSelection : function() 
24573     {
24574         this.assignDocWin();
24575         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24576     },
24577     
24578     getSelectedNode: function() 
24579     {
24580         // this may only work on Gecko!!!
24581         
24582         // should we cache this!!!!
24583         
24584         
24585         
24586          
24587         var range = this.createRange(this.getSelection()).cloneRange();
24588         
24589         if (Roo.isIE) {
24590             var parent = range.parentElement();
24591             while (true) {
24592                 var testRange = range.duplicate();
24593                 testRange.moveToElementText(parent);
24594                 if (testRange.inRange(range)) {
24595                     break;
24596                 }
24597                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24598                     break;
24599                 }
24600                 parent = parent.parentElement;
24601             }
24602             return parent;
24603         }
24604         
24605         // is ancestor a text element.
24606         var ac =  range.commonAncestorContainer;
24607         if (ac.nodeType == 3) {
24608             ac = ac.parentNode;
24609         }
24610         
24611         var ar = ac.childNodes;
24612          
24613         var nodes = [];
24614         var other_nodes = [];
24615         var has_other_nodes = false;
24616         for (var i=0;i<ar.length;i++) {
24617             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24618                 continue;
24619             }
24620             // fullly contained node.
24621             
24622             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24623                 nodes.push(ar[i]);
24624                 continue;
24625             }
24626             
24627             // probably selected..
24628             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24629                 other_nodes.push(ar[i]);
24630                 continue;
24631             }
24632             // outer..
24633             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24634                 continue;
24635             }
24636             
24637             
24638             has_other_nodes = true;
24639         }
24640         if (!nodes.length && other_nodes.length) {
24641             nodes= other_nodes;
24642         }
24643         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24644             return false;
24645         }
24646         
24647         return nodes[0];
24648     },
24649     createRange: function(sel)
24650     {
24651         // this has strange effects when using with 
24652         // top toolbar - not sure if it's a great idea.
24653         //this.editor.contentWindow.focus();
24654         if (typeof sel != "undefined") {
24655             try {
24656                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24657             } catch(e) {
24658                 return this.doc.createRange();
24659             }
24660         } else {
24661             return this.doc.createRange();
24662         }
24663     },
24664     getParentElement: function()
24665     {
24666         
24667         this.assignDocWin();
24668         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24669         
24670         var range = this.createRange(sel);
24671          
24672         try {
24673             var p = range.commonAncestorContainer;
24674             while (p.nodeType == 3) { // text node
24675                 p = p.parentNode;
24676             }
24677             return p;
24678         } catch (e) {
24679             return null;
24680         }
24681     
24682     },
24683     /***
24684      *
24685      * Range intersection.. the hard stuff...
24686      *  '-1' = before
24687      *  '0' = hits..
24688      *  '1' = after.
24689      *         [ -- selected range --- ]
24690      *   [fail]                        [fail]
24691      *
24692      *    basically..
24693      *      if end is before start or  hits it. fail.
24694      *      if start is after end or hits it fail.
24695      *
24696      *   if either hits (but other is outside. - then it's not 
24697      *   
24698      *    
24699      **/
24700     
24701     
24702     // @see http://www.thismuchiknow.co.uk/?p=64.
24703     rangeIntersectsNode : function(range, node)
24704     {
24705         var nodeRange = node.ownerDocument.createRange();
24706         try {
24707             nodeRange.selectNode(node);
24708         } catch (e) {
24709             nodeRange.selectNodeContents(node);
24710         }
24711     
24712         var rangeStartRange = range.cloneRange();
24713         rangeStartRange.collapse(true);
24714     
24715         var rangeEndRange = range.cloneRange();
24716         rangeEndRange.collapse(false);
24717     
24718         var nodeStartRange = nodeRange.cloneRange();
24719         nodeStartRange.collapse(true);
24720     
24721         var nodeEndRange = nodeRange.cloneRange();
24722         nodeEndRange.collapse(false);
24723     
24724         return rangeStartRange.compareBoundaryPoints(
24725                  Range.START_TO_START, nodeEndRange) == -1 &&
24726                rangeEndRange.compareBoundaryPoints(
24727                  Range.START_TO_START, nodeStartRange) == 1;
24728         
24729          
24730     },
24731     rangeCompareNode : function(range, node)
24732     {
24733         var nodeRange = node.ownerDocument.createRange();
24734         try {
24735             nodeRange.selectNode(node);
24736         } catch (e) {
24737             nodeRange.selectNodeContents(node);
24738         }
24739         
24740         
24741         range.collapse(true);
24742     
24743         nodeRange.collapse(true);
24744      
24745         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24746         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24747          
24748         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24749         
24750         var nodeIsBefore   =  ss == 1;
24751         var nodeIsAfter    = ee == -1;
24752         
24753         if (nodeIsBefore && nodeIsAfter) {
24754             return 0; // outer
24755         }
24756         if (!nodeIsBefore && nodeIsAfter) {
24757             return 1; //right trailed.
24758         }
24759         
24760         if (nodeIsBefore && !nodeIsAfter) {
24761             return 2;  // left trailed.
24762         }
24763         // fully contined.
24764         return 3;
24765     },
24766
24767     // private? - in a new class?
24768     cleanUpPaste :  function()
24769     {
24770         // cleans up the whole document..
24771         Roo.log('cleanuppaste');
24772         
24773         this.cleanUpChildren(this.doc.body);
24774         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24775         if (clean != this.doc.body.innerHTML) {
24776             this.doc.body.innerHTML = clean;
24777         }
24778         
24779     },
24780     
24781     cleanWordChars : function(input) {// change the chars to hex code
24782         var he = Roo.HtmlEditorCore;
24783         
24784         var output = input;
24785         Roo.each(he.swapCodes, function(sw) { 
24786             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24787             
24788             output = output.replace(swapper, sw[1]);
24789         });
24790         
24791         return output;
24792     },
24793     
24794     
24795     cleanUpChildren : function (n)
24796     {
24797         if (!n.childNodes.length) {
24798             return;
24799         }
24800         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24801            this.cleanUpChild(n.childNodes[i]);
24802         }
24803     },
24804     
24805     
24806         
24807     
24808     cleanUpChild : function (node)
24809     {
24810         var ed = this;
24811         //console.log(node);
24812         if (node.nodeName == "#text") {
24813             // clean up silly Windows -- stuff?
24814             return; 
24815         }
24816         if (node.nodeName == "#comment") {
24817             node.parentNode.removeChild(node);
24818             // clean up silly Windows -- stuff?
24819             return; 
24820         }
24821         var lcname = node.tagName.toLowerCase();
24822         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24823         // whitelist of tags..
24824         
24825         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24826             // remove node.
24827             node.parentNode.removeChild(node);
24828             return;
24829             
24830         }
24831         
24832         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24833         
24834         // spans with no attributes - just remove them..
24835         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24836             remove_keep_children = true;
24837         }
24838         
24839         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24840         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24841         
24842         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24843         //    remove_keep_children = true;
24844         //}
24845         
24846         if (remove_keep_children) {
24847             this.cleanUpChildren(node);
24848             // inserts everything just before this node...
24849             while (node.childNodes.length) {
24850                 var cn = node.childNodes[0];
24851                 node.removeChild(cn);
24852                 node.parentNode.insertBefore(cn, node);
24853             }
24854             node.parentNode.removeChild(node);
24855             return;
24856         }
24857         
24858         if (!node.attributes || !node.attributes.length) {
24859             
24860           
24861             
24862             
24863             this.cleanUpChildren(node);
24864             return;
24865         }
24866         
24867         function cleanAttr(n,v)
24868         {
24869             
24870             if (v.match(/^\./) || v.match(/^\//)) {
24871                 return;
24872             }
24873             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24874                 return;
24875             }
24876             if (v.match(/^#/)) {
24877                 return;
24878             }
24879             if (v.match(/^\{/)) { // allow template editing.
24880                 return;
24881             }
24882 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24883             node.removeAttribute(n);
24884             
24885         }
24886         
24887         var cwhite = this.cwhite;
24888         var cblack = this.cblack;
24889             
24890         function cleanStyle(n,v)
24891         {
24892             if (v.match(/expression/)) { //XSS?? should we even bother..
24893                 node.removeAttribute(n);
24894                 return;
24895             }
24896             
24897             var parts = v.split(/;/);
24898             var clean = [];
24899             
24900             Roo.each(parts, function(p) {
24901                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24902                 if (!p.length) {
24903                     return true;
24904                 }
24905                 var l = p.split(':').shift().replace(/\s+/g,'');
24906                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24907                 
24908                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24909 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24910                     //node.removeAttribute(n);
24911                     return true;
24912                 }
24913                 //Roo.log()
24914                 // only allow 'c whitelisted system attributes'
24915                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24916 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24917                     //node.removeAttribute(n);
24918                     return true;
24919                 }
24920                 
24921                 
24922                  
24923                 
24924                 clean.push(p);
24925                 return true;
24926             });
24927             if (clean.length) { 
24928                 node.setAttribute(n, clean.join(';'));
24929             } else {
24930                 node.removeAttribute(n);
24931             }
24932             
24933         }
24934         
24935         
24936         for (var i = node.attributes.length-1; i > -1 ; i--) {
24937             var a = node.attributes[i];
24938             //console.log(a);
24939             
24940             if (a.name.toLowerCase().substr(0,2)=='on')  {
24941                 node.removeAttribute(a.name);
24942                 continue;
24943             }
24944             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24945                 node.removeAttribute(a.name);
24946                 continue;
24947             }
24948             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24949                 cleanAttr(a.name,a.value); // fixme..
24950                 continue;
24951             }
24952             if (a.name == 'style') {
24953                 cleanStyle(a.name,a.value);
24954                 continue;
24955             }
24956             /// clean up MS crap..
24957             // tecnically this should be a list of valid class'es..
24958             
24959             
24960             if (a.name == 'class') {
24961                 if (a.value.match(/^Mso/)) {
24962                     node.removeAttribute('class');
24963                 }
24964                 
24965                 if (a.value.match(/^body$/)) {
24966                     node.removeAttribute('class');
24967                 }
24968                 continue;
24969             }
24970             
24971             // style cleanup!?
24972             // class cleanup?
24973             
24974         }
24975         
24976         
24977         this.cleanUpChildren(node);
24978         
24979         
24980     },
24981     
24982     /**
24983      * Clean up MS wordisms...
24984      */
24985     cleanWord : function(node)
24986     {
24987         if (!node) {
24988             this.cleanWord(this.doc.body);
24989             return;
24990         }
24991         
24992         if(
24993                 node.nodeName == 'SPAN' &&
24994                 !node.hasAttributes() &&
24995                 node.childNodes.length == 1 &&
24996                 node.firstChild.nodeName == "#text"  
24997         ) {
24998             var textNode = node.firstChild;
24999             node.removeChild(textNode);
25000             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25001                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25002             }
25003             node.parentNode.insertBefore(textNode, node);
25004             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25005                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25006             }
25007             node.parentNode.removeChild(node);
25008         }
25009         
25010         if (node.nodeName == "#text") {
25011             // clean up silly Windows -- stuff?
25012             return; 
25013         }
25014         if (node.nodeName == "#comment") {
25015             node.parentNode.removeChild(node);
25016             // clean up silly Windows -- stuff?
25017             return; 
25018         }
25019         
25020         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25021             node.parentNode.removeChild(node);
25022             return;
25023         }
25024         //Roo.log(node.tagName);
25025         // remove - but keep children..
25026         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25027             //Roo.log('-- removed');
25028             while (node.childNodes.length) {
25029                 var cn = node.childNodes[0];
25030                 node.removeChild(cn);
25031                 node.parentNode.insertBefore(cn, node);
25032                 // move node to parent - and clean it..
25033                 this.cleanWord(cn);
25034             }
25035             node.parentNode.removeChild(node);
25036             /// no need to iterate chidlren = it's got none..
25037             //this.iterateChildren(node, this.cleanWord);
25038             return;
25039         }
25040         // clean styles
25041         if (node.className.length) {
25042             
25043             var cn = node.className.split(/\W+/);
25044             var cna = [];
25045             Roo.each(cn, function(cls) {
25046                 if (cls.match(/Mso[a-zA-Z]+/)) {
25047                     return;
25048                 }
25049                 cna.push(cls);
25050             });
25051             node.className = cna.length ? cna.join(' ') : '';
25052             if (!cna.length) {
25053                 node.removeAttribute("class");
25054             }
25055         }
25056         
25057         if (node.hasAttribute("lang")) {
25058             node.removeAttribute("lang");
25059         }
25060         
25061         if (node.hasAttribute("style")) {
25062             
25063             var styles = node.getAttribute("style").split(";");
25064             var nstyle = [];
25065             Roo.each(styles, function(s) {
25066                 if (!s.match(/:/)) {
25067                     return;
25068                 }
25069                 var kv = s.split(":");
25070                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25071                     return;
25072                 }
25073                 // what ever is left... we allow.
25074                 nstyle.push(s);
25075             });
25076             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25077             if (!nstyle.length) {
25078                 node.removeAttribute('style');
25079             }
25080         }
25081         this.iterateChildren(node, this.cleanWord);
25082         
25083         
25084         
25085     },
25086     /**
25087      * iterateChildren of a Node, calling fn each time, using this as the scole..
25088      * @param {DomNode} node node to iterate children of.
25089      * @param {Function} fn method of this class to call on each item.
25090      */
25091     iterateChildren : function(node, fn)
25092     {
25093         if (!node.childNodes.length) {
25094                 return;
25095         }
25096         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25097            fn.call(this, node.childNodes[i])
25098         }
25099     },
25100     
25101     
25102     /**
25103      * cleanTableWidths.
25104      *
25105      * Quite often pasting from word etc.. results in tables with column and widths.
25106      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25107      *
25108      */
25109     cleanTableWidths : function(node)
25110     {
25111          
25112          
25113         if (!node) {
25114             this.cleanTableWidths(this.doc.body);
25115             return;
25116         }
25117         
25118         // ignore list...
25119         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25120             return; 
25121         }
25122         Roo.log(node.tagName);
25123         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25124             this.iterateChildren(node, this.cleanTableWidths);
25125             return;
25126         }
25127         if (node.hasAttribute('width')) {
25128             node.removeAttribute('width');
25129         }
25130         
25131          
25132         if (node.hasAttribute("style")) {
25133             // pretty basic...
25134             
25135             var styles = node.getAttribute("style").split(";");
25136             var nstyle = [];
25137             Roo.each(styles, function(s) {
25138                 if (!s.match(/:/)) {
25139                     return;
25140                 }
25141                 var kv = s.split(":");
25142                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25143                     return;
25144                 }
25145                 // what ever is left... we allow.
25146                 nstyle.push(s);
25147             });
25148             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25149             if (!nstyle.length) {
25150                 node.removeAttribute('style');
25151             }
25152         }
25153         
25154         this.iterateChildren(node, this.cleanTableWidths);
25155         
25156         
25157     },
25158     
25159     
25160     
25161     
25162     domToHTML : function(currentElement, depth, nopadtext) {
25163         
25164         depth = depth || 0;
25165         nopadtext = nopadtext || false;
25166     
25167         if (!currentElement) {
25168             return this.domToHTML(this.doc.body);
25169         }
25170         
25171         //Roo.log(currentElement);
25172         var j;
25173         var allText = false;
25174         var nodeName = currentElement.nodeName;
25175         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25176         
25177         if  (nodeName == '#text') {
25178             
25179             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25180         }
25181         
25182         
25183         var ret = '';
25184         if (nodeName != 'BODY') {
25185              
25186             var i = 0;
25187             // Prints the node tagName, such as <A>, <IMG>, etc
25188             if (tagName) {
25189                 var attr = [];
25190                 for(i = 0; i < currentElement.attributes.length;i++) {
25191                     // quoting?
25192                     var aname = currentElement.attributes.item(i).name;
25193                     if (!currentElement.attributes.item(i).value.length) {
25194                         continue;
25195                     }
25196                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25197                 }
25198                 
25199                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25200             } 
25201             else {
25202                 
25203                 // eack
25204             }
25205         } else {
25206             tagName = false;
25207         }
25208         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25209             return ret;
25210         }
25211         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25212             nopadtext = true;
25213         }
25214         
25215         
25216         // Traverse the tree
25217         i = 0;
25218         var currentElementChild = currentElement.childNodes.item(i);
25219         var allText = true;
25220         var innerHTML  = '';
25221         lastnode = '';
25222         while (currentElementChild) {
25223             // Formatting code (indent the tree so it looks nice on the screen)
25224             var nopad = nopadtext;
25225             if (lastnode == 'SPAN') {
25226                 nopad  = true;
25227             }
25228             // text
25229             if  (currentElementChild.nodeName == '#text') {
25230                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25231                 toadd = nopadtext ? toadd : toadd.trim();
25232                 if (!nopad && toadd.length > 80) {
25233                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25234                 }
25235                 innerHTML  += toadd;
25236                 
25237                 i++;
25238                 currentElementChild = currentElement.childNodes.item(i);
25239                 lastNode = '';
25240                 continue;
25241             }
25242             allText = false;
25243             
25244             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25245                 
25246             // Recursively traverse the tree structure of the child node
25247             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25248             lastnode = currentElementChild.nodeName;
25249             i++;
25250             currentElementChild=currentElement.childNodes.item(i);
25251         }
25252         
25253         ret += innerHTML;
25254         
25255         if (!allText) {
25256                 // The remaining code is mostly for formatting the tree
25257             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25258         }
25259         
25260         
25261         if (tagName) {
25262             ret+= "</"+tagName+">";
25263         }
25264         return ret;
25265         
25266     },
25267         
25268     applyBlacklists : function()
25269     {
25270         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25271         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25272         
25273         this.white = [];
25274         this.black = [];
25275         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25276             if (b.indexOf(tag) > -1) {
25277                 return;
25278             }
25279             this.white.push(tag);
25280             
25281         }, this);
25282         
25283         Roo.each(w, function(tag) {
25284             if (b.indexOf(tag) > -1) {
25285                 return;
25286             }
25287             if (this.white.indexOf(tag) > -1) {
25288                 return;
25289             }
25290             this.white.push(tag);
25291             
25292         }, this);
25293         
25294         
25295         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25296             if (w.indexOf(tag) > -1) {
25297                 return;
25298             }
25299             this.black.push(tag);
25300             
25301         }, this);
25302         
25303         Roo.each(b, function(tag) {
25304             if (w.indexOf(tag) > -1) {
25305                 return;
25306             }
25307             if (this.black.indexOf(tag) > -1) {
25308                 return;
25309             }
25310             this.black.push(tag);
25311             
25312         }, this);
25313         
25314         
25315         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25316         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25317         
25318         this.cwhite = [];
25319         this.cblack = [];
25320         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25321             if (b.indexOf(tag) > -1) {
25322                 return;
25323             }
25324             this.cwhite.push(tag);
25325             
25326         }, this);
25327         
25328         Roo.each(w, function(tag) {
25329             if (b.indexOf(tag) > -1) {
25330                 return;
25331             }
25332             if (this.cwhite.indexOf(tag) > -1) {
25333                 return;
25334             }
25335             this.cwhite.push(tag);
25336             
25337         }, this);
25338         
25339         
25340         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25341             if (w.indexOf(tag) > -1) {
25342                 return;
25343             }
25344             this.cblack.push(tag);
25345             
25346         }, this);
25347         
25348         Roo.each(b, function(tag) {
25349             if (w.indexOf(tag) > -1) {
25350                 return;
25351             }
25352             if (this.cblack.indexOf(tag) > -1) {
25353                 return;
25354             }
25355             this.cblack.push(tag);
25356             
25357         }, this);
25358     },
25359     
25360     setStylesheets : function(stylesheets)
25361     {
25362         if(typeof(stylesheets) == 'string'){
25363             Roo.get(this.iframe.contentDocument.head).createChild({
25364                 tag : 'link',
25365                 rel : 'stylesheet',
25366                 type : 'text/css',
25367                 href : stylesheets
25368             });
25369             
25370             return;
25371         }
25372         var _this = this;
25373      
25374         Roo.each(stylesheets, function(s) {
25375             if(!s.length){
25376                 return;
25377             }
25378             
25379             Roo.get(_this.iframe.contentDocument.head).createChild({
25380                 tag : 'link',
25381                 rel : 'stylesheet',
25382                 type : 'text/css',
25383                 href : s
25384             });
25385         });
25386
25387         
25388     },
25389     
25390     removeStylesheets : function()
25391     {
25392         var _this = this;
25393         
25394         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25395             s.remove();
25396         });
25397     },
25398     
25399     setStyle : function(style)
25400     {
25401         Roo.get(this.iframe.contentDocument.head).createChild({
25402             tag : 'style',
25403             type : 'text/css',
25404             html : style
25405         });
25406
25407         return;
25408     }
25409     
25410     // hide stuff that is not compatible
25411     /**
25412      * @event blur
25413      * @hide
25414      */
25415     /**
25416      * @event change
25417      * @hide
25418      */
25419     /**
25420      * @event focus
25421      * @hide
25422      */
25423     /**
25424      * @event specialkey
25425      * @hide
25426      */
25427     /**
25428      * @cfg {String} fieldClass @hide
25429      */
25430     /**
25431      * @cfg {String} focusClass @hide
25432      */
25433     /**
25434      * @cfg {String} autoCreate @hide
25435      */
25436     /**
25437      * @cfg {String} inputType @hide
25438      */
25439     /**
25440      * @cfg {String} invalidClass @hide
25441      */
25442     /**
25443      * @cfg {String} invalidText @hide
25444      */
25445     /**
25446      * @cfg {String} msgFx @hide
25447      */
25448     /**
25449      * @cfg {String} validateOnBlur @hide
25450      */
25451 });
25452
25453 Roo.HtmlEditorCore.white = [
25454         'area', 'br', 'img', 'input', 'hr', 'wbr',
25455         
25456        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25457        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25458        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25459        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25460        'table',   'ul',         'xmp', 
25461        
25462        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25463       'thead',   'tr', 
25464      
25465       'dir', 'menu', 'ol', 'ul', 'dl',
25466        
25467       'embed',  'object'
25468 ];
25469
25470
25471 Roo.HtmlEditorCore.black = [
25472     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25473         'applet', // 
25474         'base',   'basefont', 'bgsound', 'blink',  'body', 
25475         'frame',  'frameset', 'head',    'html',   'ilayer', 
25476         'iframe', 'layer',  'link',     'meta',    'object',   
25477         'script', 'style' ,'title',  'xml' // clean later..
25478 ];
25479 Roo.HtmlEditorCore.clean = [
25480     'script', 'style', 'title', 'xml'
25481 ];
25482 Roo.HtmlEditorCore.remove = [
25483     'font'
25484 ];
25485 // attributes..
25486
25487 Roo.HtmlEditorCore.ablack = [
25488     'on'
25489 ];
25490     
25491 Roo.HtmlEditorCore.aclean = [ 
25492     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25493 ];
25494
25495 // protocols..
25496 Roo.HtmlEditorCore.pwhite= [
25497         'http',  'https',  'mailto'
25498 ];
25499
25500 // white listed style attributes.
25501 Roo.HtmlEditorCore.cwhite= [
25502       //  'text-align', /// default is to allow most things..
25503       
25504          
25505 //        'font-size'//??
25506 ];
25507
25508 // black listed style attributes.
25509 Roo.HtmlEditorCore.cblack= [
25510       //  'font-size' -- this can be set by the project 
25511 ];
25512
25513
25514 Roo.HtmlEditorCore.swapCodes   =[ 
25515     [    8211, "--" ], 
25516     [    8212, "--" ], 
25517     [    8216,  "'" ],  
25518     [    8217, "'" ],  
25519     [    8220, '"' ],  
25520     [    8221, '"' ],  
25521     [    8226, "*" ],  
25522     [    8230, "..." ]
25523 ]; 
25524
25525     /*
25526  * - LGPL
25527  *
25528  * HtmlEditor
25529  * 
25530  */
25531
25532 /**
25533  * @class Roo.bootstrap.HtmlEditor
25534  * @extends Roo.bootstrap.TextArea
25535  * Bootstrap HtmlEditor class
25536
25537  * @constructor
25538  * Create a new HtmlEditor
25539  * @param {Object} config The config object
25540  */
25541
25542 Roo.bootstrap.HtmlEditor = function(config){
25543     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25544     if (!this.toolbars) {
25545         this.toolbars = [];
25546     }
25547     
25548     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25549     this.addEvents({
25550             /**
25551              * @event initialize
25552              * Fires when the editor is fully initialized (including the iframe)
25553              * @param {HtmlEditor} this
25554              */
25555             initialize: true,
25556             /**
25557              * @event activate
25558              * Fires when the editor is first receives the focus. Any insertion must wait
25559              * until after this event.
25560              * @param {HtmlEditor} this
25561              */
25562             activate: true,
25563              /**
25564              * @event beforesync
25565              * Fires before the textarea is updated with content from the editor iframe. Return false
25566              * to cancel the sync.
25567              * @param {HtmlEditor} this
25568              * @param {String} html
25569              */
25570             beforesync: true,
25571              /**
25572              * @event beforepush
25573              * Fires before the iframe editor is updated with content from the textarea. Return false
25574              * to cancel the push.
25575              * @param {HtmlEditor} this
25576              * @param {String} html
25577              */
25578             beforepush: true,
25579              /**
25580              * @event sync
25581              * Fires when the textarea is updated with content from the editor iframe.
25582              * @param {HtmlEditor} this
25583              * @param {String} html
25584              */
25585             sync: true,
25586              /**
25587              * @event push
25588              * Fires when the iframe editor is updated with content from the textarea.
25589              * @param {HtmlEditor} this
25590              * @param {String} html
25591              */
25592             push: true,
25593              /**
25594              * @event editmodechange
25595              * Fires when the editor switches edit modes
25596              * @param {HtmlEditor} this
25597              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25598              */
25599             editmodechange: true,
25600             /**
25601              * @event editorevent
25602              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25603              * @param {HtmlEditor} this
25604              */
25605             editorevent: true,
25606             /**
25607              * @event firstfocus
25608              * Fires when on first focus - needed by toolbars..
25609              * @param {HtmlEditor} this
25610              */
25611             firstfocus: true,
25612             /**
25613              * @event autosave
25614              * Auto save the htmlEditor value as a file into Events
25615              * @param {HtmlEditor} this
25616              */
25617             autosave: true,
25618             /**
25619              * @event savedpreview
25620              * preview the saved version of htmlEditor
25621              * @param {HtmlEditor} this
25622              */
25623             savedpreview: true
25624         });
25625 };
25626
25627
25628 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25629     
25630     
25631       /**
25632      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25633      */
25634     toolbars : false,
25635     
25636      /**
25637     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25638     */
25639     btns : [],
25640    
25641      /**
25642      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25643      *                        Roo.resizable.
25644      */
25645     resizable : false,
25646      /**
25647      * @cfg {Number} height (in pixels)
25648      */   
25649     height: 300,
25650    /**
25651      * @cfg {Number} width (in pixels)
25652      */   
25653     width: false,
25654     
25655     /**
25656      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25657      * 
25658      */
25659     stylesheets: false,
25660     
25661     // id of frame..
25662     frameId: false,
25663     
25664     // private properties
25665     validationEvent : false,
25666     deferHeight: true,
25667     initialized : false,
25668     activated : false,
25669     
25670     onFocus : Roo.emptyFn,
25671     iframePad:3,
25672     hideMode:'offsets',
25673     
25674     tbContainer : false,
25675     
25676     bodyCls : '',
25677     
25678     toolbarContainer :function() {
25679         return this.wrap.select('.x-html-editor-tb',true).first();
25680     },
25681
25682     /**
25683      * Protected method that will not generally be called directly. It
25684      * is called when the editor creates its toolbar. Override this method if you need to
25685      * add custom toolbar buttons.
25686      * @param {HtmlEditor} editor
25687      */
25688     createToolbar : function(){
25689         Roo.log('renewing');
25690         Roo.log("create toolbars");
25691         
25692         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25693         this.toolbars[0].render(this.toolbarContainer());
25694         
25695         return;
25696         
25697 //        if (!editor.toolbars || !editor.toolbars.length) {
25698 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25699 //        }
25700 //        
25701 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25702 //            editor.toolbars[i] = Roo.factory(
25703 //                    typeof(editor.toolbars[i]) == 'string' ?
25704 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25705 //                Roo.bootstrap.HtmlEditor);
25706 //            editor.toolbars[i].init(editor);
25707 //        }
25708     },
25709
25710      
25711     // private
25712     onRender : function(ct, position)
25713     {
25714        // Roo.log("Call onRender: " + this.xtype);
25715         var _t = this;
25716         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25717       
25718         this.wrap = this.inputEl().wrap({
25719             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25720         });
25721         
25722         this.editorcore.onRender(ct, position);
25723          
25724         if (this.resizable) {
25725             this.resizeEl = new Roo.Resizable(this.wrap, {
25726                 pinned : true,
25727                 wrap: true,
25728                 dynamic : true,
25729                 minHeight : this.height,
25730                 height: this.height,
25731                 handles : this.resizable,
25732                 width: this.width,
25733                 listeners : {
25734                     resize : function(r, w, h) {
25735                         _t.onResize(w,h); // -something
25736                     }
25737                 }
25738             });
25739             
25740         }
25741         this.createToolbar(this);
25742        
25743         
25744         if(!this.width && this.resizable){
25745             this.setSize(this.wrap.getSize());
25746         }
25747         if (this.resizeEl) {
25748             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25749             // should trigger onReize..
25750         }
25751         
25752     },
25753
25754     // private
25755     onResize : function(w, h)
25756     {
25757         Roo.log('resize: ' +w + ',' + h );
25758         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25759         var ew = false;
25760         var eh = false;
25761         
25762         if(this.inputEl() ){
25763             if(typeof w == 'number'){
25764                 var aw = w - this.wrap.getFrameWidth('lr');
25765                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25766                 ew = aw;
25767             }
25768             if(typeof h == 'number'){
25769                  var tbh = -11;  // fixme it needs to tool bar size!
25770                 for (var i =0; i < this.toolbars.length;i++) {
25771                     // fixme - ask toolbars for heights?
25772                     tbh += this.toolbars[i].el.getHeight();
25773                     //if (this.toolbars[i].footer) {
25774                     //    tbh += this.toolbars[i].footer.el.getHeight();
25775                     //}
25776                 }
25777               
25778                 
25779                 
25780                 
25781                 
25782                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25783                 ah -= 5; // knock a few pixes off for look..
25784                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25785                 var eh = ah;
25786             }
25787         }
25788         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25789         this.editorcore.onResize(ew,eh);
25790         
25791     },
25792
25793     /**
25794      * Toggles the editor between standard and source edit mode.
25795      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25796      */
25797     toggleSourceEdit : function(sourceEditMode)
25798     {
25799         this.editorcore.toggleSourceEdit(sourceEditMode);
25800         
25801         if(this.editorcore.sourceEditMode){
25802             Roo.log('editor - showing textarea');
25803             
25804 //            Roo.log('in');
25805 //            Roo.log(this.syncValue());
25806             this.syncValue();
25807             this.inputEl().removeClass(['hide', 'x-hidden']);
25808             this.inputEl().dom.removeAttribute('tabIndex');
25809             this.inputEl().focus();
25810         }else{
25811             Roo.log('editor - hiding textarea');
25812 //            Roo.log('out')
25813 //            Roo.log(this.pushValue()); 
25814             this.pushValue();
25815             
25816             this.inputEl().addClass(['hide', 'x-hidden']);
25817             this.inputEl().dom.setAttribute('tabIndex', -1);
25818             //this.deferFocus();
25819         }
25820          
25821         if(this.resizable){
25822             this.setSize(this.wrap.getSize());
25823         }
25824         
25825         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25826     },
25827  
25828     // private (for BoxComponent)
25829     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25830
25831     // private (for BoxComponent)
25832     getResizeEl : function(){
25833         return this.wrap;
25834     },
25835
25836     // private (for BoxComponent)
25837     getPositionEl : function(){
25838         return this.wrap;
25839     },
25840
25841     // private
25842     initEvents : function(){
25843         this.originalValue = this.getValue();
25844     },
25845
25846 //    /**
25847 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25848 //     * @method
25849 //     */
25850 //    markInvalid : Roo.emptyFn,
25851 //    /**
25852 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25853 //     * @method
25854 //     */
25855 //    clearInvalid : Roo.emptyFn,
25856
25857     setValue : function(v){
25858         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25859         this.editorcore.pushValue();
25860     },
25861
25862      
25863     // private
25864     deferFocus : function(){
25865         this.focus.defer(10, this);
25866     },
25867
25868     // doc'ed in Field
25869     focus : function(){
25870         this.editorcore.focus();
25871         
25872     },
25873       
25874
25875     // private
25876     onDestroy : function(){
25877         
25878         
25879         
25880         if(this.rendered){
25881             
25882             for (var i =0; i < this.toolbars.length;i++) {
25883                 // fixme - ask toolbars for heights?
25884                 this.toolbars[i].onDestroy();
25885             }
25886             
25887             this.wrap.dom.innerHTML = '';
25888             this.wrap.remove();
25889         }
25890     },
25891
25892     // private
25893     onFirstFocus : function(){
25894         //Roo.log("onFirstFocus");
25895         this.editorcore.onFirstFocus();
25896          for (var i =0; i < this.toolbars.length;i++) {
25897             this.toolbars[i].onFirstFocus();
25898         }
25899         
25900     },
25901     
25902     // private
25903     syncValue : function()
25904     {   
25905         this.editorcore.syncValue();
25906     },
25907     
25908     pushValue : function()
25909     {   
25910         this.editorcore.pushValue();
25911     }
25912      
25913     
25914     // hide stuff that is not compatible
25915     /**
25916      * @event blur
25917      * @hide
25918      */
25919     /**
25920      * @event change
25921      * @hide
25922      */
25923     /**
25924      * @event focus
25925      * @hide
25926      */
25927     /**
25928      * @event specialkey
25929      * @hide
25930      */
25931     /**
25932      * @cfg {String} fieldClass @hide
25933      */
25934     /**
25935      * @cfg {String} focusClass @hide
25936      */
25937     /**
25938      * @cfg {String} autoCreate @hide
25939      */
25940     /**
25941      * @cfg {String} inputType @hide
25942      */
25943      
25944     /**
25945      * @cfg {String} invalidText @hide
25946      */
25947     /**
25948      * @cfg {String} msgFx @hide
25949      */
25950     /**
25951      * @cfg {String} validateOnBlur @hide
25952      */
25953 });
25954  
25955     
25956    
25957    
25958    
25959       
25960 Roo.namespace('Roo.bootstrap.htmleditor');
25961 /**
25962  * @class Roo.bootstrap.HtmlEditorToolbar1
25963  * Basic Toolbar
25964  * 
25965  * @example
25966  * Usage:
25967  *
25968  new Roo.bootstrap.HtmlEditor({
25969     ....
25970     toolbars : [
25971         new Roo.bootstrap.HtmlEditorToolbar1({
25972             disable : { fonts: 1 , format: 1, ..., ... , ...],
25973             btns : [ .... ]
25974         })
25975     }
25976      
25977  * 
25978  * @cfg {Object} disable List of elements to disable..
25979  * @cfg {Array} btns List of additional buttons.
25980  * 
25981  * 
25982  * NEEDS Extra CSS? 
25983  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25984  */
25985  
25986 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25987 {
25988     
25989     Roo.apply(this, config);
25990     
25991     // default disabled, based on 'good practice'..
25992     this.disable = this.disable || {};
25993     Roo.applyIf(this.disable, {
25994         fontSize : true,
25995         colors : true,
25996         specialElements : true
25997     });
25998     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25999     
26000     this.editor = config.editor;
26001     this.editorcore = config.editor.editorcore;
26002     
26003     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26004     
26005     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26006     // dont call parent... till later.
26007 }
26008 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26009      
26010     bar : true,
26011     
26012     editor : false,
26013     editorcore : false,
26014     
26015     
26016     formats : [
26017         "p" ,  
26018         "h1","h2","h3","h4","h5","h6", 
26019         "pre", "code", 
26020         "abbr", "acronym", "address", "cite", "samp", "var",
26021         'div','span'
26022     ],
26023     
26024     onRender : function(ct, position)
26025     {
26026        // Roo.log("Call onRender: " + this.xtype);
26027         
26028        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26029        Roo.log(this.el);
26030        this.el.dom.style.marginBottom = '0';
26031        var _this = this;
26032        var editorcore = this.editorcore;
26033        var editor= this.editor;
26034        
26035        var children = [];
26036        var btn = function(id,cmd , toggle, handler, html){
26037        
26038             var  event = toggle ? 'toggle' : 'click';
26039        
26040             var a = {
26041                 size : 'sm',
26042                 xtype: 'Button',
26043                 xns: Roo.bootstrap,
26044                 //glyphicon : id,
26045                 fa: id,
26046                 cmd : id || cmd,
26047                 enableToggle:toggle !== false,
26048                 html : html || '',
26049                 pressed : toggle ? false : null,
26050                 listeners : {}
26051             };
26052             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26053                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26054             };
26055             children.push(a);
26056             return a;
26057        }
26058        
26059     //    var cb_box = function...
26060         
26061         var style = {
26062                 xtype: 'Button',
26063                 size : 'sm',
26064                 xns: Roo.bootstrap,
26065                 fa : 'font',
26066                 //html : 'submit'
26067                 menu : {
26068                     xtype: 'Menu',
26069                     xns: Roo.bootstrap,
26070                     items:  []
26071                 }
26072         };
26073         Roo.each(this.formats, function(f) {
26074             style.menu.items.push({
26075                 xtype :'MenuItem',
26076                 xns: Roo.bootstrap,
26077                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26078                 tagname : f,
26079                 listeners : {
26080                     click : function()
26081                     {
26082                         editorcore.insertTag(this.tagname);
26083                         editor.focus();
26084                     }
26085                 }
26086                 
26087             });
26088         });
26089         children.push(style);   
26090         
26091         btn('bold',false,true);
26092         btn('italic',false,true);
26093         btn('align-left', 'justifyleft',true);
26094         btn('align-center', 'justifycenter',true);
26095         btn('align-right' , 'justifyright',true);
26096         btn('link', false, false, function(btn) {
26097             //Roo.log("create link?");
26098             var url = prompt(this.createLinkText, this.defaultLinkValue);
26099             if(url && url != 'http:/'+'/'){
26100                 this.editorcore.relayCmd('createlink', url);
26101             }
26102         }),
26103         btn('list','insertunorderedlist',true);
26104         btn('pencil', false,true, function(btn){
26105                 Roo.log(this);
26106                 this.toggleSourceEdit(btn.pressed);
26107         });
26108         
26109         if (this.editor.btns.length > 0) {
26110             for (var i = 0; i<this.editor.btns.length; i++) {
26111                 children.push(this.editor.btns[i]);
26112             }
26113         }
26114         
26115         /*
26116         var cog = {
26117                 xtype: 'Button',
26118                 size : 'sm',
26119                 xns: Roo.bootstrap,
26120                 glyphicon : 'cog',
26121                 //html : 'submit'
26122                 menu : {
26123                     xtype: 'Menu',
26124                     xns: Roo.bootstrap,
26125                     items:  []
26126                 }
26127         };
26128         
26129         cog.menu.items.push({
26130             xtype :'MenuItem',
26131             xns: Roo.bootstrap,
26132             html : Clean styles,
26133             tagname : f,
26134             listeners : {
26135                 click : function()
26136                 {
26137                     editorcore.insertTag(this.tagname);
26138                     editor.focus();
26139                 }
26140             }
26141             
26142         });
26143        */
26144         
26145          
26146        this.xtype = 'NavSimplebar';
26147         
26148         for(var i=0;i< children.length;i++) {
26149             
26150             this.buttons.add(this.addxtypeChild(children[i]));
26151             
26152         }
26153         
26154         editor.on('editorevent', this.updateToolbar, this);
26155     },
26156     onBtnClick : function(id)
26157     {
26158        this.editorcore.relayCmd(id);
26159        this.editorcore.focus();
26160     },
26161     
26162     /**
26163      * Protected method that will not generally be called directly. It triggers
26164      * a toolbar update by reading the markup state of the current selection in the editor.
26165      */
26166     updateToolbar: function(){
26167
26168         if(!this.editorcore.activated){
26169             this.editor.onFirstFocus(); // is this neeed?
26170             return;
26171         }
26172
26173         var btns = this.buttons; 
26174         var doc = this.editorcore.doc;
26175         btns.get('bold').setActive(doc.queryCommandState('bold'));
26176         btns.get('italic').setActive(doc.queryCommandState('italic'));
26177         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26178         
26179         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26180         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26181         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26182         
26183         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26184         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26185          /*
26186         
26187         var ans = this.editorcore.getAllAncestors();
26188         if (this.formatCombo) {
26189             
26190             
26191             var store = this.formatCombo.store;
26192             this.formatCombo.setValue("");
26193             for (var i =0; i < ans.length;i++) {
26194                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26195                     // select it..
26196                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26197                     break;
26198                 }
26199             }
26200         }
26201         
26202         
26203         
26204         // hides menus... - so this cant be on a menu...
26205         Roo.bootstrap.MenuMgr.hideAll();
26206         */
26207         Roo.bootstrap.MenuMgr.hideAll();
26208         //this.editorsyncValue();
26209     },
26210     onFirstFocus: function() {
26211         this.buttons.each(function(item){
26212            item.enable();
26213         });
26214     },
26215     toggleSourceEdit : function(sourceEditMode){
26216         
26217           
26218         if(sourceEditMode){
26219             Roo.log("disabling buttons");
26220            this.buttons.each( function(item){
26221                 if(item.cmd != 'pencil'){
26222                     item.disable();
26223                 }
26224             });
26225           
26226         }else{
26227             Roo.log("enabling buttons");
26228             if(this.editorcore.initialized){
26229                 this.buttons.each( function(item){
26230                     item.enable();
26231                 });
26232             }
26233             
26234         }
26235         Roo.log("calling toggole on editor");
26236         // tell the editor that it's been pressed..
26237         this.editor.toggleSourceEdit(sourceEditMode);
26238        
26239     }
26240 });
26241
26242
26243
26244
26245  
26246 /*
26247  * - LGPL
26248  */
26249
26250 /**
26251  * @class Roo.bootstrap.Markdown
26252  * @extends Roo.bootstrap.TextArea
26253  * Bootstrap Showdown editable area
26254  * @cfg {string} content
26255  * 
26256  * @constructor
26257  * Create a new Showdown
26258  */
26259
26260 Roo.bootstrap.Markdown = function(config){
26261     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26262    
26263 };
26264
26265 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26266     
26267     editing :false,
26268     
26269     initEvents : function()
26270     {
26271         
26272         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26273         this.markdownEl = this.el.createChild({
26274             cls : 'roo-markdown-area'
26275         });
26276         this.inputEl().addClass('d-none');
26277         if (this.getValue() == '') {
26278             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26279             
26280         } else {
26281             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26282         }
26283         this.markdownEl.on('click', this.toggleTextEdit, this);
26284         this.on('blur', this.toggleTextEdit, this);
26285         this.on('specialkey', this.resizeTextArea, this);
26286     },
26287     
26288     toggleTextEdit : function()
26289     {
26290         var sh = this.markdownEl.getHeight();
26291         this.inputEl().addClass('d-none');
26292         this.markdownEl.addClass('d-none');
26293         if (!this.editing) {
26294             // show editor?
26295             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26296             this.inputEl().removeClass('d-none');
26297             this.inputEl().focus();
26298             this.editing = true;
26299             return;
26300         }
26301         // show showdown...
26302         this.updateMarkdown();
26303         this.markdownEl.removeClass('d-none');
26304         this.editing = false;
26305         return;
26306     },
26307     updateMarkdown : function()
26308     {
26309         if (this.getValue() == '') {
26310             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26311             return;
26312         }
26313  
26314         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26315     },
26316     
26317     resizeTextArea: function () {
26318         
26319         var sh = 100;
26320         Roo.log([sh, this.getValue().split("\n").length * 30]);
26321         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26322     },
26323     setValue : function(val)
26324     {
26325         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26326         if (!this.editing) {
26327             this.updateMarkdown();
26328         }
26329         
26330     },
26331     focus : function()
26332     {
26333         if (!this.editing) {
26334             this.toggleTextEdit();
26335         }
26336         
26337     }
26338
26339
26340 });
26341 /**
26342  * @class Roo.bootstrap.Table.AbstractSelectionModel
26343  * @extends Roo.util.Observable
26344  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26345  * implemented by descendant classes.  This class should not be directly instantiated.
26346  * @constructor
26347  */
26348 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26349     this.locked = false;
26350     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26351 };
26352
26353
26354 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26355     /** @ignore Called by the grid automatically. Do not call directly. */
26356     init : function(grid){
26357         this.grid = grid;
26358         this.initEvents();
26359     },
26360
26361     /**
26362      * Locks the selections.
26363      */
26364     lock : function(){
26365         this.locked = true;
26366     },
26367
26368     /**
26369      * Unlocks the selections.
26370      */
26371     unlock : function(){
26372         this.locked = false;
26373     },
26374
26375     /**
26376      * Returns true if the selections are locked.
26377      * @return {Boolean}
26378      */
26379     isLocked : function(){
26380         return this.locked;
26381     },
26382     
26383     
26384     initEvents : function ()
26385     {
26386         
26387     }
26388 });
26389 /**
26390  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26391  * @class Roo.bootstrap.Table.RowSelectionModel
26392  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26393  * It supports multiple selections and keyboard selection/navigation. 
26394  * @constructor
26395  * @param {Object} config
26396  */
26397
26398 Roo.bootstrap.Table.RowSelectionModel = function(config){
26399     Roo.apply(this, config);
26400     this.selections = new Roo.util.MixedCollection(false, function(o){
26401         return o.id;
26402     });
26403
26404     this.last = false;
26405     this.lastActive = false;
26406
26407     this.addEvents({
26408         /**
26409              * @event selectionchange
26410              * Fires when the selection changes
26411              * @param {SelectionModel} this
26412              */
26413             "selectionchange" : true,
26414         /**
26415              * @event afterselectionchange
26416              * Fires after the selection changes (eg. by key press or clicking)
26417              * @param {SelectionModel} this
26418              */
26419             "afterselectionchange" : true,
26420         /**
26421              * @event beforerowselect
26422              * Fires when a row is selected being selected, return false to cancel.
26423              * @param {SelectionModel} this
26424              * @param {Number} rowIndex The selected index
26425              * @param {Boolean} keepExisting False if other selections will be cleared
26426              */
26427             "beforerowselect" : true,
26428         /**
26429              * @event rowselect
26430              * Fires when a row is selected.
26431              * @param {SelectionModel} this
26432              * @param {Number} rowIndex The selected index
26433              * @param {Roo.data.Record} r The record
26434              */
26435             "rowselect" : true,
26436         /**
26437              * @event rowdeselect
26438              * Fires when a row is deselected.
26439              * @param {SelectionModel} this
26440              * @param {Number} rowIndex The selected index
26441              */
26442         "rowdeselect" : true
26443     });
26444     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26445     this.locked = false;
26446  };
26447
26448 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26449     /**
26450      * @cfg {Boolean} singleSelect
26451      * True to allow selection of only one row at a time (defaults to false)
26452      */
26453     singleSelect : false,
26454
26455     // private
26456     initEvents : function()
26457     {
26458
26459         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26460         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26461         //}else{ // allow click to work like normal
26462          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26463         //}
26464         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26465         this.grid.on("rowclick", this.handleMouseDown, this);
26466         
26467         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26468             "up" : function(e){
26469                 if(!e.shiftKey){
26470                     this.selectPrevious(e.shiftKey);
26471                 }else if(this.last !== false && this.lastActive !== false){
26472                     var last = this.last;
26473                     this.selectRange(this.last,  this.lastActive-1);
26474                     this.grid.getView().focusRow(this.lastActive);
26475                     if(last !== false){
26476                         this.last = last;
26477                     }
26478                 }else{
26479                     this.selectFirstRow();
26480                 }
26481                 this.fireEvent("afterselectionchange", this);
26482             },
26483             "down" : function(e){
26484                 if(!e.shiftKey){
26485                     this.selectNext(e.shiftKey);
26486                 }else if(this.last !== false && this.lastActive !== false){
26487                     var last = this.last;
26488                     this.selectRange(this.last,  this.lastActive+1);
26489                     this.grid.getView().focusRow(this.lastActive);
26490                     if(last !== false){
26491                         this.last = last;
26492                     }
26493                 }else{
26494                     this.selectFirstRow();
26495                 }
26496                 this.fireEvent("afterselectionchange", this);
26497             },
26498             scope: this
26499         });
26500         this.grid.store.on('load', function(){
26501             this.selections.clear();
26502         },this);
26503         /*
26504         var view = this.grid.view;
26505         view.on("refresh", this.onRefresh, this);
26506         view.on("rowupdated", this.onRowUpdated, this);
26507         view.on("rowremoved", this.onRemove, this);
26508         */
26509     },
26510
26511     // private
26512     onRefresh : function()
26513     {
26514         var ds = this.grid.store, i, v = this.grid.view;
26515         var s = this.selections;
26516         s.each(function(r){
26517             if((i = ds.indexOfId(r.id)) != -1){
26518                 v.onRowSelect(i);
26519             }else{
26520                 s.remove(r);
26521             }
26522         });
26523     },
26524
26525     // private
26526     onRemove : function(v, index, r){
26527         this.selections.remove(r);
26528     },
26529
26530     // private
26531     onRowUpdated : function(v, index, r){
26532         if(this.isSelected(r)){
26533             v.onRowSelect(index);
26534         }
26535     },
26536
26537     /**
26538      * Select records.
26539      * @param {Array} records The records to select
26540      * @param {Boolean} keepExisting (optional) True to keep existing selections
26541      */
26542     selectRecords : function(records, keepExisting)
26543     {
26544         if(!keepExisting){
26545             this.clearSelections();
26546         }
26547             var ds = this.grid.store;
26548         for(var i = 0, len = records.length; i < len; i++){
26549             this.selectRow(ds.indexOf(records[i]), true);
26550         }
26551     },
26552
26553     /**
26554      * Gets the number of selected rows.
26555      * @return {Number}
26556      */
26557     getCount : function(){
26558         return this.selections.length;
26559     },
26560
26561     /**
26562      * Selects the first row in the grid.
26563      */
26564     selectFirstRow : function(){
26565         this.selectRow(0);
26566     },
26567
26568     /**
26569      * Select the last row.
26570      * @param {Boolean} keepExisting (optional) True to keep existing selections
26571      */
26572     selectLastRow : function(keepExisting){
26573         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26574         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26575     },
26576
26577     /**
26578      * Selects the row immediately following the last selected row.
26579      * @param {Boolean} keepExisting (optional) True to keep existing selections
26580      */
26581     selectNext : function(keepExisting)
26582     {
26583             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26584             this.selectRow(this.last+1, keepExisting);
26585             this.grid.getView().focusRow(this.last);
26586         }
26587     },
26588
26589     /**
26590      * Selects the row that precedes the last selected row.
26591      * @param {Boolean} keepExisting (optional) True to keep existing selections
26592      */
26593     selectPrevious : function(keepExisting){
26594         if(this.last){
26595             this.selectRow(this.last-1, keepExisting);
26596             this.grid.getView().focusRow(this.last);
26597         }
26598     },
26599
26600     /**
26601      * Returns the selected records
26602      * @return {Array} Array of selected records
26603      */
26604     getSelections : function(){
26605         return [].concat(this.selections.items);
26606     },
26607
26608     /**
26609      * Returns the first selected record.
26610      * @return {Record}
26611      */
26612     getSelected : function(){
26613         return this.selections.itemAt(0);
26614     },
26615
26616
26617     /**
26618      * Clears all selections.
26619      */
26620     clearSelections : function(fast)
26621     {
26622         if(this.locked) {
26623             return;
26624         }
26625         if(fast !== true){
26626                 var ds = this.grid.store;
26627             var s = this.selections;
26628             s.each(function(r){
26629                 this.deselectRow(ds.indexOfId(r.id));
26630             }, this);
26631             s.clear();
26632         }else{
26633             this.selections.clear();
26634         }
26635         this.last = false;
26636     },
26637
26638
26639     /**
26640      * Selects all rows.
26641      */
26642     selectAll : function(){
26643         if(this.locked) {
26644             return;
26645         }
26646         this.selections.clear();
26647         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26648             this.selectRow(i, true);
26649         }
26650     },
26651
26652     /**
26653      * Returns True if there is a selection.
26654      * @return {Boolean}
26655      */
26656     hasSelection : function(){
26657         return this.selections.length > 0;
26658     },
26659
26660     /**
26661      * Returns True if the specified row is selected.
26662      * @param {Number/Record} record The record or index of the record to check
26663      * @return {Boolean}
26664      */
26665     isSelected : function(index){
26666             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26667         return (r && this.selections.key(r.id) ? true : false);
26668     },
26669
26670     /**
26671      * Returns True if the specified record id is selected.
26672      * @param {String} id The id of record to check
26673      * @return {Boolean}
26674      */
26675     isIdSelected : function(id){
26676         return (this.selections.key(id) ? true : false);
26677     },
26678
26679
26680     // private
26681     handleMouseDBClick : function(e, t){
26682         
26683     },
26684     // private
26685     handleMouseDown : function(e, t)
26686     {
26687             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26688         if(this.isLocked() || rowIndex < 0 ){
26689             return;
26690         };
26691         if(e.shiftKey && this.last !== false){
26692             var last = this.last;
26693             this.selectRange(last, rowIndex, e.ctrlKey);
26694             this.last = last; // reset the last
26695             t.focus();
26696     
26697         }else{
26698             var isSelected = this.isSelected(rowIndex);
26699             //Roo.log("select row:" + rowIndex);
26700             if(isSelected){
26701                 this.deselectRow(rowIndex);
26702             } else {
26703                         this.selectRow(rowIndex, true);
26704             }
26705     
26706             /*
26707                 if(e.button !== 0 && isSelected){
26708                 alert('rowIndex 2: ' + rowIndex);
26709                     view.focusRow(rowIndex);
26710                 }else if(e.ctrlKey && isSelected){
26711                     this.deselectRow(rowIndex);
26712                 }else if(!isSelected){
26713                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26714                     view.focusRow(rowIndex);
26715                 }
26716             */
26717         }
26718         this.fireEvent("afterselectionchange", this);
26719     },
26720     // private
26721     handleDragableRowClick :  function(grid, rowIndex, e) 
26722     {
26723         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26724             this.selectRow(rowIndex, false);
26725             grid.view.focusRow(rowIndex);
26726              this.fireEvent("afterselectionchange", this);
26727         }
26728     },
26729     
26730     /**
26731      * Selects multiple rows.
26732      * @param {Array} rows Array of the indexes of the row to select
26733      * @param {Boolean} keepExisting (optional) True to keep existing selections
26734      */
26735     selectRows : function(rows, keepExisting){
26736         if(!keepExisting){
26737             this.clearSelections();
26738         }
26739         for(var i = 0, len = rows.length; i < len; i++){
26740             this.selectRow(rows[i], true);
26741         }
26742     },
26743
26744     /**
26745      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26746      * @param {Number} startRow The index of the first row in the range
26747      * @param {Number} endRow The index of the last row in the range
26748      * @param {Boolean} keepExisting (optional) True to retain existing selections
26749      */
26750     selectRange : function(startRow, endRow, keepExisting){
26751         if(this.locked) {
26752             return;
26753         }
26754         if(!keepExisting){
26755             this.clearSelections();
26756         }
26757         if(startRow <= endRow){
26758             for(var i = startRow; i <= endRow; i++){
26759                 this.selectRow(i, true);
26760             }
26761         }else{
26762             for(var i = startRow; i >= endRow; i--){
26763                 this.selectRow(i, true);
26764             }
26765         }
26766     },
26767
26768     /**
26769      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26770      * @param {Number} startRow The index of the first row in the range
26771      * @param {Number} endRow The index of the last row in the range
26772      */
26773     deselectRange : function(startRow, endRow, preventViewNotify){
26774         if(this.locked) {
26775             return;
26776         }
26777         for(var i = startRow; i <= endRow; i++){
26778             this.deselectRow(i, preventViewNotify);
26779         }
26780     },
26781
26782     /**
26783      * Selects a row.
26784      * @param {Number} row The index of the row to select
26785      * @param {Boolean} keepExisting (optional) True to keep existing selections
26786      */
26787     selectRow : function(index, keepExisting, preventViewNotify)
26788     {
26789             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26790             return;
26791         }
26792         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26793             if(!keepExisting || this.singleSelect){
26794                 this.clearSelections();
26795             }
26796             
26797             var r = this.grid.store.getAt(index);
26798             //console.log('selectRow - record id :' + r.id);
26799             
26800             this.selections.add(r);
26801             this.last = this.lastActive = index;
26802             if(!preventViewNotify){
26803                 var proxy = new Roo.Element(
26804                                 this.grid.getRowDom(index)
26805                 );
26806                 proxy.addClass('bg-info info');
26807             }
26808             this.fireEvent("rowselect", this, index, r);
26809             this.fireEvent("selectionchange", this);
26810         }
26811     },
26812
26813     /**
26814      * Deselects a row.
26815      * @param {Number} row The index of the row to deselect
26816      */
26817     deselectRow : function(index, preventViewNotify)
26818     {
26819         if(this.locked) {
26820             return;
26821         }
26822         if(this.last == index){
26823             this.last = false;
26824         }
26825         if(this.lastActive == index){
26826             this.lastActive = false;
26827         }
26828         
26829         var r = this.grid.store.getAt(index);
26830         if (!r) {
26831             return;
26832         }
26833         
26834         this.selections.remove(r);
26835         //.console.log('deselectRow - record id :' + r.id);
26836         if(!preventViewNotify){
26837         
26838             var proxy = new Roo.Element(
26839                 this.grid.getRowDom(index)
26840             );
26841             proxy.removeClass('bg-info info');
26842         }
26843         this.fireEvent("rowdeselect", this, index);
26844         this.fireEvent("selectionchange", this);
26845     },
26846
26847     // private
26848     restoreLast : function(){
26849         if(this._last){
26850             this.last = this._last;
26851         }
26852     },
26853
26854     // private
26855     acceptsNav : function(row, col, cm){
26856         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26857     },
26858
26859     // private
26860     onEditorKey : function(field, e){
26861         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26862         if(k == e.TAB){
26863             e.stopEvent();
26864             ed.completeEdit();
26865             if(e.shiftKey){
26866                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26867             }else{
26868                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26869             }
26870         }else if(k == e.ENTER && !e.ctrlKey){
26871             e.stopEvent();
26872             ed.completeEdit();
26873             if(e.shiftKey){
26874                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26875             }else{
26876                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26877             }
26878         }else if(k == e.ESC){
26879             ed.cancelEdit();
26880         }
26881         if(newCell){
26882             g.startEditing(newCell[0], newCell[1]);
26883         }
26884     }
26885 });
26886 /*
26887  * Based on:
26888  * Ext JS Library 1.1.1
26889  * Copyright(c) 2006-2007, Ext JS, LLC.
26890  *
26891  * Originally Released Under LGPL - original licence link has changed is not relivant.
26892  *
26893  * Fork - LGPL
26894  * <script type="text/javascript">
26895  */
26896  
26897 /**
26898  * @class Roo.bootstrap.PagingToolbar
26899  * @extends Roo.bootstrap.NavSimplebar
26900  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26901  * @constructor
26902  * Create a new PagingToolbar
26903  * @param {Object} config The config object
26904  * @param {Roo.data.Store} store
26905  */
26906 Roo.bootstrap.PagingToolbar = function(config)
26907 {
26908     // old args format still supported... - xtype is prefered..
26909         // created from xtype...
26910     
26911     this.ds = config.dataSource;
26912     
26913     if (config.store && !this.ds) {
26914         this.store= Roo.factory(config.store, Roo.data);
26915         this.ds = this.store;
26916         this.ds.xmodule = this.xmodule || false;
26917     }
26918     
26919     this.toolbarItems = [];
26920     if (config.items) {
26921         this.toolbarItems = config.items;
26922     }
26923     
26924     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26925     
26926     this.cursor = 0;
26927     
26928     if (this.ds) { 
26929         this.bind(this.ds);
26930     }
26931     
26932     if (Roo.bootstrap.version == 4) {
26933         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26934     } else {
26935         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26936     }
26937     
26938 };
26939
26940 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26941     /**
26942      * @cfg {Roo.data.Store} dataSource
26943      * The underlying data store providing the paged data
26944      */
26945     /**
26946      * @cfg {String/HTMLElement/Element} container
26947      * container The id or element that will contain the toolbar
26948      */
26949     /**
26950      * @cfg {Boolean} displayInfo
26951      * True to display the displayMsg (defaults to false)
26952      */
26953     /**
26954      * @cfg {Number} pageSize
26955      * The number of records to display per page (defaults to 20)
26956      */
26957     pageSize: 20,
26958     /**
26959      * @cfg {String} displayMsg
26960      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26961      */
26962     displayMsg : 'Displaying {0} - {1} of {2}',
26963     /**
26964      * @cfg {String} emptyMsg
26965      * The message to display when no records are found (defaults to "No data to display")
26966      */
26967     emptyMsg : 'No data to display',
26968     /**
26969      * Customizable piece of the default paging text (defaults to "Page")
26970      * @type String
26971      */
26972     beforePageText : "Page",
26973     /**
26974      * Customizable piece of the default paging text (defaults to "of %0")
26975      * @type String
26976      */
26977     afterPageText : "of {0}",
26978     /**
26979      * Customizable piece of the default paging text (defaults to "First Page")
26980      * @type String
26981      */
26982     firstText : "First Page",
26983     /**
26984      * Customizable piece of the default paging text (defaults to "Previous Page")
26985      * @type String
26986      */
26987     prevText : "Previous Page",
26988     /**
26989      * Customizable piece of the default paging text (defaults to "Next Page")
26990      * @type String
26991      */
26992     nextText : "Next Page",
26993     /**
26994      * Customizable piece of the default paging text (defaults to "Last Page")
26995      * @type String
26996      */
26997     lastText : "Last Page",
26998     /**
26999      * Customizable piece of the default paging text (defaults to "Refresh")
27000      * @type String
27001      */
27002     refreshText : "Refresh",
27003
27004     buttons : false,
27005     // private
27006     onRender : function(ct, position) 
27007     {
27008         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27009         this.navgroup.parentId = this.id;
27010         this.navgroup.onRender(this.el, null);
27011         // add the buttons to the navgroup
27012         
27013         if(this.displayInfo){
27014             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27015             this.displayEl = this.el.select('.x-paging-info', true).first();
27016 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27017 //            this.displayEl = navel.el.select('span',true).first();
27018         }
27019         
27020         var _this = this;
27021         
27022         if(this.buttons){
27023             Roo.each(_this.buttons, function(e){ // this might need to use render????
27024                Roo.factory(e).render(_this.el);
27025             });
27026         }
27027             
27028         Roo.each(_this.toolbarItems, function(e) {
27029             _this.navgroup.addItem(e);
27030         });
27031         
27032         
27033         this.first = this.navgroup.addItem({
27034             tooltip: this.firstText,
27035             cls: "prev btn-outline-secondary",
27036             html : ' <i class="fa fa-step-backward"></i>',
27037             disabled: true,
27038             preventDefault: true,
27039             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27040         });
27041         
27042         this.prev =  this.navgroup.addItem({
27043             tooltip: this.prevText,
27044             cls: "prev btn-outline-secondary",
27045             html : ' <i class="fa fa-backward"></i>',
27046             disabled: true,
27047             preventDefault: true,
27048             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27049         });
27050     //this.addSeparator();
27051         
27052         
27053         var field = this.navgroup.addItem( {
27054             tagtype : 'span',
27055             cls : 'x-paging-position  btn-outline-secondary',
27056              disabled: true,
27057             html : this.beforePageText  +
27058                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27059                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27060          } ); //?? escaped?
27061         
27062         this.field = field.el.select('input', true).first();
27063         this.field.on("keydown", this.onPagingKeydown, this);
27064         this.field.on("focus", function(){this.dom.select();});
27065     
27066     
27067         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27068         //this.field.setHeight(18);
27069         //this.addSeparator();
27070         this.next = this.navgroup.addItem({
27071             tooltip: this.nextText,
27072             cls: "next btn-outline-secondary",
27073             html : ' <i class="fa fa-forward"></i>',
27074             disabled: true,
27075             preventDefault: true,
27076             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27077         });
27078         this.last = this.navgroup.addItem({
27079             tooltip: this.lastText,
27080             html : ' <i class="fa fa-step-forward"></i>',
27081             cls: "next btn-outline-secondary",
27082             disabled: true,
27083             preventDefault: true,
27084             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27085         });
27086     //this.addSeparator();
27087         this.loading = this.navgroup.addItem({
27088             tooltip: this.refreshText,
27089             cls: "btn-outline-secondary",
27090             html : ' <i class="fa fa-refresh"></i>',
27091             preventDefault: true,
27092             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27093         });
27094         
27095     },
27096
27097     // private
27098     updateInfo : function(){
27099         if(this.displayEl){
27100             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27101             var msg = count == 0 ?
27102                 this.emptyMsg :
27103                 String.format(
27104                     this.displayMsg,
27105                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27106                 );
27107             this.displayEl.update(msg);
27108         }
27109     },
27110
27111     // private
27112     onLoad : function(ds, r, o)
27113     {
27114         this.cursor = o.params.start ? o.params.start : 0;
27115         
27116         var d = this.getPageData(),
27117             ap = d.activePage,
27118             ps = d.pages;
27119         
27120         
27121         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27122         this.field.dom.value = ap;
27123         this.first.setDisabled(ap == 1);
27124         this.prev.setDisabled(ap == 1);
27125         this.next.setDisabled(ap == ps);
27126         this.last.setDisabled(ap == ps);
27127         this.loading.enable();
27128         this.updateInfo();
27129     },
27130
27131     // private
27132     getPageData : function(){
27133         var total = this.ds.getTotalCount();
27134         return {
27135             total : total,
27136             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27137             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27138         };
27139     },
27140
27141     // private
27142     onLoadError : function(){
27143         this.loading.enable();
27144     },
27145
27146     // private
27147     onPagingKeydown : function(e){
27148         var k = e.getKey();
27149         var d = this.getPageData();
27150         if(k == e.RETURN){
27151             var v = this.field.dom.value, pageNum;
27152             if(!v || isNaN(pageNum = parseInt(v, 10))){
27153                 this.field.dom.value = d.activePage;
27154                 return;
27155             }
27156             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27157             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27158             e.stopEvent();
27159         }
27160         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))
27161         {
27162           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27163           this.field.dom.value = pageNum;
27164           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27165           e.stopEvent();
27166         }
27167         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27168         {
27169           var v = this.field.dom.value, pageNum; 
27170           var increment = (e.shiftKey) ? 10 : 1;
27171           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27172                 increment *= -1;
27173           }
27174           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27175             this.field.dom.value = d.activePage;
27176             return;
27177           }
27178           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27179           {
27180             this.field.dom.value = parseInt(v, 10) + increment;
27181             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27182             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27183           }
27184           e.stopEvent();
27185         }
27186     },
27187
27188     // private
27189     beforeLoad : function(){
27190         if(this.loading){
27191             this.loading.disable();
27192         }
27193     },
27194
27195     // private
27196     onClick : function(which){
27197         
27198         var ds = this.ds;
27199         if (!ds) {
27200             return;
27201         }
27202         
27203         switch(which){
27204             case "first":
27205                 ds.load({params:{start: 0, limit: this.pageSize}});
27206             break;
27207             case "prev":
27208                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27209             break;
27210             case "next":
27211                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27212             break;
27213             case "last":
27214                 var total = ds.getTotalCount();
27215                 var extra = total % this.pageSize;
27216                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27217                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27218             break;
27219             case "refresh":
27220                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27221             break;
27222         }
27223     },
27224
27225     /**
27226      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27227      * @param {Roo.data.Store} store The data store to unbind
27228      */
27229     unbind : function(ds){
27230         ds.un("beforeload", this.beforeLoad, this);
27231         ds.un("load", this.onLoad, this);
27232         ds.un("loadexception", this.onLoadError, this);
27233         ds.un("remove", this.updateInfo, this);
27234         ds.un("add", this.updateInfo, this);
27235         this.ds = undefined;
27236     },
27237
27238     /**
27239      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27240      * @param {Roo.data.Store} store The data store to bind
27241      */
27242     bind : function(ds){
27243         ds.on("beforeload", this.beforeLoad, this);
27244         ds.on("load", this.onLoad, this);
27245         ds.on("loadexception", this.onLoadError, this);
27246         ds.on("remove", this.updateInfo, this);
27247         ds.on("add", this.updateInfo, this);
27248         this.ds = ds;
27249     }
27250 });/*
27251  * - LGPL
27252  *
27253  * element
27254  * 
27255  */
27256
27257 /**
27258  * @class Roo.bootstrap.MessageBar
27259  * @extends Roo.bootstrap.Component
27260  * Bootstrap MessageBar class
27261  * @cfg {String} html contents of the MessageBar
27262  * @cfg {String} weight (info | success | warning | danger) default info
27263  * @cfg {String} beforeClass insert the bar before the given class
27264  * @cfg {Boolean} closable (true | false) default false
27265  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27266  * 
27267  * @constructor
27268  * Create a new Element
27269  * @param {Object} config The config object
27270  */
27271
27272 Roo.bootstrap.MessageBar = function(config){
27273     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27274 };
27275
27276 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27277     
27278     html: '',
27279     weight: 'info',
27280     closable: false,
27281     fixed: false,
27282     beforeClass: 'bootstrap-sticky-wrap',
27283     
27284     getAutoCreate : function(){
27285         
27286         var cfg = {
27287             tag: 'div',
27288             cls: 'alert alert-dismissable alert-' + this.weight,
27289             cn: [
27290                 {
27291                     tag: 'span',
27292                     cls: 'message',
27293                     html: this.html || ''
27294                 }
27295             ]
27296         };
27297         
27298         if(this.fixed){
27299             cfg.cls += ' alert-messages-fixed';
27300         }
27301         
27302         if(this.closable){
27303             cfg.cn.push({
27304                 tag: 'button',
27305                 cls: 'close',
27306                 html: 'x'
27307             });
27308         }
27309         
27310         return cfg;
27311     },
27312     
27313     onRender : function(ct, position)
27314     {
27315         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27316         
27317         if(!this.el){
27318             var cfg = Roo.apply({},  this.getAutoCreate());
27319             cfg.id = Roo.id();
27320             
27321             if (this.cls) {
27322                 cfg.cls += ' ' + this.cls;
27323             }
27324             if (this.style) {
27325                 cfg.style = this.style;
27326             }
27327             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27328             
27329             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27330         }
27331         
27332         this.el.select('>button.close').on('click', this.hide, this);
27333         
27334     },
27335     
27336     show : function()
27337     {
27338         if (!this.rendered) {
27339             this.render();
27340         }
27341         
27342         this.el.show();
27343         
27344         this.fireEvent('show', this);
27345         
27346     },
27347     
27348     hide : function()
27349     {
27350         if (!this.rendered) {
27351             this.render();
27352         }
27353         
27354         this.el.hide();
27355         
27356         this.fireEvent('hide', this);
27357     },
27358     
27359     update : function()
27360     {
27361 //        var e = this.el.dom.firstChild;
27362 //        
27363 //        if(this.closable){
27364 //            e = e.nextSibling;
27365 //        }
27366 //        
27367 //        e.data = this.html || '';
27368
27369         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27370     }
27371    
27372 });
27373
27374  
27375
27376      /*
27377  * - LGPL
27378  *
27379  * Graph
27380  * 
27381  */
27382
27383
27384 /**
27385  * @class Roo.bootstrap.Graph
27386  * @extends Roo.bootstrap.Component
27387  * Bootstrap Graph class
27388 > Prameters
27389  -sm {number} sm 4
27390  -md {number} md 5
27391  @cfg {String} graphtype  bar | vbar | pie
27392  @cfg {number} g_x coodinator | centre x (pie)
27393  @cfg {number} g_y coodinator | centre y (pie)
27394  @cfg {number} g_r radius (pie)
27395  @cfg {number} g_height height of the chart (respected by all elements in the set)
27396  @cfg {number} g_width width of the chart (respected by all elements in the set)
27397  @cfg {Object} title The title of the chart
27398     
27399  -{Array}  values
27400  -opts (object) options for the chart 
27401      o {
27402      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27403      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27404      o vgutter (number)
27405      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.
27406      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27407      o to
27408      o stretch (boolean)
27409      o }
27410  -opts (object) options for the pie
27411      o{
27412      o cut
27413      o startAngle (number)
27414      o endAngle (number)
27415      } 
27416  *
27417  * @constructor
27418  * Create a new Input
27419  * @param {Object} config The config object
27420  */
27421
27422 Roo.bootstrap.Graph = function(config){
27423     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27424     
27425     this.addEvents({
27426         // img events
27427         /**
27428          * @event click
27429          * The img click event for the img.
27430          * @param {Roo.EventObject} e
27431          */
27432         "click" : true
27433     });
27434 };
27435
27436 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27437     
27438     sm: 4,
27439     md: 5,
27440     graphtype: 'bar',
27441     g_height: 250,
27442     g_width: 400,
27443     g_x: 50,
27444     g_y: 50,
27445     g_r: 30,
27446     opts:{
27447         //g_colors: this.colors,
27448         g_type: 'soft',
27449         g_gutter: '20%'
27450
27451     },
27452     title : false,
27453
27454     getAutoCreate : function(){
27455         
27456         var cfg = {
27457             tag: 'div',
27458             html : null
27459         };
27460         
27461         
27462         return  cfg;
27463     },
27464
27465     onRender : function(ct,position){
27466         
27467         
27468         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27469         
27470         if (typeof(Raphael) == 'undefined') {
27471             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27472             return;
27473         }
27474         
27475         this.raphael = Raphael(this.el.dom);
27476         
27477                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27478                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27479                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27480                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27481                 /*
27482                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27483                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27484                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27485                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27486                 
27487                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27488                 r.barchart(330, 10, 300, 220, data1);
27489                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27490                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27491                 */
27492                 
27493                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27494                 // r.barchart(30, 30, 560, 250,  xdata, {
27495                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27496                 //     axis : "0 0 1 1",
27497                 //     axisxlabels :  xdata
27498                 //     //yvalues : cols,
27499                    
27500                 // });
27501 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27502 //        
27503 //        this.load(null,xdata,{
27504 //                axis : "0 0 1 1",
27505 //                axisxlabels :  xdata
27506 //                });
27507
27508     },
27509
27510     load : function(graphtype,xdata,opts)
27511     {
27512         this.raphael.clear();
27513         if(!graphtype) {
27514             graphtype = this.graphtype;
27515         }
27516         if(!opts){
27517             opts = this.opts;
27518         }
27519         var r = this.raphael,
27520             fin = function () {
27521                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27522             },
27523             fout = function () {
27524                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27525             },
27526             pfin = function() {
27527                 this.sector.stop();
27528                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27529
27530                 if (this.label) {
27531                     this.label[0].stop();
27532                     this.label[0].attr({ r: 7.5 });
27533                     this.label[1].attr({ "font-weight": 800 });
27534                 }
27535             },
27536             pfout = function() {
27537                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27538
27539                 if (this.label) {
27540                     this.label[0].animate({ r: 5 }, 500, "bounce");
27541                     this.label[1].attr({ "font-weight": 400 });
27542                 }
27543             };
27544
27545         switch(graphtype){
27546             case 'bar':
27547                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27548                 break;
27549             case 'hbar':
27550                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27551                 break;
27552             case 'pie':
27553 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27554 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27555 //            
27556                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27557                 
27558                 break;
27559
27560         }
27561         
27562         if(this.title){
27563             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27564         }
27565         
27566     },
27567     
27568     setTitle: function(o)
27569     {
27570         this.title = o;
27571     },
27572     
27573     initEvents: function() {
27574         
27575         if(!this.href){
27576             this.el.on('click', this.onClick, this);
27577         }
27578     },
27579     
27580     onClick : function(e)
27581     {
27582         Roo.log('img onclick');
27583         this.fireEvent('click', this, e);
27584     }
27585    
27586 });
27587
27588  
27589 /*
27590  * - LGPL
27591  *
27592  * numberBox
27593  * 
27594  */
27595 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27596
27597 /**
27598  * @class Roo.bootstrap.dash.NumberBox
27599  * @extends Roo.bootstrap.Component
27600  * Bootstrap NumberBox class
27601  * @cfg {String} headline Box headline
27602  * @cfg {String} content Box content
27603  * @cfg {String} icon Box icon
27604  * @cfg {String} footer Footer text
27605  * @cfg {String} fhref Footer href
27606  * 
27607  * @constructor
27608  * Create a new NumberBox
27609  * @param {Object} config The config object
27610  */
27611
27612
27613 Roo.bootstrap.dash.NumberBox = function(config){
27614     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27615     
27616 };
27617
27618 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27619     
27620     headline : '',
27621     content : '',
27622     icon : '',
27623     footer : '',
27624     fhref : '',
27625     ficon : '',
27626     
27627     getAutoCreate : function(){
27628         
27629         var cfg = {
27630             tag : 'div',
27631             cls : 'small-box ',
27632             cn : [
27633                 {
27634                     tag : 'div',
27635                     cls : 'inner',
27636                     cn :[
27637                         {
27638                             tag : 'h3',
27639                             cls : 'roo-headline',
27640                             html : this.headline
27641                         },
27642                         {
27643                             tag : 'p',
27644                             cls : 'roo-content',
27645                             html : this.content
27646                         }
27647                     ]
27648                 }
27649             ]
27650         };
27651         
27652         if(this.icon){
27653             cfg.cn.push({
27654                 tag : 'div',
27655                 cls : 'icon',
27656                 cn :[
27657                     {
27658                         tag : 'i',
27659                         cls : 'ion ' + this.icon
27660                     }
27661                 ]
27662             });
27663         }
27664         
27665         if(this.footer){
27666             var footer = {
27667                 tag : 'a',
27668                 cls : 'small-box-footer',
27669                 href : this.fhref || '#',
27670                 html : this.footer
27671             };
27672             
27673             cfg.cn.push(footer);
27674             
27675         }
27676         
27677         return  cfg;
27678     },
27679
27680     onRender : function(ct,position){
27681         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27682
27683
27684        
27685                 
27686     },
27687
27688     setHeadline: function (value)
27689     {
27690         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27691     },
27692     
27693     setFooter: function (value, href)
27694     {
27695         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27696         
27697         if(href){
27698             this.el.select('a.small-box-footer',true).first().attr('href', href);
27699         }
27700         
27701     },
27702
27703     setContent: function (value)
27704     {
27705         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27706     },
27707
27708     initEvents: function() 
27709     {   
27710         
27711     }
27712     
27713 });
27714
27715  
27716 /*
27717  * - LGPL
27718  *
27719  * TabBox
27720  * 
27721  */
27722 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27723
27724 /**
27725  * @class Roo.bootstrap.dash.TabBox
27726  * @extends Roo.bootstrap.Component
27727  * Bootstrap TabBox class
27728  * @cfg {String} title Title of the TabBox
27729  * @cfg {String} icon Icon of the TabBox
27730  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27731  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27732  * 
27733  * @constructor
27734  * Create a new TabBox
27735  * @param {Object} config The config object
27736  */
27737
27738
27739 Roo.bootstrap.dash.TabBox = function(config){
27740     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27741     this.addEvents({
27742         // raw events
27743         /**
27744          * @event addpane
27745          * When a pane is added
27746          * @param {Roo.bootstrap.dash.TabPane} pane
27747          */
27748         "addpane" : true,
27749         /**
27750          * @event activatepane
27751          * When a pane is activated
27752          * @param {Roo.bootstrap.dash.TabPane} pane
27753          */
27754         "activatepane" : true
27755         
27756          
27757     });
27758     
27759     this.panes = [];
27760 };
27761
27762 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27763
27764     title : '',
27765     icon : false,
27766     showtabs : true,
27767     tabScrollable : false,
27768     
27769     getChildContainer : function()
27770     {
27771         return this.el.select('.tab-content', true).first();
27772     },
27773     
27774     getAutoCreate : function(){
27775         
27776         var header = {
27777             tag: 'li',
27778             cls: 'pull-left header',
27779             html: this.title,
27780             cn : []
27781         };
27782         
27783         if(this.icon){
27784             header.cn.push({
27785                 tag: 'i',
27786                 cls: 'fa ' + this.icon
27787             });
27788         }
27789         
27790         var h = {
27791             tag: 'ul',
27792             cls: 'nav nav-tabs pull-right',
27793             cn: [
27794                 header
27795             ]
27796         };
27797         
27798         if(this.tabScrollable){
27799             h = {
27800                 tag: 'div',
27801                 cls: 'tab-header',
27802                 cn: [
27803                     {
27804                         tag: 'ul',
27805                         cls: 'nav nav-tabs pull-right',
27806                         cn: [
27807                             header
27808                         ]
27809                     }
27810                 ]
27811             };
27812         }
27813         
27814         var cfg = {
27815             tag: 'div',
27816             cls: 'nav-tabs-custom',
27817             cn: [
27818                 h,
27819                 {
27820                     tag: 'div',
27821                     cls: 'tab-content no-padding',
27822                     cn: []
27823                 }
27824             ]
27825         };
27826
27827         return  cfg;
27828     },
27829     initEvents : function()
27830     {
27831         //Roo.log('add add pane handler');
27832         this.on('addpane', this.onAddPane, this);
27833     },
27834      /**
27835      * Updates the box title
27836      * @param {String} html to set the title to.
27837      */
27838     setTitle : function(value)
27839     {
27840         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27841     },
27842     onAddPane : function(pane)
27843     {
27844         this.panes.push(pane);
27845         //Roo.log('addpane');
27846         //Roo.log(pane);
27847         // tabs are rendere left to right..
27848         if(!this.showtabs){
27849             return;
27850         }
27851         
27852         var ctr = this.el.select('.nav-tabs', true).first();
27853          
27854          
27855         var existing = ctr.select('.nav-tab',true);
27856         var qty = existing.getCount();;
27857         
27858         
27859         var tab = ctr.createChild({
27860             tag : 'li',
27861             cls : 'nav-tab' + (qty ? '' : ' active'),
27862             cn : [
27863                 {
27864                     tag : 'a',
27865                     href:'#',
27866                     html : pane.title
27867                 }
27868             ]
27869         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27870         pane.tab = tab;
27871         
27872         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27873         if (!qty) {
27874             pane.el.addClass('active');
27875         }
27876         
27877                 
27878     },
27879     onTabClick : function(ev,un,ob,pane)
27880     {
27881         //Roo.log('tab - prev default');
27882         ev.preventDefault();
27883         
27884         
27885         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27886         pane.tab.addClass('active');
27887         //Roo.log(pane.title);
27888         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27889         // technically we should have a deactivate event.. but maybe add later.
27890         // and it should not de-activate the selected tab...
27891         this.fireEvent('activatepane', pane);
27892         pane.el.addClass('active');
27893         pane.fireEvent('activate');
27894         
27895         
27896     },
27897     
27898     getActivePane : function()
27899     {
27900         var r = false;
27901         Roo.each(this.panes, function(p) {
27902             if(p.el.hasClass('active')){
27903                 r = p;
27904                 return false;
27905             }
27906             
27907             return;
27908         });
27909         
27910         return r;
27911     }
27912     
27913     
27914 });
27915
27916  
27917 /*
27918  * - LGPL
27919  *
27920  * Tab pane
27921  * 
27922  */
27923 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27924 /**
27925  * @class Roo.bootstrap.TabPane
27926  * @extends Roo.bootstrap.Component
27927  * Bootstrap TabPane class
27928  * @cfg {Boolean} active (false | true) Default false
27929  * @cfg {String} title title of panel
27930
27931  * 
27932  * @constructor
27933  * Create a new TabPane
27934  * @param {Object} config The config object
27935  */
27936
27937 Roo.bootstrap.dash.TabPane = function(config){
27938     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27939     
27940     this.addEvents({
27941         // raw events
27942         /**
27943          * @event activate
27944          * When a pane is activated
27945          * @param {Roo.bootstrap.dash.TabPane} pane
27946          */
27947         "activate" : true
27948          
27949     });
27950 };
27951
27952 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27953     
27954     active : false,
27955     title : '',
27956     
27957     // the tabBox that this is attached to.
27958     tab : false,
27959      
27960     getAutoCreate : function() 
27961     {
27962         var cfg = {
27963             tag: 'div',
27964             cls: 'tab-pane'
27965         };
27966         
27967         if(this.active){
27968             cfg.cls += ' active';
27969         }
27970         
27971         return cfg;
27972     },
27973     initEvents  : function()
27974     {
27975         //Roo.log('trigger add pane handler');
27976         this.parent().fireEvent('addpane', this)
27977     },
27978     
27979      /**
27980      * Updates the tab title 
27981      * @param {String} html to set the title to.
27982      */
27983     setTitle: function(str)
27984     {
27985         if (!this.tab) {
27986             return;
27987         }
27988         this.title = str;
27989         this.tab.select('a', true).first().dom.innerHTML = str;
27990         
27991     }
27992     
27993     
27994     
27995 });
27996
27997  
27998
27999
28000  /*
28001  * - LGPL
28002  *
28003  * menu
28004  * 
28005  */
28006 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28007
28008 /**
28009  * @class Roo.bootstrap.menu.Menu
28010  * @extends Roo.bootstrap.Component
28011  * Bootstrap Menu class - container for Menu
28012  * @cfg {String} html Text of the menu
28013  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28014  * @cfg {String} icon Font awesome icon
28015  * @cfg {String} pos Menu align to (top | bottom) default bottom
28016  * 
28017  * 
28018  * @constructor
28019  * Create a new Menu
28020  * @param {Object} config The config object
28021  */
28022
28023
28024 Roo.bootstrap.menu.Menu = function(config){
28025     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28026     
28027     this.addEvents({
28028         /**
28029          * @event beforeshow
28030          * Fires before this menu is displayed
28031          * @param {Roo.bootstrap.menu.Menu} this
28032          */
28033         beforeshow : true,
28034         /**
28035          * @event beforehide
28036          * Fires before this menu is hidden
28037          * @param {Roo.bootstrap.menu.Menu} this
28038          */
28039         beforehide : true,
28040         /**
28041          * @event show
28042          * Fires after this menu is displayed
28043          * @param {Roo.bootstrap.menu.Menu} this
28044          */
28045         show : true,
28046         /**
28047          * @event hide
28048          * Fires after this menu is hidden
28049          * @param {Roo.bootstrap.menu.Menu} this
28050          */
28051         hide : true,
28052         /**
28053          * @event click
28054          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28055          * @param {Roo.bootstrap.menu.Menu} this
28056          * @param {Roo.EventObject} e
28057          */
28058         click : true
28059     });
28060     
28061 };
28062
28063 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28064     
28065     submenu : false,
28066     html : '',
28067     weight : 'default',
28068     icon : false,
28069     pos : 'bottom',
28070     
28071     
28072     getChildContainer : function() {
28073         if(this.isSubMenu){
28074             return this.el;
28075         }
28076         
28077         return this.el.select('ul.dropdown-menu', true).first();  
28078     },
28079     
28080     getAutoCreate : function()
28081     {
28082         var text = [
28083             {
28084                 tag : 'span',
28085                 cls : 'roo-menu-text',
28086                 html : this.html
28087             }
28088         ];
28089         
28090         if(this.icon){
28091             text.unshift({
28092                 tag : 'i',
28093                 cls : 'fa ' + this.icon
28094             })
28095         }
28096         
28097         
28098         var cfg = {
28099             tag : 'div',
28100             cls : 'btn-group',
28101             cn : [
28102                 {
28103                     tag : 'button',
28104                     cls : 'dropdown-button btn btn-' + this.weight,
28105                     cn : text
28106                 },
28107                 {
28108                     tag : 'button',
28109                     cls : 'dropdown-toggle btn btn-' + this.weight,
28110                     cn : [
28111                         {
28112                             tag : 'span',
28113                             cls : 'caret'
28114                         }
28115                     ]
28116                 },
28117                 {
28118                     tag : 'ul',
28119                     cls : 'dropdown-menu'
28120                 }
28121             ]
28122             
28123         };
28124         
28125         if(this.pos == 'top'){
28126             cfg.cls += ' dropup';
28127         }
28128         
28129         if(this.isSubMenu){
28130             cfg = {
28131                 tag : 'ul',
28132                 cls : 'dropdown-menu'
28133             }
28134         }
28135         
28136         return cfg;
28137     },
28138     
28139     onRender : function(ct, position)
28140     {
28141         this.isSubMenu = ct.hasClass('dropdown-submenu');
28142         
28143         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28144     },
28145     
28146     initEvents : function() 
28147     {
28148         if(this.isSubMenu){
28149             return;
28150         }
28151         
28152         this.hidden = true;
28153         
28154         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28155         this.triggerEl.on('click', this.onTriggerPress, this);
28156         
28157         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28158         this.buttonEl.on('click', this.onClick, this);
28159         
28160     },
28161     
28162     list : function()
28163     {
28164         if(this.isSubMenu){
28165             return this.el;
28166         }
28167         
28168         return this.el.select('ul.dropdown-menu', true).first();
28169     },
28170     
28171     onClick : function(e)
28172     {
28173         this.fireEvent("click", this, e);
28174     },
28175     
28176     onTriggerPress  : function(e)
28177     {   
28178         if (this.isVisible()) {
28179             this.hide();
28180         } else {
28181             this.show();
28182         }
28183     },
28184     
28185     isVisible : function(){
28186         return !this.hidden;
28187     },
28188     
28189     show : function()
28190     {
28191         this.fireEvent("beforeshow", this);
28192         
28193         this.hidden = false;
28194         this.el.addClass('open');
28195         
28196         Roo.get(document).on("mouseup", this.onMouseUp, this);
28197         
28198         this.fireEvent("show", this);
28199         
28200         
28201     },
28202     
28203     hide : function()
28204     {
28205         this.fireEvent("beforehide", this);
28206         
28207         this.hidden = true;
28208         this.el.removeClass('open');
28209         
28210         Roo.get(document).un("mouseup", this.onMouseUp);
28211         
28212         this.fireEvent("hide", this);
28213     },
28214     
28215     onMouseUp : function()
28216     {
28217         this.hide();
28218     }
28219     
28220 });
28221
28222  
28223  /*
28224  * - LGPL
28225  *
28226  * menu item
28227  * 
28228  */
28229 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28230
28231 /**
28232  * @class Roo.bootstrap.menu.Item
28233  * @extends Roo.bootstrap.Component
28234  * Bootstrap MenuItem class
28235  * @cfg {Boolean} submenu (true | false) default false
28236  * @cfg {String} html text of the item
28237  * @cfg {String} href the link
28238  * @cfg {Boolean} disable (true | false) default false
28239  * @cfg {Boolean} preventDefault (true | false) default true
28240  * @cfg {String} icon Font awesome icon
28241  * @cfg {String} pos Submenu align to (left | right) default right 
28242  * 
28243  * 
28244  * @constructor
28245  * Create a new Item
28246  * @param {Object} config The config object
28247  */
28248
28249
28250 Roo.bootstrap.menu.Item = function(config){
28251     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28252     this.addEvents({
28253         /**
28254          * @event mouseover
28255          * Fires when the mouse is hovering over this menu
28256          * @param {Roo.bootstrap.menu.Item} this
28257          * @param {Roo.EventObject} e
28258          */
28259         mouseover : true,
28260         /**
28261          * @event mouseout
28262          * Fires when the mouse exits this menu
28263          * @param {Roo.bootstrap.menu.Item} this
28264          * @param {Roo.EventObject} e
28265          */
28266         mouseout : true,
28267         // raw events
28268         /**
28269          * @event click
28270          * The raw click event for the entire grid.
28271          * @param {Roo.EventObject} e
28272          */
28273         click : true
28274     });
28275 };
28276
28277 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28278     
28279     submenu : false,
28280     href : '',
28281     html : '',
28282     preventDefault: true,
28283     disable : false,
28284     icon : false,
28285     pos : 'right',
28286     
28287     getAutoCreate : function()
28288     {
28289         var text = [
28290             {
28291                 tag : 'span',
28292                 cls : 'roo-menu-item-text',
28293                 html : this.html
28294             }
28295         ];
28296         
28297         if(this.icon){
28298             text.unshift({
28299                 tag : 'i',
28300                 cls : 'fa ' + this.icon
28301             })
28302         }
28303         
28304         var cfg = {
28305             tag : 'li',
28306             cn : [
28307                 {
28308                     tag : 'a',
28309                     href : this.href || '#',
28310                     cn : text
28311                 }
28312             ]
28313         };
28314         
28315         if(this.disable){
28316             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28317         }
28318         
28319         if(this.submenu){
28320             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28321             
28322             if(this.pos == 'left'){
28323                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28324             }
28325         }
28326         
28327         return cfg;
28328     },
28329     
28330     initEvents : function() 
28331     {
28332         this.el.on('mouseover', this.onMouseOver, this);
28333         this.el.on('mouseout', this.onMouseOut, this);
28334         
28335         this.el.select('a', true).first().on('click', this.onClick, this);
28336         
28337     },
28338     
28339     onClick : function(e)
28340     {
28341         if(this.preventDefault){
28342             e.preventDefault();
28343         }
28344         
28345         this.fireEvent("click", this, e);
28346     },
28347     
28348     onMouseOver : function(e)
28349     {
28350         if(this.submenu && this.pos == 'left'){
28351             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28352         }
28353         
28354         this.fireEvent("mouseover", this, e);
28355     },
28356     
28357     onMouseOut : function(e)
28358     {
28359         this.fireEvent("mouseout", this, e);
28360     }
28361 });
28362
28363  
28364
28365  /*
28366  * - LGPL
28367  *
28368  * menu separator
28369  * 
28370  */
28371 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28372
28373 /**
28374  * @class Roo.bootstrap.menu.Separator
28375  * @extends Roo.bootstrap.Component
28376  * Bootstrap Separator class
28377  * 
28378  * @constructor
28379  * Create a new Separator
28380  * @param {Object} config The config object
28381  */
28382
28383
28384 Roo.bootstrap.menu.Separator = function(config){
28385     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28386 };
28387
28388 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28389     
28390     getAutoCreate : function(){
28391         var cfg = {
28392             tag : 'li',
28393             cls: 'divider'
28394         };
28395         
28396         return cfg;
28397     }
28398    
28399 });
28400
28401  
28402
28403  /*
28404  * - LGPL
28405  *
28406  * Tooltip
28407  * 
28408  */
28409
28410 /**
28411  * @class Roo.bootstrap.Tooltip
28412  * Bootstrap Tooltip class
28413  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28414  * to determine which dom element triggers the tooltip.
28415  * 
28416  * It needs to add support for additional attributes like tooltip-position
28417  * 
28418  * @constructor
28419  * Create a new Toolti
28420  * @param {Object} config The config object
28421  */
28422
28423 Roo.bootstrap.Tooltip = function(config){
28424     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28425     
28426     this.alignment = Roo.bootstrap.Tooltip.alignment;
28427     
28428     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28429         this.alignment = config.alignment;
28430     }
28431     
28432 };
28433
28434 Roo.apply(Roo.bootstrap.Tooltip, {
28435     /**
28436      * @function init initialize tooltip monitoring.
28437      * @static
28438      */
28439     currentEl : false,
28440     currentTip : false,
28441     currentRegion : false,
28442     
28443     //  init : delay?
28444     
28445     init : function()
28446     {
28447         Roo.get(document).on('mouseover', this.enter ,this);
28448         Roo.get(document).on('mouseout', this.leave, this);
28449          
28450         
28451         this.currentTip = new Roo.bootstrap.Tooltip();
28452     },
28453     
28454     enter : function(ev)
28455     {
28456         var dom = ev.getTarget();
28457         
28458         //Roo.log(['enter',dom]);
28459         var el = Roo.fly(dom);
28460         if (this.currentEl) {
28461             //Roo.log(dom);
28462             //Roo.log(this.currentEl);
28463             //Roo.log(this.currentEl.contains(dom));
28464             if (this.currentEl == el) {
28465                 return;
28466             }
28467             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28468                 return;
28469             }
28470
28471         }
28472         
28473         if (this.currentTip.el) {
28474             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28475         }    
28476         //Roo.log(ev);
28477         
28478         if(!el || el.dom == document){
28479             return;
28480         }
28481         
28482         var bindEl = el;
28483         
28484         // you can not look for children, as if el is the body.. then everythign is the child..
28485         if (!el.attr('tooltip')) { //
28486             if (!el.select("[tooltip]").elements.length) {
28487                 return;
28488             }
28489             // is the mouse over this child...?
28490             bindEl = el.select("[tooltip]").first();
28491             var xy = ev.getXY();
28492             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28493                 //Roo.log("not in region.");
28494                 return;
28495             }
28496             //Roo.log("child element over..");
28497             
28498         }
28499         this.currentEl = bindEl;
28500         this.currentTip.bind(bindEl);
28501         this.currentRegion = Roo.lib.Region.getRegion(dom);
28502         this.currentTip.enter();
28503         
28504     },
28505     leave : function(ev)
28506     {
28507         var dom = ev.getTarget();
28508         //Roo.log(['leave',dom]);
28509         if (!this.currentEl) {
28510             return;
28511         }
28512         
28513         
28514         if (dom != this.currentEl.dom) {
28515             return;
28516         }
28517         var xy = ev.getXY();
28518         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28519             return;
28520         }
28521         // only activate leave if mouse cursor is outside... bounding box..
28522         
28523         
28524         
28525         
28526         if (this.currentTip) {
28527             this.currentTip.leave();
28528         }
28529         //Roo.log('clear currentEl');
28530         this.currentEl = false;
28531         
28532         
28533     },
28534     alignment : {
28535         'left' : ['r-l', [-2,0], 'right'],
28536         'right' : ['l-r', [2,0], 'left'],
28537         'bottom' : ['t-b', [0,2], 'top'],
28538         'top' : [ 'b-t', [0,-2], 'bottom']
28539     }
28540     
28541 });
28542
28543
28544 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28545     
28546     
28547     bindEl : false,
28548     
28549     delay : null, // can be { show : 300 , hide: 500}
28550     
28551     timeout : null,
28552     
28553     hoverState : null, //???
28554     
28555     placement : 'bottom', 
28556     
28557     alignment : false,
28558     
28559     getAutoCreate : function(){
28560     
28561         var cfg = {
28562            cls : 'tooltip',   
28563            role : 'tooltip',
28564            cn : [
28565                 {
28566                     cls : 'tooltip-arrow arrow'
28567                 },
28568                 {
28569                     cls : 'tooltip-inner'
28570                 }
28571            ]
28572         };
28573         
28574         return cfg;
28575     },
28576     bind : function(el)
28577     {
28578         this.bindEl = el;
28579     },
28580     
28581     initEvents : function()
28582     {
28583         this.arrowEl = this.el.select('.arrow', true).first();
28584         this.innerEl = this.el.select('.tooltip-inner', true).first();
28585     },
28586     
28587     enter : function () {
28588        
28589         if (this.timeout != null) {
28590             clearTimeout(this.timeout);
28591         }
28592         
28593         this.hoverState = 'in';
28594          //Roo.log("enter - show");
28595         if (!this.delay || !this.delay.show) {
28596             this.show();
28597             return;
28598         }
28599         var _t = this;
28600         this.timeout = setTimeout(function () {
28601             if (_t.hoverState == 'in') {
28602                 _t.show();
28603             }
28604         }, this.delay.show);
28605     },
28606     leave : function()
28607     {
28608         clearTimeout(this.timeout);
28609     
28610         this.hoverState = 'out';
28611          if (!this.delay || !this.delay.hide) {
28612             this.hide();
28613             return;
28614         }
28615        
28616         var _t = this;
28617         this.timeout = setTimeout(function () {
28618             //Roo.log("leave - timeout");
28619             
28620             if (_t.hoverState == 'out') {
28621                 _t.hide();
28622                 Roo.bootstrap.Tooltip.currentEl = false;
28623             }
28624         }, delay);
28625     },
28626     
28627     show : function (msg)
28628     {
28629         if (!this.el) {
28630             this.render(document.body);
28631         }
28632         // set content.
28633         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28634         
28635         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28636         
28637         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28638         
28639         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28640                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28641         
28642         var placement = typeof this.placement == 'function' ?
28643             this.placement.call(this, this.el, on_el) :
28644             this.placement;
28645             
28646         var autoToken = /\s?auto?\s?/i;
28647         var autoPlace = autoToken.test(placement);
28648         if (autoPlace) {
28649             placement = placement.replace(autoToken, '') || 'top';
28650         }
28651         
28652         //this.el.detach()
28653         //this.el.setXY([0,0]);
28654         this.el.show();
28655         //this.el.dom.style.display='block';
28656         
28657         //this.el.appendTo(on_el);
28658         
28659         var p = this.getPosition();
28660         var box = this.el.getBox();
28661         
28662         if (autoPlace) {
28663             // fixme..
28664         }
28665         
28666         var align = this.alignment[placement];
28667         
28668         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28669         
28670         if(placement == 'top' || placement == 'bottom'){
28671             if(xy[0] < 0){
28672                 placement = 'right';
28673             }
28674             
28675             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28676                 placement = 'left';
28677             }
28678             
28679             var scroll = Roo.select('body', true).first().getScroll();
28680             
28681             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28682                 placement = 'top';
28683             }
28684             
28685             align = this.alignment[placement];
28686             
28687             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28688             
28689         }
28690         
28691         this.el.alignTo(this.bindEl, align[0],align[1]);
28692         //var arrow = this.el.select('.arrow',true).first();
28693         //arrow.set(align[2], 
28694         
28695         this.el.addClass(placement);
28696         this.el.addClass("bs-tooltip-"+ placement);
28697         
28698         this.el.addClass('in fade show');
28699         
28700         this.hoverState = null;
28701         
28702         if (this.el.hasClass('fade')) {
28703             // fade it?
28704         }
28705         
28706         
28707         
28708         
28709         
28710     },
28711     hide : function()
28712     {
28713          
28714         if (!this.el) {
28715             return;
28716         }
28717         //this.el.setXY([0,0]);
28718         this.el.removeClass(['show', 'in']);
28719         //this.el.hide();
28720         
28721     }
28722     
28723 });
28724  
28725
28726  /*
28727  * - LGPL
28728  *
28729  * Location Picker
28730  * 
28731  */
28732
28733 /**
28734  * @class Roo.bootstrap.LocationPicker
28735  * @extends Roo.bootstrap.Component
28736  * Bootstrap LocationPicker class
28737  * @cfg {Number} latitude Position when init default 0
28738  * @cfg {Number} longitude Position when init default 0
28739  * @cfg {Number} zoom default 15
28740  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28741  * @cfg {Boolean} mapTypeControl default false
28742  * @cfg {Boolean} disableDoubleClickZoom default false
28743  * @cfg {Boolean} scrollwheel default true
28744  * @cfg {Boolean} streetViewControl default false
28745  * @cfg {Number} radius default 0
28746  * @cfg {String} locationName
28747  * @cfg {Boolean} draggable default true
28748  * @cfg {Boolean} enableAutocomplete default false
28749  * @cfg {Boolean} enableReverseGeocode default true
28750  * @cfg {String} markerTitle
28751  * 
28752  * @constructor
28753  * Create a new LocationPicker
28754  * @param {Object} config The config object
28755  */
28756
28757
28758 Roo.bootstrap.LocationPicker = function(config){
28759     
28760     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28761     
28762     this.addEvents({
28763         /**
28764          * @event initial
28765          * Fires when the picker initialized.
28766          * @param {Roo.bootstrap.LocationPicker} this
28767          * @param {Google Location} location
28768          */
28769         initial : true,
28770         /**
28771          * @event positionchanged
28772          * Fires when the picker position changed.
28773          * @param {Roo.bootstrap.LocationPicker} this
28774          * @param {Google Location} location
28775          */
28776         positionchanged : true,
28777         /**
28778          * @event resize
28779          * Fires when the map resize.
28780          * @param {Roo.bootstrap.LocationPicker} this
28781          */
28782         resize : true,
28783         /**
28784          * @event show
28785          * Fires when the map show.
28786          * @param {Roo.bootstrap.LocationPicker} this
28787          */
28788         show : true,
28789         /**
28790          * @event hide
28791          * Fires when the map hide.
28792          * @param {Roo.bootstrap.LocationPicker} this
28793          */
28794         hide : true,
28795         /**
28796          * @event mapClick
28797          * Fires when click the map.
28798          * @param {Roo.bootstrap.LocationPicker} this
28799          * @param {Map event} e
28800          */
28801         mapClick : true,
28802         /**
28803          * @event mapRightClick
28804          * Fires when right click the map.
28805          * @param {Roo.bootstrap.LocationPicker} this
28806          * @param {Map event} e
28807          */
28808         mapRightClick : true,
28809         /**
28810          * @event markerClick
28811          * Fires when click the marker.
28812          * @param {Roo.bootstrap.LocationPicker} this
28813          * @param {Map event} e
28814          */
28815         markerClick : true,
28816         /**
28817          * @event markerRightClick
28818          * Fires when right click the marker.
28819          * @param {Roo.bootstrap.LocationPicker} this
28820          * @param {Map event} e
28821          */
28822         markerRightClick : true,
28823         /**
28824          * @event OverlayViewDraw
28825          * Fires when OverlayView Draw
28826          * @param {Roo.bootstrap.LocationPicker} this
28827          */
28828         OverlayViewDraw : true,
28829         /**
28830          * @event OverlayViewOnAdd
28831          * Fires when OverlayView Draw
28832          * @param {Roo.bootstrap.LocationPicker} this
28833          */
28834         OverlayViewOnAdd : true,
28835         /**
28836          * @event OverlayViewOnRemove
28837          * Fires when OverlayView Draw
28838          * @param {Roo.bootstrap.LocationPicker} this
28839          */
28840         OverlayViewOnRemove : true,
28841         /**
28842          * @event OverlayViewShow
28843          * Fires when OverlayView Draw
28844          * @param {Roo.bootstrap.LocationPicker} this
28845          * @param {Pixel} cpx
28846          */
28847         OverlayViewShow : true,
28848         /**
28849          * @event OverlayViewHide
28850          * Fires when OverlayView Draw
28851          * @param {Roo.bootstrap.LocationPicker} this
28852          */
28853         OverlayViewHide : true,
28854         /**
28855          * @event loadexception
28856          * Fires when load google lib failed.
28857          * @param {Roo.bootstrap.LocationPicker} this
28858          */
28859         loadexception : true
28860     });
28861         
28862 };
28863
28864 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28865     
28866     gMapContext: false,
28867     
28868     latitude: 0,
28869     longitude: 0,
28870     zoom: 15,
28871     mapTypeId: false,
28872     mapTypeControl: false,
28873     disableDoubleClickZoom: false,
28874     scrollwheel: true,
28875     streetViewControl: false,
28876     radius: 0,
28877     locationName: '',
28878     draggable: true,
28879     enableAutocomplete: false,
28880     enableReverseGeocode: true,
28881     markerTitle: '',
28882     
28883     getAutoCreate: function()
28884     {
28885
28886         var cfg = {
28887             tag: 'div',
28888             cls: 'roo-location-picker'
28889         };
28890         
28891         return cfg
28892     },
28893     
28894     initEvents: function(ct, position)
28895     {       
28896         if(!this.el.getWidth() || this.isApplied()){
28897             return;
28898         }
28899         
28900         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28901         
28902         this.initial();
28903     },
28904     
28905     initial: function()
28906     {
28907         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28908             this.fireEvent('loadexception', this);
28909             return;
28910         }
28911         
28912         if(!this.mapTypeId){
28913             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28914         }
28915         
28916         this.gMapContext = this.GMapContext();
28917         
28918         this.initOverlayView();
28919         
28920         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28921         
28922         var _this = this;
28923                 
28924         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28925             _this.setPosition(_this.gMapContext.marker.position);
28926         });
28927         
28928         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28929             _this.fireEvent('mapClick', this, event);
28930             
28931         });
28932
28933         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28934             _this.fireEvent('mapRightClick', this, event);
28935             
28936         });
28937         
28938         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28939             _this.fireEvent('markerClick', this, event);
28940             
28941         });
28942
28943         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28944             _this.fireEvent('markerRightClick', this, event);
28945             
28946         });
28947         
28948         this.setPosition(this.gMapContext.location);
28949         
28950         this.fireEvent('initial', this, this.gMapContext.location);
28951     },
28952     
28953     initOverlayView: function()
28954     {
28955         var _this = this;
28956         
28957         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28958             
28959             draw: function()
28960             {
28961                 _this.fireEvent('OverlayViewDraw', _this);
28962             },
28963             
28964             onAdd: function()
28965             {
28966                 _this.fireEvent('OverlayViewOnAdd', _this);
28967             },
28968             
28969             onRemove: function()
28970             {
28971                 _this.fireEvent('OverlayViewOnRemove', _this);
28972             },
28973             
28974             show: function(cpx)
28975             {
28976                 _this.fireEvent('OverlayViewShow', _this, cpx);
28977             },
28978             
28979             hide: function()
28980             {
28981                 _this.fireEvent('OverlayViewHide', _this);
28982             }
28983             
28984         });
28985     },
28986     
28987     fromLatLngToContainerPixel: function(event)
28988     {
28989         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28990     },
28991     
28992     isApplied: function() 
28993     {
28994         return this.getGmapContext() == false ? false : true;
28995     },
28996     
28997     getGmapContext: function() 
28998     {
28999         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29000     },
29001     
29002     GMapContext: function() 
29003     {
29004         var position = new google.maps.LatLng(this.latitude, this.longitude);
29005         
29006         var _map = new google.maps.Map(this.el.dom, {
29007             center: position,
29008             zoom: this.zoom,
29009             mapTypeId: this.mapTypeId,
29010             mapTypeControl: this.mapTypeControl,
29011             disableDoubleClickZoom: this.disableDoubleClickZoom,
29012             scrollwheel: this.scrollwheel,
29013             streetViewControl: this.streetViewControl,
29014             locationName: this.locationName,
29015             draggable: this.draggable,
29016             enableAutocomplete: this.enableAutocomplete,
29017             enableReverseGeocode: this.enableReverseGeocode
29018         });
29019         
29020         var _marker = new google.maps.Marker({
29021             position: position,
29022             map: _map,
29023             title: this.markerTitle,
29024             draggable: this.draggable
29025         });
29026         
29027         return {
29028             map: _map,
29029             marker: _marker,
29030             circle: null,
29031             location: position,
29032             radius: this.radius,
29033             locationName: this.locationName,
29034             addressComponents: {
29035                 formatted_address: null,
29036                 addressLine1: null,
29037                 addressLine2: null,
29038                 streetName: null,
29039                 streetNumber: null,
29040                 city: null,
29041                 district: null,
29042                 state: null,
29043                 stateOrProvince: null
29044             },
29045             settings: this,
29046             domContainer: this.el.dom,
29047             geodecoder: new google.maps.Geocoder()
29048         };
29049     },
29050     
29051     drawCircle: function(center, radius, options) 
29052     {
29053         if (this.gMapContext.circle != null) {
29054             this.gMapContext.circle.setMap(null);
29055         }
29056         if (radius > 0) {
29057             radius *= 1;
29058             options = Roo.apply({}, options, {
29059                 strokeColor: "#0000FF",
29060                 strokeOpacity: .35,
29061                 strokeWeight: 2,
29062                 fillColor: "#0000FF",
29063                 fillOpacity: .2
29064             });
29065             
29066             options.map = this.gMapContext.map;
29067             options.radius = radius;
29068             options.center = center;
29069             this.gMapContext.circle = new google.maps.Circle(options);
29070             return this.gMapContext.circle;
29071         }
29072         
29073         return null;
29074     },
29075     
29076     setPosition: function(location) 
29077     {
29078         this.gMapContext.location = location;
29079         this.gMapContext.marker.setPosition(location);
29080         this.gMapContext.map.panTo(location);
29081         this.drawCircle(location, this.gMapContext.radius, {});
29082         
29083         var _this = this;
29084         
29085         if (this.gMapContext.settings.enableReverseGeocode) {
29086             this.gMapContext.geodecoder.geocode({
29087                 latLng: this.gMapContext.location
29088             }, function(results, status) {
29089                 
29090                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29091                     _this.gMapContext.locationName = results[0].formatted_address;
29092                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29093                     
29094                     _this.fireEvent('positionchanged', this, location);
29095                 }
29096             });
29097             
29098             return;
29099         }
29100         
29101         this.fireEvent('positionchanged', this, location);
29102     },
29103     
29104     resize: function()
29105     {
29106         google.maps.event.trigger(this.gMapContext.map, "resize");
29107         
29108         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29109         
29110         this.fireEvent('resize', this);
29111     },
29112     
29113     setPositionByLatLng: function(latitude, longitude)
29114     {
29115         this.setPosition(new google.maps.LatLng(latitude, longitude));
29116     },
29117     
29118     getCurrentPosition: function() 
29119     {
29120         return {
29121             latitude: this.gMapContext.location.lat(),
29122             longitude: this.gMapContext.location.lng()
29123         };
29124     },
29125     
29126     getAddressName: function() 
29127     {
29128         return this.gMapContext.locationName;
29129     },
29130     
29131     getAddressComponents: function() 
29132     {
29133         return this.gMapContext.addressComponents;
29134     },
29135     
29136     address_component_from_google_geocode: function(address_components) 
29137     {
29138         var result = {};
29139         
29140         for (var i = 0; i < address_components.length; i++) {
29141             var component = address_components[i];
29142             if (component.types.indexOf("postal_code") >= 0) {
29143                 result.postalCode = component.short_name;
29144             } else if (component.types.indexOf("street_number") >= 0) {
29145                 result.streetNumber = component.short_name;
29146             } else if (component.types.indexOf("route") >= 0) {
29147                 result.streetName = component.short_name;
29148             } else if (component.types.indexOf("neighborhood") >= 0) {
29149                 result.city = component.short_name;
29150             } else if (component.types.indexOf("locality") >= 0) {
29151                 result.city = component.short_name;
29152             } else if (component.types.indexOf("sublocality") >= 0) {
29153                 result.district = component.short_name;
29154             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29155                 result.stateOrProvince = component.short_name;
29156             } else if (component.types.indexOf("country") >= 0) {
29157                 result.country = component.short_name;
29158             }
29159         }
29160         
29161         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29162         result.addressLine2 = "";
29163         return result;
29164     },
29165     
29166     setZoomLevel: function(zoom)
29167     {
29168         this.gMapContext.map.setZoom(zoom);
29169     },
29170     
29171     show: function()
29172     {
29173         if(!this.el){
29174             return;
29175         }
29176         
29177         this.el.show();
29178         
29179         this.resize();
29180         
29181         this.fireEvent('show', this);
29182     },
29183     
29184     hide: function()
29185     {
29186         if(!this.el){
29187             return;
29188         }
29189         
29190         this.el.hide();
29191         
29192         this.fireEvent('hide', this);
29193     }
29194     
29195 });
29196
29197 Roo.apply(Roo.bootstrap.LocationPicker, {
29198     
29199     OverlayView : function(map, options)
29200     {
29201         options = options || {};
29202         
29203         this.setMap(map);
29204     }
29205     
29206     
29207 });/**
29208  * @class Roo.bootstrap.Alert
29209  * @extends Roo.bootstrap.Component
29210  * Bootstrap Alert class - shows an alert area box
29211  * eg
29212  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29213   Enter a valid email address
29214 </div>
29215  * @licence LGPL
29216  * @cfg {String} title The title of alert
29217  * @cfg {String} html The content of alert
29218  * @cfg {String} weight (  success | info | warning | danger )
29219  * @cfg {String} faicon font-awesomeicon
29220  * 
29221  * @constructor
29222  * Create a new alert
29223  * @param {Object} config The config object
29224  */
29225
29226
29227 Roo.bootstrap.Alert = function(config){
29228     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29229     
29230 };
29231
29232 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29233     
29234     title: '',
29235     html: '',
29236     weight: false,
29237     faicon: false,
29238     
29239     getAutoCreate : function()
29240     {
29241         
29242         var cfg = {
29243             tag : 'div',
29244             cls : 'alert',
29245             cn : [
29246                 {
29247                     tag : 'i',
29248                     cls : 'roo-alert-icon'
29249                     
29250                 },
29251                 {
29252                     tag : 'b',
29253                     cls : 'roo-alert-title',
29254                     html : this.title
29255                 },
29256                 {
29257                     tag : 'span',
29258                     cls : 'roo-alert-text',
29259                     html : this.html
29260                 }
29261             ]
29262         };
29263         
29264         if(this.faicon){
29265             cfg.cn[0].cls += ' fa ' + this.faicon;
29266         }
29267         
29268         if(this.weight){
29269             cfg.cls += ' alert-' + this.weight;
29270         }
29271         
29272         return cfg;
29273     },
29274     
29275     initEvents: function() 
29276     {
29277         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29278     },
29279     
29280     setTitle : function(str)
29281     {
29282         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29283     },
29284     
29285     setText : function(str)
29286     {
29287         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29288     },
29289     
29290     setWeight : function(weight)
29291     {
29292         if(this.weight){
29293             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29294         }
29295         
29296         this.weight = weight;
29297         
29298         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29299     },
29300     
29301     setIcon : function(icon)
29302     {
29303         if(this.faicon){
29304             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29305         }
29306         
29307         this.faicon = icon;
29308         
29309         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29310     },
29311     
29312     hide: function() 
29313     {
29314         this.el.hide();   
29315     },
29316     
29317     show: function() 
29318     {  
29319         this.el.show();   
29320     }
29321     
29322 });
29323
29324  
29325 /*
29326 * Licence: LGPL
29327 */
29328
29329 /**
29330  * @class Roo.bootstrap.UploadCropbox
29331  * @extends Roo.bootstrap.Component
29332  * Bootstrap UploadCropbox class
29333  * @cfg {String} emptyText show when image has been loaded
29334  * @cfg {String} rotateNotify show when image too small to rotate
29335  * @cfg {Number} errorTimeout default 3000
29336  * @cfg {Number} minWidth default 300
29337  * @cfg {Number} minHeight default 300
29338  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29339  * @cfg {Boolean} isDocument (true|false) default false
29340  * @cfg {String} url action url
29341  * @cfg {String} paramName default 'imageUpload'
29342  * @cfg {String} method default POST
29343  * @cfg {Boolean} loadMask (true|false) default true
29344  * @cfg {Boolean} loadingText default 'Loading...'
29345  * 
29346  * @constructor
29347  * Create a new UploadCropbox
29348  * @param {Object} config The config object
29349  */
29350
29351 Roo.bootstrap.UploadCropbox = function(config){
29352     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29353     
29354     this.addEvents({
29355         /**
29356          * @event beforeselectfile
29357          * Fire before select file
29358          * @param {Roo.bootstrap.UploadCropbox} this
29359          */
29360         "beforeselectfile" : true,
29361         /**
29362          * @event initial
29363          * Fire after initEvent
29364          * @param {Roo.bootstrap.UploadCropbox} this
29365          */
29366         "initial" : true,
29367         /**
29368          * @event crop
29369          * Fire after initEvent
29370          * @param {Roo.bootstrap.UploadCropbox} this
29371          * @param {String} data
29372          */
29373         "crop" : true,
29374         /**
29375          * @event prepare
29376          * Fire when preparing the file data
29377          * @param {Roo.bootstrap.UploadCropbox} this
29378          * @param {Object} file
29379          */
29380         "prepare" : true,
29381         /**
29382          * @event exception
29383          * Fire when get exception
29384          * @param {Roo.bootstrap.UploadCropbox} this
29385          * @param {XMLHttpRequest} xhr
29386          */
29387         "exception" : true,
29388         /**
29389          * @event beforeloadcanvas
29390          * Fire before load the canvas
29391          * @param {Roo.bootstrap.UploadCropbox} this
29392          * @param {String} src
29393          */
29394         "beforeloadcanvas" : true,
29395         /**
29396          * @event trash
29397          * Fire when trash image
29398          * @param {Roo.bootstrap.UploadCropbox} this
29399          */
29400         "trash" : true,
29401         /**
29402          * @event download
29403          * Fire when download the image
29404          * @param {Roo.bootstrap.UploadCropbox} this
29405          */
29406         "download" : true,
29407         /**
29408          * @event footerbuttonclick
29409          * Fire when footerbuttonclick
29410          * @param {Roo.bootstrap.UploadCropbox} this
29411          * @param {String} type
29412          */
29413         "footerbuttonclick" : true,
29414         /**
29415          * @event resize
29416          * Fire when resize
29417          * @param {Roo.bootstrap.UploadCropbox} this
29418          */
29419         "resize" : true,
29420         /**
29421          * @event rotate
29422          * Fire when rotate the image
29423          * @param {Roo.bootstrap.UploadCropbox} this
29424          * @param {String} pos
29425          */
29426         "rotate" : true,
29427         /**
29428          * @event inspect
29429          * Fire when inspect the file
29430          * @param {Roo.bootstrap.UploadCropbox} this
29431          * @param {Object} file
29432          */
29433         "inspect" : true,
29434         /**
29435          * @event upload
29436          * Fire when xhr upload the file
29437          * @param {Roo.bootstrap.UploadCropbox} this
29438          * @param {Object} data
29439          */
29440         "upload" : true,
29441         /**
29442          * @event arrange
29443          * Fire when arrange the file data
29444          * @param {Roo.bootstrap.UploadCropbox} this
29445          * @param {Object} formData
29446          */
29447         "arrange" : true
29448     });
29449     
29450     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29451 };
29452
29453 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29454     
29455     emptyText : 'Click to upload image',
29456     rotateNotify : 'Image is too small to rotate',
29457     errorTimeout : 3000,
29458     scale : 0,
29459     baseScale : 1,
29460     rotate : 0,
29461     dragable : false,
29462     pinching : false,
29463     mouseX : 0,
29464     mouseY : 0,
29465     cropData : false,
29466     minWidth : 300,
29467     minHeight : 300,
29468     file : false,
29469     exif : {},
29470     baseRotate : 1,
29471     cropType : 'image/jpeg',
29472     buttons : false,
29473     canvasLoaded : false,
29474     isDocument : false,
29475     method : 'POST',
29476     paramName : 'imageUpload',
29477     loadMask : true,
29478     loadingText : 'Loading...',
29479     maskEl : false,
29480     
29481     getAutoCreate : function()
29482     {
29483         var cfg = {
29484             tag : 'div',
29485             cls : 'roo-upload-cropbox',
29486             cn : [
29487                 {
29488                     tag : 'input',
29489                     cls : 'roo-upload-cropbox-selector',
29490                     type : 'file'
29491                 },
29492                 {
29493                     tag : 'div',
29494                     cls : 'roo-upload-cropbox-body',
29495                     style : 'cursor:pointer',
29496                     cn : [
29497                         {
29498                             tag : 'div',
29499                             cls : 'roo-upload-cropbox-preview'
29500                         },
29501                         {
29502                             tag : 'div',
29503                             cls : 'roo-upload-cropbox-thumb'
29504                         },
29505                         {
29506                             tag : 'div',
29507                             cls : 'roo-upload-cropbox-empty-notify',
29508                             html : this.emptyText
29509                         },
29510                         {
29511                             tag : 'div',
29512                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29513                             html : this.rotateNotify
29514                         }
29515                     ]
29516                 },
29517                 {
29518                     tag : 'div',
29519                     cls : 'roo-upload-cropbox-footer',
29520                     cn : {
29521                         tag : 'div',
29522                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29523                         cn : []
29524                     }
29525                 }
29526             ]
29527         };
29528         
29529         return cfg;
29530     },
29531     
29532     onRender : function(ct, position)
29533     {
29534         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29535         
29536         if (this.buttons.length) {
29537             
29538             Roo.each(this.buttons, function(bb) {
29539                 
29540                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29541                 
29542                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29543                 
29544             }, this);
29545         }
29546         
29547         if(this.loadMask){
29548             this.maskEl = this.el;
29549         }
29550     },
29551     
29552     initEvents : function()
29553     {
29554         this.urlAPI = (window.createObjectURL && window) || 
29555                                 (window.URL && URL.revokeObjectURL && URL) || 
29556                                 (window.webkitURL && webkitURL);
29557                         
29558         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29559         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29560         
29561         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29562         this.selectorEl.hide();
29563         
29564         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29565         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29566         
29567         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29568         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29569         this.thumbEl.hide();
29570         
29571         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29572         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29573         
29574         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29575         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29576         this.errorEl.hide();
29577         
29578         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29579         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29580         this.footerEl.hide();
29581         
29582         this.setThumbBoxSize();
29583         
29584         this.bind();
29585         
29586         this.resize();
29587         
29588         this.fireEvent('initial', this);
29589     },
29590
29591     bind : function()
29592     {
29593         var _this = this;
29594         
29595         window.addEventListener("resize", function() { _this.resize(); } );
29596         
29597         this.bodyEl.on('click', this.beforeSelectFile, this);
29598         
29599         if(Roo.isTouch){
29600             this.bodyEl.on('touchstart', this.onTouchStart, this);
29601             this.bodyEl.on('touchmove', this.onTouchMove, this);
29602             this.bodyEl.on('touchend', this.onTouchEnd, this);
29603         }
29604         
29605         if(!Roo.isTouch){
29606             this.bodyEl.on('mousedown', this.onMouseDown, this);
29607             this.bodyEl.on('mousemove', this.onMouseMove, this);
29608             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29609             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29610             Roo.get(document).on('mouseup', this.onMouseUp, this);
29611         }
29612         
29613         this.selectorEl.on('change', this.onFileSelected, this);
29614     },
29615     
29616     reset : function()
29617     {    
29618         this.scale = 0;
29619         this.baseScale = 1;
29620         this.rotate = 0;
29621         this.baseRotate = 1;
29622         this.dragable = false;
29623         this.pinching = false;
29624         this.mouseX = 0;
29625         this.mouseY = 0;
29626         this.cropData = false;
29627         this.notifyEl.dom.innerHTML = this.emptyText;
29628         
29629         this.selectorEl.dom.value = '';
29630         
29631     },
29632     
29633     resize : function()
29634     {
29635         if(this.fireEvent('resize', this) != false){
29636             this.setThumbBoxPosition();
29637             this.setCanvasPosition();
29638         }
29639     },
29640     
29641     onFooterButtonClick : function(e, el, o, type)
29642     {
29643         switch (type) {
29644             case 'rotate-left' :
29645                 this.onRotateLeft(e);
29646                 break;
29647             case 'rotate-right' :
29648                 this.onRotateRight(e);
29649                 break;
29650             case 'picture' :
29651                 this.beforeSelectFile(e);
29652                 break;
29653             case 'trash' :
29654                 this.trash(e);
29655                 break;
29656             case 'crop' :
29657                 this.crop(e);
29658                 break;
29659             case 'download' :
29660                 this.download(e);
29661                 break;
29662             default :
29663                 break;
29664         }
29665         
29666         this.fireEvent('footerbuttonclick', this, type);
29667     },
29668     
29669     beforeSelectFile : function(e)
29670     {
29671         e.preventDefault();
29672         
29673         if(this.fireEvent('beforeselectfile', this) != false){
29674             this.selectorEl.dom.click();
29675         }
29676     },
29677     
29678     onFileSelected : function(e)
29679     {
29680         e.preventDefault();
29681         
29682         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29683             return;
29684         }
29685         
29686         var file = this.selectorEl.dom.files[0];
29687         
29688         if(this.fireEvent('inspect', this, file) != false){
29689             this.prepare(file);
29690         }
29691         
29692     },
29693     
29694     trash : function(e)
29695     {
29696         this.fireEvent('trash', this);
29697     },
29698     
29699     download : function(e)
29700     {
29701         this.fireEvent('download', this);
29702     },
29703     
29704     loadCanvas : function(src)
29705     {   
29706         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29707             
29708             this.reset();
29709             
29710             this.imageEl = document.createElement('img');
29711             
29712             var _this = this;
29713             
29714             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29715             
29716             this.imageEl.src = src;
29717         }
29718     },
29719     
29720     onLoadCanvas : function()
29721     {   
29722         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29723         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29724         
29725         this.bodyEl.un('click', this.beforeSelectFile, this);
29726         
29727         this.notifyEl.hide();
29728         this.thumbEl.show();
29729         this.footerEl.show();
29730         
29731         this.baseRotateLevel();
29732         
29733         if(this.isDocument){
29734             this.setThumbBoxSize();
29735         }
29736         
29737         this.setThumbBoxPosition();
29738         
29739         this.baseScaleLevel();
29740         
29741         this.draw();
29742         
29743         this.resize();
29744         
29745         this.canvasLoaded = true;
29746         
29747         if(this.loadMask){
29748             this.maskEl.unmask();
29749         }
29750         
29751     },
29752     
29753     setCanvasPosition : function()
29754     {   
29755         if(!this.canvasEl){
29756             return;
29757         }
29758         
29759         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29760         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29761         
29762         this.previewEl.setLeft(pw);
29763         this.previewEl.setTop(ph);
29764         
29765     },
29766     
29767     onMouseDown : function(e)
29768     {   
29769         e.stopEvent();
29770         
29771         this.dragable = true;
29772         this.pinching = false;
29773         
29774         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29775             this.dragable = false;
29776             return;
29777         }
29778         
29779         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29780         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29781         
29782     },
29783     
29784     onMouseMove : function(e)
29785     {   
29786         e.stopEvent();
29787         
29788         if(!this.canvasLoaded){
29789             return;
29790         }
29791         
29792         if (!this.dragable){
29793             return;
29794         }
29795         
29796         var minX = Math.ceil(this.thumbEl.getLeft(true));
29797         var minY = Math.ceil(this.thumbEl.getTop(true));
29798         
29799         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29800         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29801         
29802         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29803         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29804         
29805         x = x - this.mouseX;
29806         y = y - this.mouseY;
29807         
29808         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29809         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29810         
29811         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29812         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29813         
29814         this.previewEl.setLeft(bgX);
29815         this.previewEl.setTop(bgY);
29816         
29817         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29818         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29819     },
29820     
29821     onMouseUp : function(e)
29822     {   
29823         e.stopEvent();
29824         
29825         this.dragable = false;
29826     },
29827     
29828     onMouseWheel : function(e)
29829     {   
29830         e.stopEvent();
29831         
29832         this.startScale = this.scale;
29833         
29834         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29835         
29836         if(!this.zoomable()){
29837             this.scale = this.startScale;
29838             return;
29839         }
29840         
29841         this.draw();
29842         
29843         return;
29844     },
29845     
29846     zoomable : function()
29847     {
29848         var minScale = this.thumbEl.getWidth() / this.minWidth;
29849         
29850         if(this.minWidth < this.minHeight){
29851             minScale = this.thumbEl.getHeight() / this.minHeight;
29852         }
29853         
29854         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29855         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29856         
29857         if(
29858                 this.isDocument &&
29859                 (this.rotate == 0 || this.rotate == 180) && 
29860                 (
29861                     width > this.imageEl.OriginWidth || 
29862                     height > this.imageEl.OriginHeight ||
29863                     (width < this.minWidth && height < this.minHeight)
29864                 )
29865         ){
29866             return false;
29867         }
29868         
29869         if(
29870                 this.isDocument &&
29871                 (this.rotate == 90 || this.rotate == 270) && 
29872                 (
29873                     width > this.imageEl.OriginWidth || 
29874                     height > this.imageEl.OriginHeight ||
29875                     (width < this.minHeight && height < this.minWidth)
29876                 )
29877         ){
29878             return false;
29879         }
29880         
29881         if(
29882                 !this.isDocument &&
29883                 (this.rotate == 0 || this.rotate == 180) && 
29884                 (
29885                     width < this.minWidth || 
29886                     width > this.imageEl.OriginWidth || 
29887                     height < this.minHeight || 
29888                     height > this.imageEl.OriginHeight
29889                 )
29890         ){
29891             return false;
29892         }
29893         
29894         if(
29895                 !this.isDocument &&
29896                 (this.rotate == 90 || this.rotate == 270) && 
29897                 (
29898                     width < this.minHeight || 
29899                     width > this.imageEl.OriginWidth || 
29900                     height < this.minWidth || 
29901                     height > this.imageEl.OriginHeight
29902                 )
29903         ){
29904             return false;
29905         }
29906         
29907         return true;
29908         
29909     },
29910     
29911     onRotateLeft : function(e)
29912     {   
29913         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29914             
29915             var minScale = this.thumbEl.getWidth() / this.minWidth;
29916             
29917             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29918             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29919             
29920             this.startScale = this.scale;
29921             
29922             while (this.getScaleLevel() < minScale){
29923             
29924                 this.scale = this.scale + 1;
29925                 
29926                 if(!this.zoomable()){
29927                     break;
29928                 }
29929                 
29930                 if(
29931                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29932                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29933                 ){
29934                     continue;
29935                 }
29936                 
29937                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29938
29939                 this.draw();
29940                 
29941                 return;
29942             }
29943             
29944             this.scale = this.startScale;
29945             
29946             this.onRotateFail();
29947             
29948             return false;
29949         }
29950         
29951         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29952
29953         if(this.isDocument){
29954             this.setThumbBoxSize();
29955             this.setThumbBoxPosition();
29956             this.setCanvasPosition();
29957         }
29958         
29959         this.draw();
29960         
29961         this.fireEvent('rotate', this, 'left');
29962         
29963     },
29964     
29965     onRotateRight : function(e)
29966     {
29967         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29968             
29969             var minScale = this.thumbEl.getWidth() / this.minWidth;
29970         
29971             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29972             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29973             
29974             this.startScale = this.scale;
29975             
29976             while (this.getScaleLevel() < minScale){
29977             
29978                 this.scale = this.scale + 1;
29979                 
29980                 if(!this.zoomable()){
29981                     break;
29982                 }
29983                 
29984                 if(
29985                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29986                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29987                 ){
29988                     continue;
29989                 }
29990                 
29991                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29992
29993                 this.draw();
29994                 
29995                 return;
29996             }
29997             
29998             this.scale = this.startScale;
29999             
30000             this.onRotateFail();
30001             
30002             return false;
30003         }
30004         
30005         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30006
30007         if(this.isDocument){
30008             this.setThumbBoxSize();
30009             this.setThumbBoxPosition();
30010             this.setCanvasPosition();
30011         }
30012         
30013         this.draw();
30014         
30015         this.fireEvent('rotate', this, 'right');
30016     },
30017     
30018     onRotateFail : function()
30019     {
30020         this.errorEl.show(true);
30021         
30022         var _this = this;
30023         
30024         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30025     },
30026     
30027     draw : function()
30028     {
30029         this.previewEl.dom.innerHTML = '';
30030         
30031         var canvasEl = document.createElement("canvas");
30032         
30033         var contextEl = canvasEl.getContext("2d");
30034         
30035         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30036         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30037         var center = this.imageEl.OriginWidth / 2;
30038         
30039         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30040             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30041             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30042             center = this.imageEl.OriginHeight / 2;
30043         }
30044         
30045         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30046         
30047         contextEl.translate(center, center);
30048         contextEl.rotate(this.rotate * Math.PI / 180);
30049
30050         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30051         
30052         this.canvasEl = document.createElement("canvas");
30053         
30054         this.contextEl = this.canvasEl.getContext("2d");
30055         
30056         switch (this.rotate) {
30057             case 0 :
30058                 
30059                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30060                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30061                 
30062                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30063                 
30064                 break;
30065             case 90 : 
30066                 
30067                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30068                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30069                 
30070                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30071                     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);
30072                     break;
30073                 }
30074                 
30075                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30076                 
30077                 break;
30078             case 180 :
30079                 
30080                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30081                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30082                 
30083                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30084                     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);
30085                     break;
30086                 }
30087                 
30088                 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);
30089                 
30090                 break;
30091             case 270 :
30092                 
30093                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30094                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30095         
30096                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30097                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30098                     break;
30099                 }
30100                 
30101                 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);
30102                 
30103                 break;
30104             default : 
30105                 break;
30106         }
30107         
30108         this.previewEl.appendChild(this.canvasEl);
30109         
30110         this.setCanvasPosition();
30111     },
30112     
30113     crop : function()
30114     {
30115         if(!this.canvasLoaded){
30116             return;
30117         }
30118         
30119         var imageCanvas = document.createElement("canvas");
30120         
30121         var imageContext = imageCanvas.getContext("2d");
30122         
30123         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30124         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30125         
30126         var center = imageCanvas.width / 2;
30127         
30128         imageContext.translate(center, center);
30129         
30130         imageContext.rotate(this.rotate * Math.PI / 180);
30131         
30132         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30133         
30134         var canvas = document.createElement("canvas");
30135         
30136         var context = canvas.getContext("2d");
30137                 
30138         canvas.width = this.minWidth;
30139         canvas.height = this.minHeight;
30140
30141         switch (this.rotate) {
30142             case 0 :
30143                 
30144                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30145                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30146                 
30147                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30148                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30149                 
30150                 var targetWidth = this.minWidth - 2 * x;
30151                 var targetHeight = this.minHeight - 2 * y;
30152                 
30153                 var scale = 1;
30154                 
30155                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30156                     scale = targetWidth / width;
30157                 }
30158                 
30159                 if(x > 0 && y == 0){
30160                     scale = targetHeight / height;
30161                 }
30162                 
30163                 if(x > 0 && y > 0){
30164                     scale = targetWidth / width;
30165                     
30166                     if(width < height){
30167                         scale = targetHeight / height;
30168                     }
30169                 }
30170                 
30171                 context.scale(scale, scale);
30172                 
30173                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30174                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30175
30176                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30177                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30178
30179                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30180                 
30181                 break;
30182             case 90 : 
30183                 
30184                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30185                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30186                 
30187                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30188                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30189                 
30190                 var targetWidth = this.minWidth - 2 * x;
30191                 var targetHeight = this.minHeight - 2 * y;
30192                 
30193                 var scale = 1;
30194                 
30195                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30196                     scale = targetWidth / width;
30197                 }
30198                 
30199                 if(x > 0 && y == 0){
30200                     scale = targetHeight / height;
30201                 }
30202                 
30203                 if(x > 0 && y > 0){
30204                     scale = targetWidth / width;
30205                     
30206                     if(width < height){
30207                         scale = targetHeight / height;
30208                     }
30209                 }
30210                 
30211                 context.scale(scale, scale);
30212                 
30213                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30214                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30215
30216                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30217                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30218                 
30219                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30220                 
30221                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30222                 
30223                 break;
30224             case 180 :
30225                 
30226                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30227                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30228                 
30229                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30230                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30231                 
30232                 var targetWidth = this.minWidth - 2 * x;
30233                 var targetHeight = this.minHeight - 2 * y;
30234                 
30235                 var scale = 1;
30236                 
30237                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30238                     scale = targetWidth / width;
30239                 }
30240                 
30241                 if(x > 0 && y == 0){
30242                     scale = targetHeight / height;
30243                 }
30244                 
30245                 if(x > 0 && y > 0){
30246                     scale = targetWidth / width;
30247                     
30248                     if(width < height){
30249                         scale = targetHeight / height;
30250                     }
30251                 }
30252                 
30253                 context.scale(scale, scale);
30254                 
30255                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30256                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30257
30258                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30259                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30260
30261                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30262                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30263                 
30264                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30265                 
30266                 break;
30267             case 270 :
30268                 
30269                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30270                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30271                 
30272                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30273                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30274                 
30275                 var targetWidth = this.minWidth - 2 * x;
30276                 var targetHeight = this.minHeight - 2 * y;
30277                 
30278                 var scale = 1;
30279                 
30280                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30281                     scale = targetWidth / width;
30282                 }
30283                 
30284                 if(x > 0 && y == 0){
30285                     scale = targetHeight / height;
30286                 }
30287                 
30288                 if(x > 0 && y > 0){
30289                     scale = targetWidth / width;
30290                     
30291                     if(width < height){
30292                         scale = targetHeight / height;
30293                     }
30294                 }
30295                 
30296                 context.scale(scale, scale);
30297                 
30298                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30299                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30300
30301                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30302                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30303                 
30304                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30305                 
30306                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30307                 
30308                 break;
30309             default : 
30310                 break;
30311         }
30312         
30313         this.cropData = canvas.toDataURL(this.cropType);
30314         
30315         if(this.fireEvent('crop', this, this.cropData) !== false){
30316             this.process(this.file, this.cropData);
30317         }
30318         
30319         return;
30320         
30321     },
30322     
30323     setThumbBoxSize : function()
30324     {
30325         var width, height;
30326         
30327         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30328             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30329             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30330             
30331             this.minWidth = width;
30332             this.minHeight = height;
30333             
30334             if(this.rotate == 90 || this.rotate == 270){
30335                 this.minWidth = height;
30336                 this.minHeight = width;
30337             }
30338         }
30339         
30340         height = 300;
30341         width = Math.ceil(this.minWidth * height / this.minHeight);
30342         
30343         if(this.minWidth > this.minHeight){
30344             width = 300;
30345             height = Math.ceil(this.minHeight * width / this.minWidth);
30346         }
30347         
30348         this.thumbEl.setStyle({
30349             width : width + 'px',
30350             height : height + 'px'
30351         });
30352
30353         return;
30354             
30355     },
30356     
30357     setThumbBoxPosition : function()
30358     {
30359         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30360         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30361         
30362         this.thumbEl.setLeft(x);
30363         this.thumbEl.setTop(y);
30364         
30365     },
30366     
30367     baseRotateLevel : function()
30368     {
30369         this.baseRotate = 1;
30370         
30371         if(
30372                 typeof(this.exif) != 'undefined' &&
30373                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30374                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30375         ){
30376             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30377         }
30378         
30379         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30380         
30381     },
30382     
30383     baseScaleLevel : function()
30384     {
30385         var width, height;
30386         
30387         if(this.isDocument){
30388             
30389             if(this.baseRotate == 6 || this.baseRotate == 8){
30390             
30391                 height = this.thumbEl.getHeight();
30392                 this.baseScale = height / this.imageEl.OriginWidth;
30393
30394                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30395                     width = this.thumbEl.getWidth();
30396                     this.baseScale = width / this.imageEl.OriginHeight;
30397                 }
30398
30399                 return;
30400             }
30401
30402             height = this.thumbEl.getHeight();
30403             this.baseScale = height / this.imageEl.OriginHeight;
30404
30405             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30406                 width = this.thumbEl.getWidth();
30407                 this.baseScale = width / this.imageEl.OriginWidth;
30408             }
30409
30410             return;
30411         }
30412         
30413         if(this.baseRotate == 6 || this.baseRotate == 8){
30414             
30415             width = this.thumbEl.getHeight();
30416             this.baseScale = width / this.imageEl.OriginHeight;
30417             
30418             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30419                 height = this.thumbEl.getWidth();
30420                 this.baseScale = height / this.imageEl.OriginHeight;
30421             }
30422             
30423             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30424                 height = this.thumbEl.getWidth();
30425                 this.baseScale = height / this.imageEl.OriginHeight;
30426                 
30427                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30428                     width = this.thumbEl.getHeight();
30429                     this.baseScale = width / this.imageEl.OriginWidth;
30430                 }
30431             }
30432             
30433             return;
30434         }
30435         
30436         width = this.thumbEl.getWidth();
30437         this.baseScale = width / this.imageEl.OriginWidth;
30438         
30439         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30440             height = this.thumbEl.getHeight();
30441             this.baseScale = height / this.imageEl.OriginHeight;
30442         }
30443         
30444         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30445             
30446             height = this.thumbEl.getHeight();
30447             this.baseScale = height / this.imageEl.OriginHeight;
30448             
30449             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30450                 width = this.thumbEl.getWidth();
30451                 this.baseScale = width / this.imageEl.OriginWidth;
30452             }
30453             
30454         }
30455         
30456         return;
30457     },
30458     
30459     getScaleLevel : function()
30460     {
30461         return this.baseScale * Math.pow(1.1, this.scale);
30462     },
30463     
30464     onTouchStart : function(e)
30465     {
30466         if(!this.canvasLoaded){
30467             this.beforeSelectFile(e);
30468             return;
30469         }
30470         
30471         var touches = e.browserEvent.touches;
30472         
30473         if(!touches){
30474             return;
30475         }
30476         
30477         if(touches.length == 1){
30478             this.onMouseDown(e);
30479             return;
30480         }
30481         
30482         if(touches.length != 2){
30483             return;
30484         }
30485         
30486         var coords = [];
30487         
30488         for(var i = 0, finger; finger = touches[i]; i++){
30489             coords.push(finger.pageX, finger.pageY);
30490         }
30491         
30492         var x = Math.pow(coords[0] - coords[2], 2);
30493         var y = Math.pow(coords[1] - coords[3], 2);
30494         
30495         this.startDistance = Math.sqrt(x + y);
30496         
30497         this.startScale = this.scale;
30498         
30499         this.pinching = true;
30500         this.dragable = false;
30501         
30502     },
30503     
30504     onTouchMove : function(e)
30505     {
30506         if(!this.pinching && !this.dragable){
30507             return;
30508         }
30509         
30510         var touches = e.browserEvent.touches;
30511         
30512         if(!touches){
30513             return;
30514         }
30515         
30516         if(this.dragable){
30517             this.onMouseMove(e);
30518             return;
30519         }
30520         
30521         var coords = [];
30522         
30523         for(var i = 0, finger; finger = touches[i]; i++){
30524             coords.push(finger.pageX, finger.pageY);
30525         }
30526         
30527         var x = Math.pow(coords[0] - coords[2], 2);
30528         var y = Math.pow(coords[1] - coords[3], 2);
30529         
30530         this.endDistance = Math.sqrt(x + y);
30531         
30532         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30533         
30534         if(!this.zoomable()){
30535             this.scale = this.startScale;
30536             return;
30537         }
30538         
30539         this.draw();
30540         
30541     },
30542     
30543     onTouchEnd : function(e)
30544     {
30545         this.pinching = false;
30546         this.dragable = false;
30547         
30548     },
30549     
30550     process : function(file, crop)
30551     {
30552         if(this.loadMask){
30553             this.maskEl.mask(this.loadingText);
30554         }
30555         
30556         this.xhr = new XMLHttpRequest();
30557         
30558         file.xhr = this.xhr;
30559
30560         this.xhr.open(this.method, this.url, true);
30561         
30562         var headers = {
30563             "Accept": "application/json",
30564             "Cache-Control": "no-cache",
30565             "X-Requested-With": "XMLHttpRequest"
30566         };
30567         
30568         for (var headerName in headers) {
30569             var headerValue = headers[headerName];
30570             if (headerValue) {
30571                 this.xhr.setRequestHeader(headerName, headerValue);
30572             }
30573         }
30574         
30575         var _this = this;
30576         
30577         this.xhr.onload = function()
30578         {
30579             _this.xhrOnLoad(_this.xhr);
30580         }
30581         
30582         this.xhr.onerror = function()
30583         {
30584             _this.xhrOnError(_this.xhr);
30585         }
30586         
30587         var formData = new FormData();
30588
30589         formData.append('returnHTML', 'NO');
30590         
30591         if(crop){
30592             formData.append('crop', crop);
30593         }
30594         
30595         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30596             formData.append(this.paramName, file, file.name);
30597         }
30598         
30599         if(typeof(file.filename) != 'undefined'){
30600             formData.append('filename', file.filename);
30601         }
30602         
30603         if(typeof(file.mimetype) != 'undefined'){
30604             formData.append('mimetype', file.mimetype);
30605         }
30606         
30607         if(this.fireEvent('arrange', this, formData) != false){
30608             this.xhr.send(formData);
30609         };
30610     },
30611     
30612     xhrOnLoad : function(xhr)
30613     {
30614         if(this.loadMask){
30615             this.maskEl.unmask();
30616         }
30617         
30618         if (xhr.readyState !== 4) {
30619             this.fireEvent('exception', this, xhr);
30620             return;
30621         }
30622
30623         var response = Roo.decode(xhr.responseText);
30624         
30625         if(!response.success){
30626             this.fireEvent('exception', this, xhr);
30627             return;
30628         }
30629         
30630         var response = Roo.decode(xhr.responseText);
30631         
30632         this.fireEvent('upload', this, response);
30633         
30634     },
30635     
30636     xhrOnError : function()
30637     {
30638         if(this.loadMask){
30639             this.maskEl.unmask();
30640         }
30641         
30642         Roo.log('xhr on error');
30643         
30644         var response = Roo.decode(xhr.responseText);
30645           
30646         Roo.log(response);
30647         
30648     },
30649     
30650     prepare : function(file)
30651     {   
30652         if(this.loadMask){
30653             this.maskEl.mask(this.loadingText);
30654         }
30655         
30656         this.file = false;
30657         this.exif = {};
30658         
30659         if(typeof(file) === 'string'){
30660             this.loadCanvas(file);
30661             return;
30662         }
30663         
30664         if(!file || !this.urlAPI){
30665             return;
30666         }
30667         
30668         this.file = file;
30669         this.cropType = file.type;
30670         
30671         var _this = this;
30672         
30673         if(this.fireEvent('prepare', this, this.file) != false){
30674             
30675             var reader = new FileReader();
30676             
30677             reader.onload = function (e) {
30678                 if (e.target.error) {
30679                     Roo.log(e.target.error);
30680                     return;
30681                 }
30682                 
30683                 var buffer = e.target.result,
30684                     dataView = new DataView(buffer),
30685                     offset = 2,
30686                     maxOffset = dataView.byteLength - 4,
30687                     markerBytes,
30688                     markerLength;
30689                 
30690                 if (dataView.getUint16(0) === 0xffd8) {
30691                     while (offset < maxOffset) {
30692                         markerBytes = dataView.getUint16(offset);
30693                         
30694                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30695                             markerLength = dataView.getUint16(offset + 2) + 2;
30696                             if (offset + markerLength > dataView.byteLength) {
30697                                 Roo.log('Invalid meta data: Invalid segment size.');
30698                                 break;
30699                             }
30700                             
30701                             if(markerBytes == 0xffe1){
30702                                 _this.parseExifData(
30703                                     dataView,
30704                                     offset,
30705                                     markerLength
30706                                 );
30707                             }
30708                             
30709                             offset += markerLength;
30710                             
30711                             continue;
30712                         }
30713                         
30714                         break;
30715                     }
30716                     
30717                 }
30718                 
30719                 var url = _this.urlAPI.createObjectURL(_this.file);
30720                 
30721                 _this.loadCanvas(url);
30722                 
30723                 return;
30724             }
30725             
30726             reader.readAsArrayBuffer(this.file);
30727             
30728         }
30729         
30730     },
30731     
30732     parseExifData : function(dataView, offset, length)
30733     {
30734         var tiffOffset = offset + 10,
30735             littleEndian,
30736             dirOffset;
30737     
30738         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30739             // No Exif data, might be XMP data instead
30740             return;
30741         }
30742         
30743         // Check for the ASCII code for "Exif" (0x45786966):
30744         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30745             // No Exif data, might be XMP data instead
30746             return;
30747         }
30748         if (tiffOffset + 8 > dataView.byteLength) {
30749             Roo.log('Invalid Exif data: Invalid segment size.');
30750             return;
30751         }
30752         // Check for the two null bytes:
30753         if (dataView.getUint16(offset + 8) !== 0x0000) {
30754             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30755             return;
30756         }
30757         // Check the byte alignment:
30758         switch (dataView.getUint16(tiffOffset)) {
30759         case 0x4949:
30760             littleEndian = true;
30761             break;
30762         case 0x4D4D:
30763             littleEndian = false;
30764             break;
30765         default:
30766             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30767             return;
30768         }
30769         // Check for the TIFF tag marker (0x002A):
30770         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30771             Roo.log('Invalid Exif data: Missing TIFF marker.');
30772             return;
30773         }
30774         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30775         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30776         
30777         this.parseExifTags(
30778             dataView,
30779             tiffOffset,
30780             tiffOffset + dirOffset,
30781             littleEndian
30782         );
30783     },
30784     
30785     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30786     {
30787         var tagsNumber,
30788             dirEndOffset,
30789             i;
30790         if (dirOffset + 6 > dataView.byteLength) {
30791             Roo.log('Invalid Exif data: Invalid directory offset.');
30792             return;
30793         }
30794         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30795         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30796         if (dirEndOffset + 4 > dataView.byteLength) {
30797             Roo.log('Invalid Exif data: Invalid directory size.');
30798             return;
30799         }
30800         for (i = 0; i < tagsNumber; i += 1) {
30801             this.parseExifTag(
30802                 dataView,
30803                 tiffOffset,
30804                 dirOffset + 2 + 12 * i, // tag offset
30805                 littleEndian
30806             );
30807         }
30808         // Return the offset to the next directory:
30809         return dataView.getUint32(dirEndOffset, littleEndian);
30810     },
30811     
30812     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30813     {
30814         var tag = dataView.getUint16(offset, littleEndian);
30815         
30816         this.exif[tag] = this.getExifValue(
30817             dataView,
30818             tiffOffset,
30819             offset,
30820             dataView.getUint16(offset + 2, littleEndian), // tag type
30821             dataView.getUint32(offset + 4, littleEndian), // tag length
30822             littleEndian
30823         );
30824     },
30825     
30826     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30827     {
30828         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30829             tagSize,
30830             dataOffset,
30831             values,
30832             i,
30833             str,
30834             c;
30835     
30836         if (!tagType) {
30837             Roo.log('Invalid Exif data: Invalid tag type.');
30838             return;
30839         }
30840         
30841         tagSize = tagType.size * length;
30842         // Determine if the value is contained in the dataOffset bytes,
30843         // or if the value at the dataOffset is a pointer to the actual data:
30844         dataOffset = tagSize > 4 ?
30845                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30846         if (dataOffset + tagSize > dataView.byteLength) {
30847             Roo.log('Invalid Exif data: Invalid data offset.');
30848             return;
30849         }
30850         if (length === 1) {
30851             return tagType.getValue(dataView, dataOffset, littleEndian);
30852         }
30853         values = [];
30854         for (i = 0; i < length; i += 1) {
30855             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30856         }
30857         
30858         if (tagType.ascii) {
30859             str = '';
30860             // Concatenate the chars:
30861             for (i = 0; i < values.length; i += 1) {
30862                 c = values[i];
30863                 // Ignore the terminating NULL byte(s):
30864                 if (c === '\u0000') {
30865                     break;
30866                 }
30867                 str += c;
30868             }
30869             return str;
30870         }
30871         return values;
30872     }
30873     
30874 });
30875
30876 Roo.apply(Roo.bootstrap.UploadCropbox, {
30877     tags : {
30878         'Orientation': 0x0112
30879     },
30880     
30881     Orientation: {
30882             1: 0, //'top-left',
30883 //            2: 'top-right',
30884             3: 180, //'bottom-right',
30885 //            4: 'bottom-left',
30886 //            5: 'left-top',
30887             6: 90, //'right-top',
30888 //            7: 'right-bottom',
30889             8: 270 //'left-bottom'
30890     },
30891     
30892     exifTagTypes : {
30893         // byte, 8-bit unsigned int:
30894         1: {
30895             getValue: function (dataView, dataOffset) {
30896                 return dataView.getUint8(dataOffset);
30897             },
30898             size: 1
30899         },
30900         // ascii, 8-bit byte:
30901         2: {
30902             getValue: function (dataView, dataOffset) {
30903                 return String.fromCharCode(dataView.getUint8(dataOffset));
30904             },
30905             size: 1,
30906             ascii: true
30907         },
30908         // short, 16 bit int:
30909         3: {
30910             getValue: function (dataView, dataOffset, littleEndian) {
30911                 return dataView.getUint16(dataOffset, littleEndian);
30912             },
30913             size: 2
30914         },
30915         // long, 32 bit int:
30916         4: {
30917             getValue: function (dataView, dataOffset, littleEndian) {
30918                 return dataView.getUint32(dataOffset, littleEndian);
30919             },
30920             size: 4
30921         },
30922         // rational = two long values, first is numerator, second is denominator:
30923         5: {
30924             getValue: function (dataView, dataOffset, littleEndian) {
30925                 return dataView.getUint32(dataOffset, littleEndian) /
30926                     dataView.getUint32(dataOffset + 4, littleEndian);
30927             },
30928             size: 8
30929         },
30930         // slong, 32 bit signed int:
30931         9: {
30932             getValue: function (dataView, dataOffset, littleEndian) {
30933                 return dataView.getInt32(dataOffset, littleEndian);
30934             },
30935             size: 4
30936         },
30937         // srational, two slongs, first is numerator, second is denominator:
30938         10: {
30939             getValue: function (dataView, dataOffset, littleEndian) {
30940                 return dataView.getInt32(dataOffset, littleEndian) /
30941                     dataView.getInt32(dataOffset + 4, littleEndian);
30942             },
30943             size: 8
30944         }
30945     },
30946     
30947     footer : {
30948         STANDARD : [
30949             {
30950                 tag : 'div',
30951                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30952                 action : 'rotate-left',
30953                 cn : [
30954                     {
30955                         tag : 'button',
30956                         cls : 'btn btn-default',
30957                         html : '<i class="fa fa-undo"></i>'
30958                     }
30959                 ]
30960             },
30961             {
30962                 tag : 'div',
30963                 cls : 'btn-group roo-upload-cropbox-picture',
30964                 action : 'picture',
30965                 cn : [
30966                     {
30967                         tag : 'button',
30968                         cls : 'btn btn-default',
30969                         html : '<i class="fa fa-picture-o"></i>'
30970                     }
30971                 ]
30972             },
30973             {
30974                 tag : 'div',
30975                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30976                 action : 'rotate-right',
30977                 cn : [
30978                     {
30979                         tag : 'button',
30980                         cls : 'btn btn-default',
30981                         html : '<i class="fa fa-repeat"></i>'
30982                     }
30983                 ]
30984             }
30985         ],
30986         DOCUMENT : [
30987             {
30988                 tag : 'div',
30989                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30990                 action : 'rotate-left',
30991                 cn : [
30992                     {
30993                         tag : 'button',
30994                         cls : 'btn btn-default',
30995                         html : '<i class="fa fa-undo"></i>'
30996                     }
30997                 ]
30998             },
30999             {
31000                 tag : 'div',
31001                 cls : 'btn-group roo-upload-cropbox-download',
31002                 action : 'download',
31003                 cn : [
31004                     {
31005                         tag : 'button',
31006                         cls : 'btn btn-default',
31007                         html : '<i class="fa fa-download"></i>'
31008                     }
31009                 ]
31010             },
31011             {
31012                 tag : 'div',
31013                 cls : 'btn-group roo-upload-cropbox-crop',
31014                 action : 'crop',
31015                 cn : [
31016                     {
31017                         tag : 'button',
31018                         cls : 'btn btn-default',
31019                         html : '<i class="fa fa-crop"></i>'
31020                     }
31021                 ]
31022             },
31023             {
31024                 tag : 'div',
31025                 cls : 'btn-group roo-upload-cropbox-trash',
31026                 action : 'trash',
31027                 cn : [
31028                     {
31029                         tag : 'button',
31030                         cls : 'btn btn-default',
31031                         html : '<i class="fa fa-trash"></i>'
31032                     }
31033                 ]
31034             },
31035             {
31036                 tag : 'div',
31037                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31038                 action : 'rotate-right',
31039                 cn : [
31040                     {
31041                         tag : 'button',
31042                         cls : 'btn btn-default',
31043                         html : '<i class="fa fa-repeat"></i>'
31044                     }
31045                 ]
31046             }
31047         ],
31048         ROTATOR : [
31049             {
31050                 tag : 'div',
31051                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31052                 action : 'rotate-left',
31053                 cn : [
31054                     {
31055                         tag : 'button',
31056                         cls : 'btn btn-default',
31057                         html : '<i class="fa fa-undo"></i>'
31058                     }
31059                 ]
31060             },
31061             {
31062                 tag : 'div',
31063                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31064                 action : 'rotate-right',
31065                 cn : [
31066                     {
31067                         tag : 'button',
31068                         cls : 'btn btn-default',
31069                         html : '<i class="fa fa-repeat"></i>'
31070                     }
31071                 ]
31072             }
31073         ]
31074     }
31075 });
31076
31077 /*
31078 * Licence: LGPL
31079 */
31080
31081 /**
31082  * @class Roo.bootstrap.DocumentManager
31083  * @extends Roo.bootstrap.Component
31084  * Bootstrap DocumentManager class
31085  * @cfg {String} paramName default 'imageUpload'
31086  * @cfg {String} toolTipName default 'filename'
31087  * @cfg {String} method default POST
31088  * @cfg {String} url action url
31089  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31090  * @cfg {Boolean} multiple multiple upload default true
31091  * @cfg {Number} thumbSize default 300
31092  * @cfg {String} fieldLabel
31093  * @cfg {Number} labelWidth default 4
31094  * @cfg {String} labelAlign (left|top) default left
31095  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31096 * @cfg {Number} labellg set the width of label (1-12)
31097  * @cfg {Number} labelmd set the width of label (1-12)
31098  * @cfg {Number} labelsm set the width of label (1-12)
31099  * @cfg {Number} labelxs set the width of label (1-12)
31100  * 
31101  * @constructor
31102  * Create a new DocumentManager
31103  * @param {Object} config The config object
31104  */
31105
31106 Roo.bootstrap.DocumentManager = function(config){
31107     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31108     
31109     this.files = [];
31110     this.delegates = [];
31111     
31112     this.addEvents({
31113         /**
31114          * @event initial
31115          * Fire when initial the DocumentManager
31116          * @param {Roo.bootstrap.DocumentManager} this
31117          */
31118         "initial" : true,
31119         /**
31120          * @event inspect
31121          * inspect selected file
31122          * @param {Roo.bootstrap.DocumentManager} this
31123          * @param {File} file
31124          */
31125         "inspect" : true,
31126         /**
31127          * @event exception
31128          * Fire when xhr load exception
31129          * @param {Roo.bootstrap.DocumentManager} this
31130          * @param {XMLHttpRequest} xhr
31131          */
31132         "exception" : true,
31133         /**
31134          * @event afterupload
31135          * Fire when xhr load exception
31136          * @param {Roo.bootstrap.DocumentManager} this
31137          * @param {XMLHttpRequest} xhr
31138          */
31139         "afterupload" : true,
31140         /**
31141          * @event prepare
31142          * prepare the form data
31143          * @param {Roo.bootstrap.DocumentManager} this
31144          * @param {Object} formData
31145          */
31146         "prepare" : true,
31147         /**
31148          * @event remove
31149          * Fire when remove the file
31150          * @param {Roo.bootstrap.DocumentManager} this
31151          * @param {Object} file
31152          */
31153         "remove" : true,
31154         /**
31155          * @event refresh
31156          * Fire after refresh the file
31157          * @param {Roo.bootstrap.DocumentManager} this
31158          */
31159         "refresh" : true,
31160         /**
31161          * @event click
31162          * Fire after click the image
31163          * @param {Roo.bootstrap.DocumentManager} this
31164          * @param {Object} file
31165          */
31166         "click" : true,
31167         /**
31168          * @event edit
31169          * Fire when upload a image and editable set to true
31170          * @param {Roo.bootstrap.DocumentManager} this
31171          * @param {Object} file
31172          */
31173         "edit" : true,
31174         /**
31175          * @event beforeselectfile
31176          * Fire before select file
31177          * @param {Roo.bootstrap.DocumentManager} this
31178          */
31179         "beforeselectfile" : true,
31180         /**
31181          * @event process
31182          * Fire before process file
31183          * @param {Roo.bootstrap.DocumentManager} this
31184          * @param {Object} file
31185          */
31186         "process" : true,
31187         /**
31188          * @event previewrendered
31189          * Fire when preview rendered
31190          * @param {Roo.bootstrap.DocumentManager} this
31191          * @param {Object} file
31192          */
31193         "previewrendered" : true,
31194         /**
31195          */
31196         "previewResize" : true
31197         
31198     });
31199 };
31200
31201 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31202     
31203     boxes : 0,
31204     inputName : '',
31205     thumbSize : 300,
31206     multiple : true,
31207     files : false,
31208     method : 'POST',
31209     url : '',
31210     paramName : 'imageUpload',
31211     toolTipName : 'filename',
31212     fieldLabel : '',
31213     labelWidth : 4,
31214     labelAlign : 'left',
31215     editable : true,
31216     delegates : false,
31217     xhr : false, 
31218     
31219     labellg : 0,
31220     labelmd : 0,
31221     labelsm : 0,
31222     labelxs : 0,
31223     
31224     getAutoCreate : function()
31225     {   
31226         var managerWidget = {
31227             tag : 'div',
31228             cls : 'roo-document-manager',
31229             cn : [
31230                 {
31231                     tag : 'input',
31232                     cls : 'roo-document-manager-selector',
31233                     type : 'file'
31234                 },
31235                 {
31236                     tag : 'div',
31237                     cls : 'roo-document-manager-uploader',
31238                     cn : [
31239                         {
31240                             tag : 'div',
31241                             cls : 'roo-document-manager-upload-btn',
31242                             html : '<i class="fa fa-plus"></i>'
31243                         }
31244                     ]
31245                     
31246                 }
31247             ]
31248         };
31249         
31250         var content = [
31251             {
31252                 tag : 'div',
31253                 cls : 'column col-md-12',
31254                 cn : managerWidget
31255             }
31256         ];
31257         
31258         if(this.fieldLabel.length){
31259             
31260             content = [
31261                 {
31262                     tag : 'div',
31263                     cls : 'column col-md-12',
31264                     html : this.fieldLabel
31265                 },
31266                 {
31267                     tag : 'div',
31268                     cls : 'column col-md-12',
31269                     cn : managerWidget
31270                 }
31271             ];
31272
31273             if(this.labelAlign == 'left'){
31274                 content = [
31275                     {
31276                         tag : 'div',
31277                         cls : 'column',
31278                         html : this.fieldLabel
31279                     },
31280                     {
31281                         tag : 'div',
31282                         cls : 'column',
31283                         cn : managerWidget
31284                     }
31285                 ];
31286                 
31287                 if(this.labelWidth > 12){
31288                     content[0].style = "width: " + this.labelWidth + 'px';
31289                 }
31290
31291                 if(this.labelWidth < 13 && this.labelmd == 0){
31292                     this.labelmd = this.labelWidth;
31293                 }
31294
31295                 if(this.labellg > 0){
31296                     content[0].cls += ' col-lg-' + this.labellg;
31297                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31298                 }
31299
31300                 if(this.labelmd > 0){
31301                     content[0].cls += ' col-md-' + this.labelmd;
31302                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31303                 }
31304
31305                 if(this.labelsm > 0){
31306                     content[0].cls += ' col-sm-' + this.labelsm;
31307                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31308                 }
31309
31310                 if(this.labelxs > 0){
31311                     content[0].cls += ' col-xs-' + this.labelxs;
31312                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31313                 }
31314                 
31315             }
31316         }
31317         
31318         var cfg = {
31319             tag : 'div',
31320             cls : 'row clearfix',
31321             cn : content
31322         };
31323         
31324         return cfg;
31325         
31326     },
31327     
31328     initEvents : function()
31329     {
31330         this.managerEl = this.el.select('.roo-document-manager', true).first();
31331         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31332         
31333         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31334         this.selectorEl.hide();
31335         
31336         if(this.multiple){
31337             this.selectorEl.attr('multiple', 'multiple');
31338         }
31339         
31340         this.selectorEl.on('change', this.onFileSelected, this);
31341         
31342         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31343         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31344         
31345         this.uploader.on('click', this.onUploaderClick, this);
31346         
31347         this.renderProgressDialog();
31348         
31349         var _this = this;
31350         
31351         window.addEventListener("resize", function() { _this.refresh(); } );
31352         
31353         this.fireEvent('initial', this);
31354     },
31355     
31356     renderProgressDialog : function()
31357     {
31358         var _this = this;
31359         
31360         this.progressDialog = new Roo.bootstrap.Modal({
31361             cls : 'roo-document-manager-progress-dialog',
31362             allow_close : false,
31363             animate : false,
31364             title : '',
31365             buttons : [
31366                 {
31367                     name  :'cancel',
31368                     weight : 'danger',
31369                     html : 'Cancel'
31370                 }
31371             ], 
31372             listeners : { 
31373                 btnclick : function() {
31374                     _this.uploadCancel();
31375                     this.hide();
31376                 }
31377             }
31378         });
31379          
31380         this.progressDialog.render(Roo.get(document.body));
31381          
31382         this.progress = new Roo.bootstrap.Progress({
31383             cls : 'roo-document-manager-progress',
31384             active : true,
31385             striped : true
31386         });
31387         
31388         this.progress.render(this.progressDialog.getChildContainer());
31389         
31390         this.progressBar = new Roo.bootstrap.ProgressBar({
31391             cls : 'roo-document-manager-progress-bar',
31392             aria_valuenow : 0,
31393             aria_valuemin : 0,
31394             aria_valuemax : 12,
31395             panel : 'success'
31396         });
31397         
31398         this.progressBar.render(this.progress.getChildContainer());
31399     },
31400     
31401     onUploaderClick : function(e)
31402     {
31403         e.preventDefault();
31404      
31405         if(this.fireEvent('beforeselectfile', this) != false){
31406             this.selectorEl.dom.click();
31407         }
31408         
31409     },
31410     
31411     onFileSelected : function(e)
31412     {
31413         e.preventDefault();
31414         
31415         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31416             return;
31417         }
31418         
31419         Roo.each(this.selectorEl.dom.files, function(file){
31420             if(this.fireEvent('inspect', this, file) != false){
31421                 this.files.push(file);
31422             }
31423         }, this);
31424         
31425         this.queue();
31426         
31427     },
31428     
31429     queue : function()
31430     {
31431         this.selectorEl.dom.value = '';
31432         
31433         if(!this.files || !this.files.length){
31434             return;
31435         }
31436         
31437         if(this.boxes > 0 && this.files.length > this.boxes){
31438             this.files = this.files.slice(0, this.boxes);
31439         }
31440         
31441         this.uploader.show();
31442         
31443         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31444             this.uploader.hide();
31445         }
31446         
31447         var _this = this;
31448         
31449         var files = [];
31450         
31451         var docs = [];
31452         
31453         Roo.each(this.files, function(file){
31454             
31455             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31456                 var f = this.renderPreview(file);
31457                 files.push(f);
31458                 return;
31459             }
31460             
31461             if(file.type.indexOf('image') != -1){
31462                 this.delegates.push(
31463                     (function(){
31464                         _this.process(file);
31465                     }).createDelegate(this)
31466                 );
31467         
31468                 return;
31469             }
31470             
31471             docs.push(
31472                 (function(){
31473                     _this.process(file);
31474                 }).createDelegate(this)
31475             );
31476             
31477         }, this);
31478         
31479         this.files = files;
31480         
31481         this.delegates = this.delegates.concat(docs);
31482         
31483         if(!this.delegates.length){
31484             this.refresh();
31485             return;
31486         }
31487         
31488         this.progressBar.aria_valuemax = this.delegates.length;
31489         
31490         this.arrange();
31491         
31492         return;
31493     },
31494     
31495     arrange : function()
31496     {
31497         if(!this.delegates.length){
31498             this.progressDialog.hide();
31499             this.refresh();
31500             return;
31501         }
31502         
31503         var delegate = this.delegates.shift();
31504         
31505         this.progressDialog.show();
31506         
31507         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31508         
31509         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31510         
31511         delegate();
31512     },
31513     
31514     refresh : function()
31515     {
31516         this.uploader.show();
31517         
31518         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31519             this.uploader.hide();
31520         }
31521         
31522         Roo.isTouch ? this.closable(false) : this.closable(true);
31523         
31524         this.fireEvent('refresh', this);
31525     },
31526     
31527     onRemove : function(e, el, o)
31528     {
31529         e.preventDefault();
31530         
31531         this.fireEvent('remove', this, o);
31532         
31533     },
31534     
31535     remove : function(o)
31536     {
31537         var files = [];
31538         
31539         Roo.each(this.files, function(file){
31540             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31541                 files.push(file);
31542                 return;
31543             }
31544
31545             o.target.remove();
31546
31547         }, this);
31548         
31549         this.files = files;
31550         
31551         this.refresh();
31552     },
31553     
31554     clear : function()
31555     {
31556         Roo.each(this.files, function(file){
31557             if(!file.target){
31558                 return;
31559             }
31560             
31561             file.target.remove();
31562
31563         }, this);
31564         
31565         this.files = [];
31566         
31567         this.refresh();
31568     },
31569     
31570     onClick : function(e, el, o)
31571     {
31572         e.preventDefault();
31573         
31574         this.fireEvent('click', this, o);
31575         
31576     },
31577     
31578     closable : function(closable)
31579     {
31580         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31581             
31582             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31583             
31584             if(closable){
31585                 el.show();
31586                 return;
31587             }
31588             
31589             el.hide();
31590             
31591         }, this);
31592     },
31593     
31594     xhrOnLoad : function(xhr)
31595     {
31596         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31597             el.remove();
31598         }, this);
31599         
31600         if (xhr.readyState !== 4) {
31601             this.arrange();
31602             this.fireEvent('exception', this, xhr);
31603             return;
31604         }
31605
31606         var response = Roo.decode(xhr.responseText);
31607         
31608         if(!response.success){
31609             this.arrange();
31610             this.fireEvent('exception', this, xhr);
31611             return;
31612         }
31613         
31614         var file = this.renderPreview(response.data);
31615         
31616         this.files.push(file);
31617         
31618         this.arrange();
31619         
31620         this.fireEvent('afterupload', this, xhr);
31621         
31622     },
31623     
31624     xhrOnError : function(xhr)
31625     {
31626         Roo.log('xhr on error');
31627         
31628         var response = Roo.decode(xhr.responseText);
31629           
31630         Roo.log(response);
31631         
31632         this.arrange();
31633     },
31634     
31635     process : function(file)
31636     {
31637         if(this.fireEvent('process', this, file) !== false){
31638             if(this.editable && file.type.indexOf('image') != -1){
31639                 this.fireEvent('edit', this, file);
31640                 return;
31641             }
31642
31643             this.uploadStart(file, false);
31644
31645             return;
31646         }
31647         
31648     },
31649     
31650     uploadStart : function(file, crop)
31651     {
31652         this.xhr = new XMLHttpRequest();
31653         
31654         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31655             this.arrange();
31656             return;
31657         }
31658         
31659         file.xhr = this.xhr;
31660             
31661         this.managerEl.createChild({
31662             tag : 'div',
31663             cls : 'roo-document-manager-loading',
31664             cn : [
31665                 {
31666                     tag : 'div',
31667                     tooltip : file.name,
31668                     cls : 'roo-document-manager-thumb',
31669                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31670                 }
31671             ]
31672
31673         });
31674
31675         this.xhr.open(this.method, this.url, true);
31676         
31677         var headers = {
31678             "Accept": "application/json",
31679             "Cache-Control": "no-cache",
31680             "X-Requested-With": "XMLHttpRequest"
31681         };
31682         
31683         for (var headerName in headers) {
31684             var headerValue = headers[headerName];
31685             if (headerValue) {
31686                 this.xhr.setRequestHeader(headerName, headerValue);
31687             }
31688         }
31689         
31690         var _this = this;
31691         
31692         this.xhr.onload = function()
31693         {
31694             _this.xhrOnLoad(_this.xhr);
31695         }
31696         
31697         this.xhr.onerror = function()
31698         {
31699             _this.xhrOnError(_this.xhr);
31700         }
31701         
31702         var formData = new FormData();
31703
31704         formData.append('returnHTML', 'NO');
31705         
31706         if(crop){
31707             formData.append('crop', crop);
31708         }
31709         
31710         formData.append(this.paramName, file, file.name);
31711         
31712         var options = {
31713             file : file, 
31714             manually : false
31715         };
31716         
31717         if(this.fireEvent('prepare', this, formData, options) != false){
31718             
31719             if(options.manually){
31720                 return;
31721             }
31722             
31723             this.xhr.send(formData);
31724             return;
31725         };
31726         
31727         this.uploadCancel();
31728     },
31729     
31730     uploadCancel : function()
31731     {
31732         if (this.xhr) {
31733             this.xhr.abort();
31734         }
31735         
31736         this.delegates = [];
31737         
31738         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31739             el.remove();
31740         }, this);
31741         
31742         this.arrange();
31743     },
31744     
31745     renderPreview : function(file)
31746     {
31747         if(typeof(file.target) != 'undefined' && file.target){
31748             return file;
31749         }
31750         
31751         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31752         
31753         var previewEl = this.managerEl.createChild({
31754             tag : 'div',
31755             cls : 'roo-document-manager-preview',
31756             cn : [
31757                 {
31758                     tag : 'div',
31759                     tooltip : file[this.toolTipName],
31760                     cls : 'roo-document-manager-thumb',
31761                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31762                 },
31763                 {
31764                     tag : 'button',
31765                     cls : 'close',
31766                     html : '<i class="fa fa-times-circle"></i>'
31767                 }
31768             ]
31769         });
31770
31771         var close = previewEl.select('button.close', true).first();
31772
31773         close.on('click', this.onRemove, this, file);
31774
31775         file.target = previewEl;
31776
31777         var image = previewEl.select('img', true).first();
31778         
31779         var _this = this;
31780         
31781         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31782         
31783         image.on('click', this.onClick, this, file);
31784         
31785         this.fireEvent('previewrendered', this, file);
31786         
31787         return file;
31788         
31789     },
31790     
31791     onPreviewLoad : function(file, image)
31792     {
31793         if(typeof(file.target) == 'undefined' || !file.target){
31794             return;
31795         }
31796         
31797         var width = image.dom.naturalWidth || image.dom.width;
31798         var height = image.dom.naturalHeight || image.dom.height;
31799         
31800         if(!this.previewResize) {
31801             return;
31802         }
31803         
31804         if(width > height){
31805             file.target.addClass('wide');
31806             return;
31807         }
31808         
31809         file.target.addClass('tall');
31810         return;
31811         
31812     },
31813     
31814     uploadFromSource : function(file, crop)
31815     {
31816         this.xhr = new XMLHttpRequest();
31817         
31818         this.managerEl.createChild({
31819             tag : 'div',
31820             cls : 'roo-document-manager-loading',
31821             cn : [
31822                 {
31823                     tag : 'div',
31824                     tooltip : file.name,
31825                     cls : 'roo-document-manager-thumb',
31826                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31827                 }
31828             ]
31829
31830         });
31831
31832         this.xhr.open(this.method, this.url, true);
31833         
31834         var headers = {
31835             "Accept": "application/json",
31836             "Cache-Control": "no-cache",
31837             "X-Requested-With": "XMLHttpRequest"
31838         };
31839         
31840         for (var headerName in headers) {
31841             var headerValue = headers[headerName];
31842             if (headerValue) {
31843                 this.xhr.setRequestHeader(headerName, headerValue);
31844             }
31845         }
31846         
31847         var _this = this;
31848         
31849         this.xhr.onload = function()
31850         {
31851             _this.xhrOnLoad(_this.xhr);
31852         }
31853         
31854         this.xhr.onerror = function()
31855         {
31856             _this.xhrOnError(_this.xhr);
31857         }
31858         
31859         var formData = new FormData();
31860
31861         formData.append('returnHTML', 'NO');
31862         
31863         formData.append('crop', crop);
31864         
31865         if(typeof(file.filename) != 'undefined'){
31866             formData.append('filename', file.filename);
31867         }
31868         
31869         if(typeof(file.mimetype) != 'undefined'){
31870             formData.append('mimetype', file.mimetype);
31871         }
31872         
31873         Roo.log(formData);
31874         
31875         if(this.fireEvent('prepare', this, formData) != false){
31876             this.xhr.send(formData);
31877         };
31878     }
31879 });
31880
31881 /*
31882 * Licence: LGPL
31883 */
31884
31885 /**
31886  * @class Roo.bootstrap.DocumentViewer
31887  * @extends Roo.bootstrap.Component
31888  * Bootstrap DocumentViewer class
31889  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31890  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31891  * 
31892  * @constructor
31893  * Create a new DocumentViewer
31894  * @param {Object} config The config object
31895  */
31896
31897 Roo.bootstrap.DocumentViewer = function(config){
31898     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31899     
31900     this.addEvents({
31901         /**
31902          * @event initial
31903          * Fire after initEvent
31904          * @param {Roo.bootstrap.DocumentViewer} this
31905          */
31906         "initial" : true,
31907         /**
31908          * @event click
31909          * Fire after click
31910          * @param {Roo.bootstrap.DocumentViewer} this
31911          */
31912         "click" : true,
31913         /**
31914          * @event download
31915          * Fire after download button
31916          * @param {Roo.bootstrap.DocumentViewer} this
31917          */
31918         "download" : true,
31919         /**
31920          * @event trash
31921          * Fire after trash button
31922          * @param {Roo.bootstrap.DocumentViewer} this
31923          */
31924         "trash" : true
31925         
31926     });
31927 };
31928
31929 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31930     
31931     showDownload : true,
31932     
31933     showTrash : true,
31934     
31935     getAutoCreate : function()
31936     {
31937         var cfg = {
31938             tag : 'div',
31939             cls : 'roo-document-viewer',
31940             cn : [
31941                 {
31942                     tag : 'div',
31943                     cls : 'roo-document-viewer-body',
31944                     cn : [
31945                         {
31946                             tag : 'div',
31947                             cls : 'roo-document-viewer-thumb',
31948                             cn : [
31949                                 {
31950                                     tag : 'img',
31951                                     cls : 'roo-document-viewer-image'
31952                                 }
31953                             ]
31954                         }
31955                     ]
31956                 },
31957                 {
31958                     tag : 'div',
31959                     cls : 'roo-document-viewer-footer',
31960                     cn : {
31961                         tag : 'div',
31962                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31963                         cn : [
31964                             {
31965                                 tag : 'div',
31966                                 cls : 'btn-group roo-document-viewer-download',
31967                                 cn : [
31968                                     {
31969                                         tag : 'button',
31970                                         cls : 'btn btn-default',
31971                                         html : '<i class="fa fa-download"></i>'
31972                                     }
31973                                 ]
31974                             },
31975                             {
31976                                 tag : 'div',
31977                                 cls : 'btn-group roo-document-viewer-trash',
31978                                 cn : [
31979                                     {
31980                                         tag : 'button',
31981                                         cls : 'btn btn-default',
31982                                         html : '<i class="fa fa-trash"></i>'
31983                                     }
31984                                 ]
31985                             }
31986                         ]
31987                     }
31988                 }
31989             ]
31990         };
31991         
31992         return cfg;
31993     },
31994     
31995     initEvents : function()
31996     {
31997         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31998         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31999         
32000         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32001         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32002         
32003         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32004         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32005         
32006         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32007         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32008         
32009         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32010         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32011         
32012         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32013         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32014         
32015         this.bodyEl.on('click', this.onClick, this);
32016         this.downloadBtn.on('click', this.onDownload, this);
32017         this.trashBtn.on('click', this.onTrash, this);
32018         
32019         this.downloadBtn.hide();
32020         this.trashBtn.hide();
32021         
32022         if(this.showDownload){
32023             this.downloadBtn.show();
32024         }
32025         
32026         if(this.showTrash){
32027             this.trashBtn.show();
32028         }
32029         
32030         if(!this.showDownload && !this.showTrash) {
32031             this.footerEl.hide();
32032         }
32033         
32034     },
32035     
32036     initial : function()
32037     {
32038         this.fireEvent('initial', this);
32039         
32040     },
32041     
32042     onClick : function(e)
32043     {
32044         e.preventDefault();
32045         
32046         this.fireEvent('click', this);
32047     },
32048     
32049     onDownload : function(e)
32050     {
32051         e.preventDefault();
32052         
32053         this.fireEvent('download', this);
32054     },
32055     
32056     onTrash : function(e)
32057     {
32058         e.preventDefault();
32059         
32060         this.fireEvent('trash', this);
32061     }
32062     
32063 });
32064 /*
32065  * - LGPL
32066  *
32067  * nav progress bar
32068  * 
32069  */
32070
32071 /**
32072  * @class Roo.bootstrap.NavProgressBar
32073  * @extends Roo.bootstrap.Component
32074  * Bootstrap NavProgressBar class
32075  * 
32076  * @constructor
32077  * Create a new nav progress bar
32078  * @param {Object} config The config object
32079  */
32080
32081 Roo.bootstrap.NavProgressBar = function(config){
32082     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32083
32084     this.bullets = this.bullets || [];
32085    
32086 //    Roo.bootstrap.NavProgressBar.register(this);
32087      this.addEvents({
32088         /**
32089              * @event changed
32090              * Fires when the active item changes
32091              * @param {Roo.bootstrap.NavProgressBar} this
32092              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32093              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32094          */
32095         'changed': true
32096      });
32097     
32098 };
32099
32100 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32101     
32102     bullets : [],
32103     barItems : [],
32104     
32105     getAutoCreate : function()
32106     {
32107         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32108         
32109         cfg = {
32110             tag : 'div',
32111             cls : 'roo-navigation-bar-group',
32112             cn : [
32113                 {
32114                     tag : 'div',
32115                     cls : 'roo-navigation-top-bar'
32116                 },
32117                 {
32118                     tag : 'div',
32119                     cls : 'roo-navigation-bullets-bar',
32120                     cn : [
32121                         {
32122                             tag : 'ul',
32123                             cls : 'roo-navigation-bar'
32124                         }
32125                     ]
32126                 },
32127                 
32128                 {
32129                     tag : 'div',
32130                     cls : 'roo-navigation-bottom-bar'
32131                 }
32132             ]
32133             
32134         };
32135         
32136         return cfg;
32137         
32138     },
32139     
32140     initEvents: function() 
32141     {
32142         
32143     },
32144     
32145     onRender : function(ct, position) 
32146     {
32147         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32148         
32149         if(this.bullets.length){
32150             Roo.each(this.bullets, function(b){
32151                this.addItem(b);
32152             }, this);
32153         }
32154         
32155         this.format();
32156         
32157     },
32158     
32159     addItem : function(cfg)
32160     {
32161         var item = new Roo.bootstrap.NavProgressItem(cfg);
32162         
32163         item.parentId = this.id;
32164         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32165         
32166         if(cfg.html){
32167             var top = new Roo.bootstrap.Element({
32168                 tag : 'div',
32169                 cls : 'roo-navigation-bar-text'
32170             });
32171             
32172             var bottom = new Roo.bootstrap.Element({
32173                 tag : 'div',
32174                 cls : 'roo-navigation-bar-text'
32175             });
32176             
32177             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32178             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32179             
32180             var topText = new Roo.bootstrap.Element({
32181                 tag : 'span',
32182                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32183             });
32184             
32185             var bottomText = new Roo.bootstrap.Element({
32186                 tag : 'span',
32187                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32188             });
32189             
32190             topText.onRender(top.el, null);
32191             bottomText.onRender(bottom.el, null);
32192             
32193             item.topEl = top;
32194             item.bottomEl = bottom;
32195         }
32196         
32197         this.barItems.push(item);
32198         
32199         return item;
32200     },
32201     
32202     getActive : function()
32203     {
32204         var active = false;
32205         
32206         Roo.each(this.barItems, function(v){
32207             
32208             if (!v.isActive()) {
32209                 return;
32210             }
32211             
32212             active = v;
32213             return false;
32214             
32215         });
32216         
32217         return active;
32218     },
32219     
32220     setActiveItem : function(item)
32221     {
32222         var prev = false;
32223         
32224         Roo.each(this.barItems, function(v){
32225             if (v.rid == item.rid) {
32226                 return ;
32227             }
32228             
32229             if (v.isActive()) {
32230                 v.setActive(false);
32231                 prev = v;
32232             }
32233         });
32234
32235         item.setActive(true);
32236         
32237         this.fireEvent('changed', this, item, prev);
32238     },
32239     
32240     getBarItem: function(rid)
32241     {
32242         var ret = false;
32243         
32244         Roo.each(this.barItems, function(e) {
32245             if (e.rid != rid) {
32246                 return;
32247             }
32248             
32249             ret =  e;
32250             return false;
32251         });
32252         
32253         return ret;
32254     },
32255     
32256     indexOfItem : function(item)
32257     {
32258         var index = false;
32259         
32260         Roo.each(this.barItems, function(v, i){
32261             
32262             if (v.rid != item.rid) {
32263                 return;
32264             }
32265             
32266             index = i;
32267             return false
32268         });
32269         
32270         return index;
32271     },
32272     
32273     setActiveNext : function()
32274     {
32275         var i = this.indexOfItem(this.getActive());
32276         
32277         if (i > this.barItems.length) {
32278             return;
32279         }
32280         
32281         this.setActiveItem(this.barItems[i+1]);
32282     },
32283     
32284     setActivePrev : function()
32285     {
32286         var i = this.indexOfItem(this.getActive());
32287         
32288         if (i  < 1) {
32289             return;
32290         }
32291         
32292         this.setActiveItem(this.barItems[i-1]);
32293     },
32294     
32295     format : function()
32296     {
32297         if(!this.barItems.length){
32298             return;
32299         }
32300      
32301         var width = 100 / this.barItems.length;
32302         
32303         Roo.each(this.barItems, function(i){
32304             i.el.setStyle('width', width + '%');
32305             i.topEl.el.setStyle('width', width + '%');
32306             i.bottomEl.el.setStyle('width', width + '%');
32307         }, this);
32308         
32309     }
32310     
32311 });
32312 /*
32313  * - LGPL
32314  *
32315  * Nav Progress Item
32316  * 
32317  */
32318
32319 /**
32320  * @class Roo.bootstrap.NavProgressItem
32321  * @extends Roo.bootstrap.Component
32322  * Bootstrap NavProgressItem class
32323  * @cfg {String} rid the reference id
32324  * @cfg {Boolean} active (true|false) Is item active default false
32325  * @cfg {Boolean} disabled (true|false) Is item active default false
32326  * @cfg {String} html
32327  * @cfg {String} position (top|bottom) text position default bottom
32328  * @cfg {String} icon show icon instead of number
32329  * 
32330  * @constructor
32331  * Create a new NavProgressItem
32332  * @param {Object} config The config object
32333  */
32334 Roo.bootstrap.NavProgressItem = function(config){
32335     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32336     this.addEvents({
32337         // raw events
32338         /**
32339          * @event click
32340          * The raw click event for the entire grid.
32341          * @param {Roo.bootstrap.NavProgressItem} this
32342          * @param {Roo.EventObject} e
32343          */
32344         "click" : true
32345     });
32346    
32347 };
32348
32349 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32350     
32351     rid : '',
32352     active : false,
32353     disabled : false,
32354     html : '',
32355     position : 'bottom',
32356     icon : false,
32357     
32358     getAutoCreate : function()
32359     {
32360         var iconCls = 'roo-navigation-bar-item-icon';
32361         
32362         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32363         
32364         var cfg = {
32365             tag: 'li',
32366             cls: 'roo-navigation-bar-item',
32367             cn : [
32368                 {
32369                     tag : 'i',
32370                     cls : iconCls
32371                 }
32372             ]
32373         };
32374         
32375         if(this.active){
32376             cfg.cls += ' active';
32377         }
32378         if(this.disabled){
32379             cfg.cls += ' disabled';
32380         }
32381         
32382         return cfg;
32383     },
32384     
32385     disable : function()
32386     {
32387         this.setDisabled(true);
32388     },
32389     
32390     enable : function()
32391     {
32392         this.setDisabled(false);
32393     },
32394     
32395     initEvents: function() 
32396     {
32397         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32398         
32399         this.iconEl.on('click', this.onClick, this);
32400     },
32401     
32402     onClick : function(e)
32403     {
32404         e.preventDefault();
32405         
32406         if(this.disabled){
32407             return;
32408         }
32409         
32410         if(this.fireEvent('click', this, e) === false){
32411             return;
32412         };
32413         
32414         this.parent().setActiveItem(this);
32415     },
32416     
32417     isActive: function () 
32418     {
32419         return this.active;
32420     },
32421     
32422     setActive : function(state)
32423     {
32424         if(this.active == state){
32425             return;
32426         }
32427         
32428         this.active = state;
32429         
32430         if (state) {
32431             this.el.addClass('active');
32432             return;
32433         }
32434         
32435         this.el.removeClass('active');
32436         
32437         return;
32438     },
32439     
32440     setDisabled : function(state)
32441     {
32442         if(this.disabled == state){
32443             return;
32444         }
32445         
32446         this.disabled = state;
32447         
32448         if (state) {
32449             this.el.addClass('disabled');
32450             return;
32451         }
32452         
32453         this.el.removeClass('disabled');
32454     },
32455     
32456     tooltipEl : function()
32457     {
32458         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32459     }
32460 });
32461  
32462
32463  /*
32464  * - LGPL
32465  *
32466  * FieldLabel
32467  * 
32468  */
32469
32470 /**
32471  * @class Roo.bootstrap.FieldLabel
32472  * @extends Roo.bootstrap.Component
32473  * Bootstrap FieldLabel class
32474  * @cfg {String} html contents of the element
32475  * @cfg {String} tag tag of the element default label
32476  * @cfg {String} cls class of the element
32477  * @cfg {String} target label target 
32478  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32479  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32480  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32481  * @cfg {String} iconTooltip default "This field is required"
32482  * @cfg {String} indicatorpos (left|right) default left
32483  * 
32484  * @constructor
32485  * Create a new FieldLabel
32486  * @param {Object} config The config object
32487  */
32488
32489 Roo.bootstrap.FieldLabel = function(config){
32490     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32491     
32492     this.addEvents({
32493             /**
32494              * @event invalid
32495              * Fires after the field has been marked as invalid.
32496              * @param {Roo.form.FieldLabel} this
32497              * @param {String} msg The validation message
32498              */
32499             invalid : true,
32500             /**
32501              * @event valid
32502              * Fires after the field has been validated with no errors.
32503              * @param {Roo.form.FieldLabel} this
32504              */
32505             valid : true
32506         });
32507 };
32508
32509 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32510     
32511     tag: 'label',
32512     cls: '',
32513     html: '',
32514     target: '',
32515     allowBlank : true,
32516     invalidClass : 'has-warning',
32517     validClass : 'has-success',
32518     iconTooltip : 'This field is required',
32519     indicatorpos : 'left',
32520     
32521     getAutoCreate : function(){
32522         
32523         var cls = "";
32524         if (!this.allowBlank) {
32525             cls  = "visible";
32526         }
32527         
32528         var cfg = {
32529             tag : this.tag,
32530             cls : 'roo-bootstrap-field-label ' + this.cls,
32531             for : this.target,
32532             cn : [
32533                 {
32534                     tag : 'i',
32535                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32536                     tooltip : this.iconTooltip
32537                 },
32538                 {
32539                     tag : 'span',
32540                     html : this.html
32541                 }
32542             ] 
32543         };
32544         
32545         if(this.indicatorpos == 'right'){
32546             var cfg = {
32547                 tag : this.tag,
32548                 cls : 'roo-bootstrap-field-label ' + this.cls,
32549                 for : this.target,
32550                 cn : [
32551                     {
32552                         tag : 'span',
32553                         html : this.html
32554                     },
32555                     {
32556                         tag : 'i',
32557                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32558                         tooltip : this.iconTooltip
32559                     }
32560                 ] 
32561             };
32562         }
32563         
32564         return cfg;
32565     },
32566     
32567     initEvents: function() 
32568     {
32569         Roo.bootstrap.Element.superclass.initEvents.call(this);
32570         
32571         this.indicator = this.indicatorEl();
32572         
32573         if(this.indicator){
32574             this.indicator.removeClass('visible');
32575             this.indicator.addClass('invisible');
32576         }
32577         
32578         Roo.bootstrap.FieldLabel.register(this);
32579     },
32580     
32581     indicatorEl : function()
32582     {
32583         var indicator = this.el.select('i.roo-required-indicator',true).first();
32584         
32585         if(!indicator){
32586             return false;
32587         }
32588         
32589         return indicator;
32590         
32591     },
32592     
32593     /**
32594      * Mark this field as valid
32595      */
32596     markValid : function()
32597     {
32598         if(this.indicator){
32599             this.indicator.removeClass('visible');
32600             this.indicator.addClass('invisible');
32601         }
32602         if (Roo.bootstrap.version == 3) {
32603             this.el.removeClass(this.invalidClass);
32604             this.el.addClass(this.validClass);
32605         } else {
32606             this.el.removeClass('is-invalid');
32607             this.el.addClass('is-valid');
32608         }
32609         
32610         
32611         this.fireEvent('valid', this);
32612     },
32613     
32614     /**
32615      * Mark this field as invalid
32616      * @param {String} msg The validation message
32617      */
32618     markInvalid : function(msg)
32619     {
32620         if(this.indicator){
32621             this.indicator.removeClass('invisible');
32622             this.indicator.addClass('visible');
32623         }
32624           if (Roo.bootstrap.version == 3) {
32625             this.el.removeClass(this.validClass);
32626             this.el.addClass(this.invalidClass);
32627         } else {
32628             this.el.removeClass('is-valid');
32629             this.el.addClass('is-invalid');
32630         }
32631         
32632         
32633         this.fireEvent('invalid', this, msg);
32634     }
32635     
32636    
32637 });
32638
32639 Roo.apply(Roo.bootstrap.FieldLabel, {
32640     
32641     groups: {},
32642     
32643      /**
32644     * register a FieldLabel Group
32645     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32646     */
32647     register : function(label)
32648     {
32649         if(this.groups.hasOwnProperty(label.target)){
32650             return;
32651         }
32652      
32653         this.groups[label.target] = label;
32654         
32655     },
32656     /**
32657     * fetch a FieldLabel Group based on the target
32658     * @param {string} target
32659     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32660     */
32661     get: function(target) {
32662         if (typeof(this.groups[target]) == 'undefined') {
32663             return false;
32664         }
32665         
32666         return this.groups[target] ;
32667     }
32668 });
32669
32670  
32671
32672  /*
32673  * - LGPL
32674  *
32675  * page DateSplitField.
32676  * 
32677  */
32678
32679
32680 /**
32681  * @class Roo.bootstrap.DateSplitField
32682  * @extends Roo.bootstrap.Component
32683  * Bootstrap DateSplitField class
32684  * @cfg {string} fieldLabel - the label associated
32685  * @cfg {Number} labelWidth set the width of label (0-12)
32686  * @cfg {String} labelAlign (top|left)
32687  * @cfg {Boolean} dayAllowBlank (true|false) default false
32688  * @cfg {Boolean} monthAllowBlank (true|false) default false
32689  * @cfg {Boolean} yearAllowBlank (true|false) default false
32690  * @cfg {string} dayPlaceholder 
32691  * @cfg {string} monthPlaceholder
32692  * @cfg {string} yearPlaceholder
32693  * @cfg {string} dayFormat default 'd'
32694  * @cfg {string} monthFormat default 'm'
32695  * @cfg {string} yearFormat default 'Y'
32696  * @cfg {Number} labellg set the width of label (1-12)
32697  * @cfg {Number} labelmd set the width of label (1-12)
32698  * @cfg {Number} labelsm set the width of label (1-12)
32699  * @cfg {Number} labelxs set the width of label (1-12)
32700
32701  *     
32702  * @constructor
32703  * Create a new DateSplitField
32704  * @param {Object} config The config object
32705  */
32706
32707 Roo.bootstrap.DateSplitField = function(config){
32708     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32709     
32710     this.addEvents({
32711         // raw events
32712          /**
32713          * @event years
32714          * getting the data of years
32715          * @param {Roo.bootstrap.DateSplitField} this
32716          * @param {Object} years
32717          */
32718         "years" : true,
32719         /**
32720          * @event days
32721          * getting the data of days
32722          * @param {Roo.bootstrap.DateSplitField} this
32723          * @param {Object} days
32724          */
32725         "days" : true,
32726         /**
32727          * @event invalid
32728          * Fires after the field has been marked as invalid.
32729          * @param {Roo.form.Field} this
32730          * @param {String} msg The validation message
32731          */
32732         invalid : true,
32733        /**
32734          * @event valid
32735          * Fires after the field has been validated with no errors.
32736          * @param {Roo.form.Field} this
32737          */
32738         valid : true
32739     });
32740 };
32741
32742 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32743     
32744     fieldLabel : '',
32745     labelAlign : 'top',
32746     labelWidth : 3,
32747     dayAllowBlank : false,
32748     monthAllowBlank : false,
32749     yearAllowBlank : false,
32750     dayPlaceholder : '',
32751     monthPlaceholder : '',
32752     yearPlaceholder : '',
32753     dayFormat : 'd',
32754     monthFormat : 'm',
32755     yearFormat : 'Y',
32756     isFormField : true,
32757     labellg : 0,
32758     labelmd : 0,
32759     labelsm : 0,
32760     labelxs : 0,
32761     
32762     getAutoCreate : function()
32763     {
32764         var cfg = {
32765             tag : 'div',
32766             cls : 'row roo-date-split-field-group',
32767             cn : [
32768                 {
32769                     tag : 'input',
32770                     type : 'hidden',
32771                     cls : 'form-hidden-field roo-date-split-field-group-value',
32772                     name : this.name
32773                 }
32774             ]
32775         };
32776         
32777         var labelCls = 'col-md-12';
32778         var contentCls = 'col-md-4';
32779         
32780         if(this.fieldLabel){
32781             
32782             var label = {
32783                 tag : 'div',
32784                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32785                 cn : [
32786                     {
32787                         tag : 'label',
32788                         html : this.fieldLabel
32789                     }
32790                 ]
32791             };
32792             
32793             if(this.labelAlign == 'left'){
32794             
32795                 if(this.labelWidth > 12){
32796                     label.style = "width: " + this.labelWidth + 'px';
32797                 }
32798
32799                 if(this.labelWidth < 13 && this.labelmd == 0){
32800                     this.labelmd = this.labelWidth;
32801                 }
32802
32803                 if(this.labellg > 0){
32804                     labelCls = ' col-lg-' + this.labellg;
32805                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32806                 }
32807
32808                 if(this.labelmd > 0){
32809                     labelCls = ' col-md-' + this.labelmd;
32810                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32811                 }
32812
32813                 if(this.labelsm > 0){
32814                     labelCls = ' col-sm-' + this.labelsm;
32815                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32816                 }
32817
32818                 if(this.labelxs > 0){
32819                     labelCls = ' col-xs-' + this.labelxs;
32820                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32821                 }
32822             }
32823             
32824             label.cls += ' ' + labelCls;
32825             
32826             cfg.cn.push(label);
32827         }
32828         
32829         Roo.each(['day', 'month', 'year'], function(t){
32830             cfg.cn.push({
32831                 tag : 'div',
32832                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32833             });
32834         }, this);
32835         
32836         return cfg;
32837     },
32838     
32839     inputEl: function ()
32840     {
32841         return this.el.select('.roo-date-split-field-group-value', true).first();
32842     },
32843     
32844     onRender : function(ct, position) 
32845     {
32846         var _this = this;
32847         
32848         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32849         
32850         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32851         
32852         this.dayField = new Roo.bootstrap.ComboBox({
32853             allowBlank : this.dayAllowBlank,
32854             alwaysQuery : true,
32855             displayField : 'value',
32856             editable : false,
32857             fieldLabel : '',
32858             forceSelection : true,
32859             mode : 'local',
32860             placeholder : this.dayPlaceholder,
32861             selectOnFocus : true,
32862             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32863             triggerAction : 'all',
32864             typeAhead : true,
32865             valueField : 'value',
32866             store : new Roo.data.SimpleStore({
32867                 data : (function() {    
32868                     var days = [];
32869                     _this.fireEvent('days', _this, days);
32870                     return days;
32871                 })(),
32872                 fields : [ 'value' ]
32873             }),
32874             listeners : {
32875                 select : function (_self, record, index)
32876                 {
32877                     _this.setValue(_this.getValue());
32878                 }
32879             }
32880         });
32881
32882         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32883         
32884         this.monthField = new Roo.bootstrap.MonthField({
32885             after : '<i class=\"fa fa-calendar\"></i>',
32886             allowBlank : this.monthAllowBlank,
32887             placeholder : this.monthPlaceholder,
32888             readOnly : true,
32889             listeners : {
32890                 render : function (_self)
32891                 {
32892                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32893                         e.preventDefault();
32894                         _self.focus();
32895                     });
32896                 },
32897                 select : function (_self, oldvalue, newvalue)
32898                 {
32899                     _this.setValue(_this.getValue());
32900                 }
32901             }
32902         });
32903         
32904         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32905         
32906         this.yearField = new Roo.bootstrap.ComboBox({
32907             allowBlank : this.yearAllowBlank,
32908             alwaysQuery : true,
32909             displayField : 'value',
32910             editable : false,
32911             fieldLabel : '',
32912             forceSelection : true,
32913             mode : 'local',
32914             placeholder : this.yearPlaceholder,
32915             selectOnFocus : true,
32916             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32917             triggerAction : 'all',
32918             typeAhead : true,
32919             valueField : 'value',
32920             store : new Roo.data.SimpleStore({
32921                 data : (function() {
32922                     var years = [];
32923                     _this.fireEvent('years', _this, years);
32924                     return years;
32925                 })(),
32926                 fields : [ 'value' ]
32927             }),
32928             listeners : {
32929                 select : function (_self, record, index)
32930                 {
32931                     _this.setValue(_this.getValue());
32932                 }
32933             }
32934         });
32935
32936         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32937     },
32938     
32939     setValue : function(v, format)
32940     {
32941         this.inputEl.dom.value = v;
32942         
32943         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32944         
32945         var d = Date.parseDate(v, f);
32946         
32947         if(!d){
32948             this.validate();
32949             return;
32950         }
32951         
32952         this.setDay(d.format(this.dayFormat));
32953         this.setMonth(d.format(this.monthFormat));
32954         this.setYear(d.format(this.yearFormat));
32955         
32956         this.validate();
32957         
32958         return;
32959     },
32960     
32961     setDay : function(v)
32962     {
32963         this.dayField.setValue(v);
32964         this.inputEl.dom.value = this.getValue();
32965         this.validate();
32966         return;
32967     },
32968     
32969     setMonth : function(v)
32970     {
32971         this.monthField.setValue(v, true);
32972         this.inputEl.dom.value = this.getValue();
32973         this.validate();
32974         return;
32975     },
32976     
32977     setYear : function(v)
32978     {
32979         this.yearField.setValue(v);
32980         this.inputEl.dom.value = this.getValue();
32981         this.validate();
32982         return;
32983     },
32984     
32985     getDay : function()
32986     {
32987         return this.dayField.getValue();
32988     },
32989     
32990     getMonth : function()
32991     {
32992         return this.monthField.getValue();
32993     },
32994     
32995     getYear : function()
32996     {
32997         return this.yearField.getValue();
32998     },
32999     
33000     getValue : function()
33001     {
33002         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33003         
33004         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33005         
33006         return date;
33007     },
33008     
33009     reset : function()
33010     {
33011         this.setDay('');
33012         this.setMonth('');
33013         this.setYear('');
33014         this.inputEl.dom.value = '';
33015         this.validate();
33016         return;
33017     },
33018     
33019     validate : function()
33020     {
33021         var d = this.dayField.validate();
33022         var m = this.monthField.validate();
33023         var y = this.yearField.validate();
33024         
33025         var valid = true;
33026         
33027         if(
33028                 (!this.dayAllowBlank && !d) ||
33029                 (!this.monthAllowBlank && !m) ||
33030                 (!this.yearAllowBlank && !y)
33031         ){
33032             valid = false;
33033         }
33034         
33035         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33036             return valid;
33037         }
33038         
33039         if(valid){
33040             this.markValid();
33041             return valid;
33042         }
33043         
33044         this.markInvalid();
33045         
33046         return valid;
33047     },
33048     
33049     markValid : function()
33050     {
33051         
33052         var label = this.el.select('label', true).first();
33053         var icon = this.el.select('i.fa-star', true).first();
33054
33055         if(label && icon){
33056             icon.remove();
33057         }
33058         
33059         this.fireEvent('valid', this);
33060     },
33061     
33062      /**
33063      * Mark this field as invalid
33064      * @param {String} msg The validation message
33065      */
33066     markInvalid : function(msg)
33067     {
33068         
33069         var label = this.el.select('label', true).first();
33070         var icon = this.el.select('i.fa-star', true).first();
33071
33072         if(label && !icon){
33073             this.el.select('.roo-date-split-field-label', true).createChild({
33074                 tag : 'i',
33075                 cls : 'text-danger fa fa-lg fa-star',
33076                 tooltip : 'This field is required',
33077                 style : 'margin-right:5px;'
33078             }, label, true);
33079         }
33080         
33081         this.fireEvent('invalid', this, msg);
33082     },
33083     
33084     clearInvalid : function()
33085     {
33086         var label = this.el.select('label', true).first();
33087         var icon = this.el.select('i.fa-star', true).first();
33088
33089         if(label && icon){
33090             icon.remove();
33091         }
33092         
33093         this.fireEvent('valid', this);
33094     },
33095     
33096     getName: function()
33097     {
33098         return this.name;
33099     }
33100     
33101 });
33102
33103  /**
33104  *
33105  * This is based on 
33106  * http://masonry.desandro.com
33107  *
33108  * The idea is to render all the bricks based on vertical width...
33109  *
33110  * The original code extends 'outlayer' - we might need to use that....
33111  * 
33112  */
33113
33114
33115 /**
33116  * @class Roo.bootstrap.LayoutMasonry
33117  * @extends Roo.bootstrap.Component
33118  * Bootstrap Layout Masonry class
33119  * 
33120  * @constructor
33121  * Create a new Element
33122  * @param {Object} config The config object
33123  */
33124
33125 Roo.bootstrap.LayoutMasonry = function(config){
33126     
33127     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33128     
33129     this.bricks = [];
33130     
33131     Roo.bootstrap.LayoutMasonry.register(this);
33132     
33133     this.addEvents({
33134         // raw events
33135         /**
33136          * @event layout
33137          * Fire after layout the items
33138          * @param {Roo.bootstrap.LayoutMasonry} this
33139          * @param {Roo.EventObject} e
33140          */
33141         "layout" : true
33142     });
33143     
33144 };
33145
33146 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33147     
33148     /**
33149      * @cfg {Boolean} isLayoutInstant = no animation?
33150      */   
33151     isLayoutInstant : false, // needed?
33152    
33153     /**
33154      * @cfg {Number} boxWidth  width of the columns
33155      */   
33156     boxWidth : 450,
33157     
33158       /**
33159      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33160      */   
33161     boxHeight : 0,
33162     
33163     /**
33164      * @cfg {Number} padWidth padding below box..
33165      */   
33166     padWidth : 10, 
33167     
33168     /**
33169      * @cfg {Number} gutter gutter width..
33170      */   
33171     gutter : 10,
33172     
33173      /**
33174      * @cfg {Number} maxCols maximum number of columns
33175      */   
33176     
33177     maxCols: 0,
33178     
33179     /**
33180      * @cfg {Boolean} isAutoInitial defalut true
33181      */   
33182     isAutoInitial : true, 
33183     
33184     containerWidth: 0,
33185     
33186     /**
33187      * @cfg {Boolean} isHorizontal defalut false
33188      */   
33189     isHorizontal : false, 
33190
33191     currentSize : null,
33192     
33193     tag: 'div',
33194     
33195     cls: '',
33196     
33197     bricks: null, //CompositeElement
33198     
33199     cols : 1,
33200     
33201     _isLayoutInited : false,
33202     
33203 //    isAlternative : false, // only use for vertical layout...
33204     
33205     /**
33206      * @cfg {Number} alternativePadWidth padding below box..
33207      */   
33208     alternativePadWidth : 50,
33209     
33210     selectedBrick : [],
33211     
33212     getAutoCreate : function(){
33213         
33214         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33215         
33216         var cfg = {
33217             tag: this.tag,
33218             cls: 'blog-masonary-wrapper ' + this.cls,
33219             cn : {
33220                 cls : 'mas-boxes masonary'
33221             }
33222         };
33223         
33224         return cfg;
33225     },
33226     
33227     getChildContainer: function( )
33228     {
33229         if (this.boxesEl) {
33230             return this.boxesEl;
33231         }
33232         
33233         this.boxesEl = this.el.select('.mas-boxes').first();
33234         
33235         return this.boxesEl;
33236     },
33237     
33238     
33239     initEvents : function()
33240     {
33241         var _this = this;
33242         
33243         if(this.isAutoInitial){
33244             Roo.log('hook children rendered');
33245             this.on('childrenrendered', function() {
33246                 Roo.log('children rendered');
33247                 _this.initial();
33248             } ,this);
33249         }
33250     },
33251     
33252     initial : function()
33253     {
33254         this.selectedBrick = [];
33255         
33256         this.currentSize = this.el.getBox(true);
33257         
33258         Roo.EventManager.onWindowResize(this.resize, this); 
33259
33260         if(!this.isAutoInitial){
33261             this.layout();
33262             return;
33263         }
33264         
33265         this.layout();
33266         
33267         return;
33268         //this.layout.defer(500,this);
33269         
33270     },
33271     
33272     resize : function()
33273     {
33274         var cs = this.el.getBox(true);
33275         
33276         if (
33277                 this.currentSize.width == cs.width && 
33278                 this.currentSize.x == cs.x && 
33279                 this.currentSize.height == cs.height && 
33280                 this.currentSize.y == cs.y 
33281         ) {
33282             Roo.log("no change in with or X or Y");
33283             return;
33284         }
33285         
33286         this.currentSize = cs;
33287         
33288         this.layout();
33289         
33290     },
33291     
33292     layout : function()
33293     {   
33294         this._resetLayout();
33295         
33296         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33297         
33298         this.layoutItems( isInstant );
33299       
33300         this._isLayoutInited = true;
33301         
33302         this.fireEvent('layout', this);
33303         
33304     },
33305     
33306     _resetLayout : function()
33307     {
33308         if(this.isHorizontal){
33309             this.horizontalMeasureColumns();
33310             return;
33311         }
33312         
33313         this.verticalMeasureColumns();
33314         
33315     },
33316     
33317     verticalMeasureColumns : function()
33318     {
33319         this.getContainerWidth();
33320         
33321 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33322 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33323 //            return;
33324 //        }
33325         
33326         var boxWidth = this.boxWidth + this.padWidth;
33327         
33328         if(this.containerWidth < this.boxWidth){
33329             boxWidth = this.containerWidth
33330         }
33331         
33332         var containerWidth = this.containerWidth;
33333         
33334         var cols = Math.floor(containerWidth / boxWidth);
33335         
33336         this.cols = Math.max( cols, 1 );
33337         
33338         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33339         
33340         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33341         
33342         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33343         
33344         this.colWidth = boxWidth + avail - this.padWidth;
33345         
33346         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33347         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33348     },
33349     
33350     horizontalMeasureColumns : function()
33351     {
33352         this.getContainerWidth();
33353         
33354         var boxWidth = this.boxWidth;
33355         
33356         if(this.containerWidth < boxWidth){
33357             boxWidth = this.containerWidth;
33358         }
33359         
33360         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33361         
33362         this.el.setHeight(boxWidth);
33363         
33364     },
33365     
33366     getContainerWidth : function()
33367     {
33368         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33369     },
33370     
33371     layoutItems : function( isInstant )
33372     {
33373         Roo.log(this.bricks);
33374         
33375         var items = Roo.apply([], this.bricks);
33376         
33377         if(this.isHorizontal){
33378             this._horizontalLayoutItems( items , isInstant );
33379             return;
33380         }
33381         
33382 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33383 //            this._verticalAlternativeLayoutItems( items , isInstant );
33384 //            return;
33385 //        }
33386         
33387         this._verticalLayoutItems( items , isInstant );
33388         
33389     },
33390     
33391     _verticalLayoutItems : function ( items , isInstant)
33392     {
33393         if ( !items || !items.length ) {
33394             return;
33395         }
33396         
33397         var standard = [
33398             ['xs', 'xs', 'xs', 'tall'],
33399             ['xs', 'xs', 'tall'],
33400             ['xs', 'xs', 'sm'],
33401             ['xs', 'xs', 'xs'],
33402             ['xs', 'tall'],
33403             ['xs', 'sm'],
33404             ['xs', 'xs'],
33405             ['xs'],
33406             
33407             ['sm', 'xs', 'xs'],
33408             ['sm', 'xs'],
33409             ['sm'],
33410             
33411             ['tall', 'xs', 'xs', 'xs'],
33412             ['tall', 'xs', 'xs'],
33413             ['tall', 'xs'],
33414             ['tall']
33415             
33416         ];
33417         
33418         var queue = [];
33419         
33420         var boxes = [];
33421         
33422         var box = [];
33423         
33424         Roo.each(items, function(item, k){
33425             
33426             switch (item.size) {
33427                 // these layouts take up a full box,
33428                 case 'md' :
33429                 case 'md-left' :
33430                 case 'md-right' :
33431                 case 'wide' :
33432                     
33433                     if(box.length){
33434                         boxes.push(box);
33435                         box = [];
33436                     }
33437                     
33438                     boxes.push([item]);
33439                     
33440                     break;
33441                     
33442                 case 'xs' :
33443                 case 'sm' :
33444                 case 'tall' :
33445                     
33446                     box.push(item);
33447                     
33448                     break;
33449                 default :
33450                     break;
33451                     
33452             }
33453             
33454         }, this);
33455         
33456         if(box.length){
33457             boxes.push(box);
33458             box = [];
33459         }
33460         
33461         var filterPattern = function(box, length)
33462         {
33463             if(!box.length){
33464                 return;
33465             }
33466             
33467             var match = false;
33468             
33469             var pattern = box.slice(0, length);
33470             
33471             var format = [];
33472             
33473             Roo.each(pattern, function(i){
33474                 format.push(i.size);
33475             }, this);
33476             
33477             Roo.each(standard, function(s){
33478                 
33479                 if(String(s) != String(format)){
33480                     return;
33481                 }
33482                 
33483                 match = true;
33484                 return false;
33485                 
33486             }, this);
33487             
33488             if(!match && length == 1){
33489                 return;
33490             }
33491             
33492             if(!match){
33493                 filterPattern(box, length - 1);
33494                 return;
33495             }
33496                 
33497             queue.push(pattern);
33498
33499             box = box.slice(length, box.length);
33500
33501             filterPattern(box, 4);
33502
33503             return;
33504             
33505         }
33506         
33507         Roo.each(boxes, function(box, k){
33508             
33509             if(!box.length){
33510                 return;
33511             }
33512             
33513             if(box.length == 1){
33514                 queue.push(box);
33515                 return;
33516             }
33517             
33518             filterPattern(box, 4);
33519             
33520         }, this);
33521         
33522         this._processVerticalLayoutQueue( queue, isInstant );
33523         
33524     },
33525     
33526 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33527 //    {
33528 //        if ( !items || !items.length ) {
33529 //            return;
33530 //        }
33531 //
33532 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33533 //        
33534 //    },
33535     
33536     _horizontalLayoutItems : function ( items , isInstant)
33537     {
33538         if ( !items || !items.length || items.length < 3) {
33539             return;
33540         }
33541         
33542         items.reverse();
33543         
33544         var eItems = items.slice(0, 3);
33545         
33546         items = items.slice(3, items.length);
33547         
33548         var standard = [
33549             ['xs', 'xs', 'xs', 'wide'],
33550             ['xs', 'xs', 'wide'],
33551             ['xs', 'xs', 'sm'],
33552             ['xs', 'xs', 'xs'],
33553             ['xs', 'wide'],
33554             ['xs', 'sm'],
33555             ['xs', 'xs'],
33556             ['xs'],
33557             
33558             ['sm', 'xs', 'xs'],
33559             ['sm', 'xs'],
33560             ['sm'],
33561             
33562             ['wide', 'xs', 'xs', 'xs'],
33563             ['wide', 'xs', 'xs'],
33564             ['wide', 'xs'],
33565             ['wide'],
33566             
33567             ['wide-thin']
33568         ];
33569         
33570         var queue = [];
33571         
33572         var boxes = [];
33573         
33574         var box = [];
33575         
33576         Roo.each(items, function(item, k){
33577             
33578             switch (item.size) {
33579                 case 'md' :
33580                 case 'md-left' :
33581                 case 'md-right' :
33582                 case 'tall' :
33583                     
33584                     if(box.length){
33585                         boxes.push(box);
33586                         box = [];
33587                     }
33588                     
33589                     boxes.push([item]);
33590                     
33591                     break;
33592                     
33593                 case 'xs' :
33594                 case 'sm' :
33595                 case 'wide' :
33596                 case 'wide-thin' :
33597                     
33598                     box.push(item);
33599                     
33600                     break;
33601                 default :
33602                     break;
33603                     
33604             }
33605             
33606         }, this);
33607         
33608         if(box.length){
33609             boxes.push(box);
33610             box = [];
33611         }
33612         
33613         var filterPattern = function(box, length)
33614         {
33615             if(!box.length){
33616                 return;
33617             }
33618             
33619             var match = false;
33620             
33621             var pattern = box.slice(0, length);
33622             
33623             var format = [];
33624             
33625             Roo.each(pattern, function(i){
33626                 format.push(i.size);
33627             }, this);
33628             
33629             Roo.each(standard, function(s){
33630                 
33631                 if(String(s) != String(format)){
33632                     return;
33633                 }
33634                 
33635                 match = true;
33636                 return false;
33637                 
33638             }, this);
33639             
33640             if(!match && length == 1){
33641                 return;
33642             }
33643             
33644             if(!match){
33645                 filterPattern(box, length - 1);
33646                 return;
33647             }
33648                 
33649             queue.push(pattern);
33650
33651             box = box.slice(length, box.length);
33652
33653             filterPattern(box, 4);
33654
33655             return;
33656             
33657         }
33658         
33659         Roo.each(boxes, function(box, k){
33660             
33661             if(!box.length){
33662                 return;
33663             }
33664             
33665             if(box.length == 1){
33666                 queue.push(box);
33667                 return;
33668             }
33669             
33670             filterPattern(box, 4);
33671             
33672         }, this);
33673         
33674         
33675         var prune = [];
33676         
33677         var pos = this.el.getBox(true);
33678         
33679         var minX = pos.x;
33680         
33681         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33682         
33683         var hit_end = false;
33684         
33685         Roo.each(queue, function(box){
33686             
33687             if(hit_end){
33688                 
33689                 Roo.each(box, function(b){
33690                 
33691                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33692                     b.el.hide();
33693
33694                 }, this);
33695
33696                 return;
33697             }
33698             
33699             var mx = 0;
33700             
33701             Roo.each(box, function(b){
33702                 
33703                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33704                 b.el.show();
33705
33706                 mx = Math.max(mx, b.x);
33707                 
33708             }, this);
33709             
33710             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33711             
33712             if(maxX < minX){
33713                 
33714                 Roo.each(box, function(b){
33715                 
33716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33717                     b.el.hide();
33718                     
33719                 }, this);
33720                 
33721                 hit_end = true;
33722                 
33723                 return;
33724             }
33725             
33726             prune.push(box);
33727             
33728         }, this);
33729         
33730         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33731     },
33732     
33733     /** Sets position of item in DOM
33734     * @param {Element} item
33735     * @param {Number} x - horizontal position
33736     * @param {Number} y - vertical position
33737     * @param {Boolean} isInstant - disables transitions
33738     */
33739     _processVerticalLayoutQueue : function( queue, isInstant )
33740     {
33741         var pos = this.el.getBox(true);
33742         var x = pos.x;
33743         var y = pos.y;
33744         var maxY = [];
33745         
33746         for (var i = 0; i < this.cols; i++){
33747             maxY[i] = pos.y;
33748         }
33749         
33750         Roo.each(queue, function(box, k){
33751             
33752             var col = k % this.cols;
33753             
33754             Roo.each(box, function(b,kk){
33755                 
33756                 b.el.position('absolute');
33757                 
33758                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33759                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33760                 
33761                 if(b.size == 'md-left' || b.size == 'md-right'){
33762                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33763                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33764                 }
33765                 
33766                 b.el.setWidth(width);
33767                 b.el.setHeight(height);
33768                 // iframe?
33769                 b.el.select('iframe',true).setSize(width,height);
33770                 
33771             }, this);
33772             
33773             for (var i = 0; i < this.cols; i++){
33774                 
33775                 if(maxY[i] < maxY[col]){
33776                     col = i;
33777                     continue;
33778                 }
33779                 
33780                 col = Math.min(col, i);
33781                 
33782             }
33783             
33784             x = pos.x + col * (this.colWidth + this.padWidth);
33785             
33786             y = maxY[col];
33787             
33788             var positions = [];
33789             
33790             switch (box.length){
33791                 case 1 :
33792                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33793                     break;
33794                 case 2 :
33795                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33796                     break;
33797                 case 3 :
33798                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33799                     break;
33800                 case 4 :
33801                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33802                     break;
33803                 default :
33804                     break;
33805             }
33806             
33807             Roo.each(box, function(b,kk){
33808                 
33809                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33810                 
33811                 var sz = b.el.getSize();
33812                 
33813                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33814                 
33815             }, this);
33816             
33817         }, this);
33818         
33819         var mY = 0;
33820         
33821         for (var i = 0; i < this.cols; i++){
33822             mY = Math.max(mY, maxY[i]);
33823         }
33824         
33825         this.el.setHeight(mY - pos.y);
33826         
33827     },
33828     
33829 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33830 //    {
33831 //        var pos = this.el.getBox(true);
33832 //        var x = pos.x;
33833 //        var y = pos.y;
33834 //        var maxX = pos.right;
33835 //        
33836 //        var maxHeight = 0;
33837 //        
33838 //        Roo.each(items, function(item, k){
33839 //            
33840 //            var c = k % 2;
33841 //            
33842 //            item.el.position('absolute');
33843 //                
33844 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33845 //
33846 //            item.el.setWidth(width);
33847 //
33848 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33849 //
33850 //            item.el.setHeight(height);
33851 //            
33852 //            if(c == 0){
33853 //                item.el.setXY([x, y], isInstant ? false : true);
33854 //            } else {
33855 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33856 //            }
33857 //            
33858 //            y = y + height + this.alternativePadWidth;
33859 //            
33860 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33861 //            
33862 //        }, this);
33863 //        
33864 //        this.el.setHeight(maxHeight);
33865 //        
33866 //    },
33867     
33868     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33869     {
33870         var pos = this.el.getBox(true);
33871         
33872         var minX = pos.x;
33873         var minY = pos.y;
33874         
33875         var maxX = pos.right;
33876         
33877         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33878         
33879         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33880         
33881         Roo.each(queue, function(box, k){
33882             
33883             Roo.each(box, function(b, kk){
33884                 
33885                 b.el.position('absolute');
33886                 
33887                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33888                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33889                 
33890                 if(b.size == 'md-left' || b.size == 'md-right'){
33891                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33892                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33893                 }
33894                 
33895                 b.el.setWidth(width);
33896                 b.el.setHeight(height);
33897                 
33898             }, this);
33899             
33900             if(!box.length){
33901                 return;
33902             }
33903             
33904             var positions = [];
33905             
33906             switch (box.length){
33907                 case 1 :
33908                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33909                     break;
33910                 case 2 :
33911                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33912                     break;
33913                 case 3 :
33914                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33915                     break;
33916                 case 4 :
33917                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33918                     break;
33919                 default :
33920                     break;
33921             }
33922             
33923             Roo.each(box, function(b,kk){
33924                 
33925                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33926                 
33927                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33928                 
33929             }, this);
33930             
33931         }, this);
33932         
33933     },
33934     
33935     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33936     {
33937         Roo.each(eItems, function(b,k){
33938             
33939             b.size = (k == 0) ? 'sm' : 'xs';
33940             b.x = (k == 0) ? 2 : 1;
33941             b.y = (k == 0) ? 2 : 1;
33942             
33943             b.el.position('absolute');
33944             
33945             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33946                 
33947             b.el.setWidth(width);
33948             
33949             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33950             
33951             b.el.setHeight(height);
33952             
33953         }, this);
33954
33955         var positions = [];
33956         
33957         positions.push({
33958             x : maxX - this.unitWidth * 2 - this.gutter,
33959             y : minY
33960         });
33961         
33962         positions.push({
33963             x : maxX - this.unitWidth,
33964             y : minY + (this.unitWidth + this.gutter) * 2
33965         });
33966         
33967         positions.push({
33968             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33969             y : minY
33970         });
33971         
33972         Roo.each(eItems, function(b,k){
33973             
33974             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33975
33976         }, this);
33977         
33978     },
33979     
33980     getVerticalOneBoxColPositions : function(x, y, box)
33981     {
33982         var pos = [];
33983         
33984         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33985         
33986         if(box[0].size == 'md-left'){
33987             rand = 0;
33988         }
33989         
33990         if(box[0].size == 'md-right'){
33991             rand = 1;
33992         }
33993         
33994         pos.push({
33995             x : x + (this.unitWidth + this.gutter) * rand,
33996             y : y
33997         });
33998         
33999         return pos;
34000     },
34001     
34002     getVerticalTwoBoxColPositions : function(x, y, box)
34003     {
34004         var pos = [];
34005         
34006         if(box[0].size == 'xs'){
34007             
34008             pos.push({
34009                 x : x,
34010                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34011             });
34012
34013             pos.push({
34014                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34015                 y : y
34016             });
34017             
34018             return pos;
34019             
34020         }
34021         
34022         pos.push({
34023             x : x,
34024             y : y
34025         });
34026
34027         pos.push({
34028             x : x + (this.unitWidth + this.gutter) * 2,
34029             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34030         });
34031         
34032         return pos;
34033         
34034     },
34035     
34036     getVerticalThreeBoxColPositions : function(x, y, box)
34037     {
34038         var pos = [];
34039         
34040         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34041             
34042             pos.push({
34043                 x : x,
34044                 y : y
34045             });
34046
34047             pos.push({
34048                 x : x + (this.unitWidth + this.gutter) * 1,
34049                 y : y
34050             });
34051             
34052             pos.push({
34053                 x : x + (this.unitWidth + this.gutter) * 2,
34054                 y : y
34055             });
34056             
34057             return pos;
34058             
34059         }
34060         
34061         if(box[0].size == 'xs' && box[1].size == 'xs'){
34062             
34063             pos.push({
34064                 x : x,
34065                 y : y
34066             });
34067
34068             pos.push({
34069                 x : x,
34070                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34071             });
34072             
34073             pos.push({
34074                 x : x + (this.unitWidth + this.gutter) * 1,
34075                 y : y
34076             });
34077             
34078             return pos;
34079             
34080         }
34081         
34082         pos.push({
34083             x : x,
34084             y : y
34085         });
34086
34087         pos.push({
34088             x : x + (this.unitWidth + this.gutter) * 2,
34089             y : y
34090         });
34091
34092         pos.push({
34093             x : x + (this.unitWidth + this.gutter) * 2,
34094             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34095         });
34096             
34097         return pos;
34098         
34099     },
34100     
34101     getVerticalFourBoxColPositions : function(x, y, box)
34102     {
34103         var pos = [];
34104         
34105         if(box[0].size == 'xs'){
34106             
34107             pos.push({
34108                 x : x,
34109                 y : y
34110             });
34111
34112             pos.push({
34113                 x : x,
34114                 y : y + (this.unitHeight + this.gutter) * 1
34115             });
34116             
34117             pos.push({
34118                 x : x,
34119                 y : y + (this.unitHeight + this.gutter) * 2
34120             });
34121             
34122             pos.push({
34123                 x : x + (this.unitWidth + this.gutter) * 1,
34124                 y : y
34125             });
34126             
34127             return pos;
34128             
34129         }
34130         
34131         pos.push({
34132             x : x,
34133             y : y
34134         });
34135
34136         pos.push({
34137             x : x + (this.unitWidth + this.gutter) * 2,
34138             y : y
34139         });
34140
34141         pos.push({
34142             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34143             y : y + (this.unitHeight + this.gutter) * 1
34144         });
34145
34146         pos.push({
34147             x : x + (this.unitWidth + this.gutter) * 2,
34148             y : y + (this.unitWidth + this.gutter) * 2
34149         });
34150
34151         return pos;
34152         
34153     },
34154     
34155     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34156     {
34157         var pos = [];
34158         
34159         if(box[0].size == 'md-left'){
34160             pos.push({
34161                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34162                 y : minY
34163             });
34164             
34165             return pos;
34166         }
34167         
34168         if(box[0].size == 'md-right'){
34169             pos.push({
34170                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34171                 y : minY + (this.unitWidth + this.gutter) * 1
34172             });
34173             
34174             return pos;
34175         }
34176         
34177         var rand = Math.floor(Math.random() * (4 - box[0].y));
34178         
34179         pos.push({
34180             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34181             y : minY + (this.unitWidth + this.gutter) * rand
34182         });
34183         
34184         return pos;
34185         
34186     },
34187     
34188     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34189     {
34190         var pos = [];
34191         
34192         if(box[0].size == 'xs'){
34193             
34194             pos.push({
34195                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34196                 y : minY
34197             });
34198
34199             pos.push({
34200                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34201                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34202             });
34203             
34204             return pos;
34205             
34206         }
34207         
34208         pos.push({
34209             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34210             y : minY
34211         });
34212
34213         pos.push({
34214             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34215             y : minY + (this.unitWidth + this.gutter) * 2
34216         });
34217         
34218         return pos;
34219         
34220     },
34221     
34222     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34223     {
34224         var pos = [];
34225         
34226         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34227             
34228             pos.push({
34229                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34230                 y : minY
34231             });
34232
34233             pos.push({
34234                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34235                 y : minY + (this.unitWidth + this.gutter) * 1
34236             });
34237             
34238             pos.push({
34239                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34240                 y : minY + (this.unitWidth + this.gutter) * 2
34241             });
34242             
34243             return pos;
34244             
34245         }
34246         
34247         if(box[0].size == 'xs' && box[1].size == 'xs'){
34248             
34249             pos.push({
34250                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34251                 y : minY
34252             });
34253
34254             pos.push({
34255                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34256                 y : minY
34257             });
34258             
34259             pos.push({
34260                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34261                 y : minY + (this.unitWidth + this.gutter) * 1
34262             });
34263             
34264             return pos;
34265             
34266         }
34267         
34268         pos.push({
34269             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34270             y : minY
34271         });
34272
34273         pos.push({
34274             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34275             y : minY + (this.unitWidth + this.gutter) * 2
34276         });
34277
34278         pos.push({
34279             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34280             y : minY + (this.unitWidth + this.gutter) * 2
34281         });
34282             
34283         return pos;
34284         
34285     },
34286     
34287     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34288     {
34289         var pos = [];
34290         
34291         if(box[0].size == 'xs'){
34292             
34293             pos.push({
34294                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34295                 y : minY
34296             });
34297
34298             pos.push({
34299                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34300                 y : minY
34301             });
34302             
34303             pos.push({
34304                 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),
34305                 y : minY
34306             });
34307             
34308             pos.push({
34309                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34310                 y : minY + (this.unitWidth + this.gutter) * 1
34311             });
34312             
34313             return pos;
34314             
34315         }
34316         
34317         pos.push({
34318             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34319             y : minY
34320         });
34321         
34322         pos.push({
34323             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34324             y : minY + (this.unitWidth + this.gutter) * 2
34325         });
34326         
34327         pos.push({
34328             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34329             y : minY + (this.unitWidth + this.gutter) * 2
34330         });
34331         
34332         pos.push({
34333             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),
34334             y : minY + (this.unitWidth + this.gutter) * 2
34335         });
34336
34337         return pos;
34338         
34339     },
34340     
34341     /**
34342     * remove a Masonry Brick
34343     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34344     */
34345     removeBrick : function(brick_id)
34346     {
34347         if (!brick_id) {
34348             return;
34349         }
34350         
34351         for (var i = 0; i<this.bricks.length; i++) {
34352             if (this.bricks[i].id == brick_id) {
34353                 this.bricks.splice(i,1);
34354                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34355                 this.initial();
34356             }
34357         }
34358     },
34359     
34360     /**
34361     * adds a Masonry Brick
34362     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34363     */
34364     addBrick : function(cfg)
34365     {
34366         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34367         //this.register(cn);
34368         cn.parentId = this.id;
34369         cn.render(this.el);
34370         return cn;
34371     },
34372     
34373     /**
34374     * register a Masonry Brick
34375     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34376     */
34377     
34378     register : function(brick)
34379     {
34380         this.bricks.push(brick);
34381         brick.masonryId = this.id;
34382     },
34383     
34384     /**
34385     * clear all the Masonry Brick
34386     */
34387     clearAll : function()
34388     {
34389         this.bricks = [];
34390         //this.getChildContainer().dom.innerHTML = "";
34391         this.el.dom.innerHTML = '';
34392     },
34393     
34394     getSelected : function()
34395     {
34396         if (!this.selectedBrick) {
34397             return false;
34398         }
34399         
34400         return this.selectedBrick;
34401     }
34402 });
34403
34404 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34405     
34406     groups: {},
34407      /**
34408     * register a Masonry Layout
34409     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34410     */
34411     
34412     register : function(layout)
34413     {
34414         this.groups[layout.id] = layout;
34415     },
34416     /**
34417     * fetch a  Masonry Layout based on the masonry layout ID
34418     * @param {string} the masonry layout to add
34419     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34420     */
34421     
34422     get: function(layout_id) {
34423         if (typeof(this.groups[layout_id]) == 'undefined') {
34424             return false;
34425         }
34426         return this.groups[layout_id] ;
34427     }
34428     
34429     
34430     
34431 });
34432
34433  
34434
34435  /**
34436  *
34437  * This is based on 
34438  * http://masonry.desandro.com
34439  *
34440  * The idea is to render all the bricks based on vertical width...
34441  *
34442  * The original code extends 'outlayer' - we might need to use that....
34443  * 
34444  */
34445
34446
34447 /**
34448  * @class Roo.bootstrap.LayoutMasonryAuto
34449  * @extends Roo.bootstrap.Component
34450  * Bootstrap Layout Masonry class
34451  * 
34452  * @constructor
34453  * Create a new Element
34454  * @param {Object} config The config object
34455  */
34456
34457 Roo.bootstrap.LayoutMasonryAuto = function(config){
34458     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34459 };
34460
34461 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34462     
34463       /**
34464      * @cfg {Boolean} isFitWidth  - resize the width..
34465      */   
34466     isFitWidth : false,  // options..
34467     /**
34468      * @cfg {Boolean} isOriginLeft = left align?
34469      */   
34470     isOriginLeft : true,
34471     /**
34472      * @cfg {Boolean} isOriginTop = top align?
34473      */   
34474     isOriginTop : false,
34475     /**
34476      * @cfg {Boolean} isLayoutInstant = no animation?
34477      */   
34478     isLayoutInstant : false, // needed?
34479     /**
34480      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34481      */   
34482     isResizingContainer : true,
34483     /**
34484      * @cfg {Number} columnWidth  width of the columns 
34485      */   
34486     
34487     columnWidth : 0,
34488     
34489     /**
34490      * @cfg {Number} maxCols maximum number of columns
34491      */   
34492     
34493     maxCols: 0,
34494     /**
34495      * @cfg {Number} padHeight padding below box..
34496      */   
34497     
34498     padHeight : 10, 
34499     
34500     /**
34501      * @cfg {Boolean} isAutoInitial defalut true
34502      */   
34503     
34504     isAutoInitial : true, 
34505     
34506     // private?
34507     gutter : 0,
34508     
34509     containerWidth: 0,
34510     initialColumnWidth : 0,
34511     currentSize : null,
34512     
34513     colYs : null, // array.
34514     maxY : 0,
34515     padWidth: 10,
34516     
34517     
34518     tag: 'div',
34519     cls: '',
34520     bricks: null, //CompositeElement
34521     cols : 0, // array?
34522     // element : null, // wrapped now this.el
34523     _isLayoutInited : null, 
34524     
34525     
34526     getAutoCreate : function(){
34527         
34528         var cfg = {
34529             tag: this.tag,
34530             cls: 'blog-masonary-wrapper ' + this.cls,
34531             cn : {
34532                 cls : 'mas-boxes masonary'
34533             }
34534         };
34535         
34536         return cfg;
34537     },
34538     
34539     getChildContainer: function( )
34540     {
34541         if (this.boxesEl) {
34542             return this.boxesEl;
34543         }
34544         
34545         this.boxesEl = this.el.select('.mas-boxes').first();
34546         
34547         return this.boxesEl;
34548     },
34549     
34550     
34551     initEvents : function()
34552     {
34553         var _this = this;
34554         
34555         if(this.isAutoInitial){
34556             Roo.log('hook children rendered');
34557             this.on('childrenrendered', function() {
34558                 Roo.log('children rendered');
34559                 _this.initial();
34560             } ,this);
34561         }
34562         
34563     },
34564     
34565     initial : function()
34566     {
34567         this.reloadItems();
34568
34569         this.currentSize = this.el.getBox(true);
34570
34571         /// was window resize... - let's see if this works..
34572         Roo.EventManager.onWindowResize(this.resize, this); 
34573
34574         if(!this.isAutoInitial){
34575             this.layout();
34576             return;
34577         }
34578         
34579         this.layout.defer(500,this);
34580     },
34581     
34582     reloadItems: function()
34583     {
34584         this.bricks = this.el.select('.masonry-brick', true);
34585         
34586         this.bricks.each(function(b) {
34587             //Roo.log(b.getSize());
34588             if (!b.attr('originalwidth')) {
34589                 b.attr('originalwidth',  b.getSize().width);
34590             }
34591             
34592         });
34593         
34594         Roo.log(this.bricks.elements.length);
34595     },
34596     
34597     resize : function()
34598     {
34599         Roo.log('resize');
34600         var cs = this.el.getBox(true);
34601         
34602         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34603             Roo.log("no change in with or X");
34604             return;
34605         }
34606         this.currentSize = cs;
34607         this.layout();
34608     },
34609     
34610     layout : function()
34611     {
34612          Roo.log('layout');
34613         this._resetLayout();
34614         //this._manageStamps();
34615       
34616         // don't animate first layout
34617         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34618         this.layoutItems( isInstant );
34619       
34620         // flag for initalized
34621         this._isLayoutInited = true;
34622     },
34623     
34624     layoutItems : function( isInstant )
34625     {
34626         //var items = this._getItemsForLayout( this.items );
34627         // original code supports filtering layout items.. we just ignore it..
34628         
34629         this._layoutItems( this.bricks , isInstant );
34630       
34631         this._postLayout();
34632     },
34633     _layoutItems : function ( items , isInstant)
34634     {
34635        //this.fireEvent( 'layout', this, items );
34636     
34637
34638         if ( !items || !items.elements.length ) {
34639           // no items, emit event with empty array
34640             return;
34641         }
34642
34643         var queue = [];
34644         items.each(function(item) {
34645             Roo.log("layout item");
34646             Roo.log(item);
34647             // get x/y object from method
34648             var position = this._getItemLayoutPosition( item );
34649             // enqueue
34650             position.item = item;
34651             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34652             queue.push( position );
34653         }, this);
34654       
34655         this._processLayoutQueue( queue );
34656     },
34657     /** Sets position of item in DOM
34658     * @param {Element} item
34659     * @param {Number} x - horizontal position
34660     * @param {Number} y - vertical position
34661     * @param {Boolean} isInstant - disables transitions
34662     */
34663     _processLayoutQueue : function( queue )
34664     {
34665         for ( var i=0, len = queue.length; i < len; i++ ) {
34666             var obj = queue[i];
34667             obj.item.position('absolute');
34668             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34669         }
34670     },
34671       
34672     
34673     /**
34674     * Any logic you want to do after each layout,
34675     * i.e. size the container
34676     */
34677     _postLayout : function()
34678     {
34679         this.resizeContainer();
34680     },
34681     
34682     resizeContainer : function()
34683     {
34684         if ( !this.isResizingContainer ) {
34685             return;
34686         }
34687         var size = this._getContainerSize();
34688         if ( size ) {
34689             this.el.setSize(size.width,size.height);
34690             this.boxesEl.setSize(size.width,size.height);
34691         }
34692     },
34693     
34694     
34695     
34696     _resetLayout : function()
34697     {
34698         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34699         this.colWidth = this.el.getWidth();
34700         //this.gutter = this.el.getWidth(); 
34701         
34702         this.measureColumns();
34703
34704         // reset column Y
34705         var i = this.cols;
34706         this.colYs = [];
34707         while (i--) {
34708             this.colYs.push( 0 );
34709         }
34710     
34711         this.maxY = 0;
34712     },
34713
34714     measureColumns : function()
34715     {
34716         this.getContainerWidth();
34717       // if columnWidth is 0, default to outerWidth of first item
34718         if ( !this.columnWidth ) {
34719             var firstItem = this.bricks.first();
34720             Roo.log(firstItem);
34721             this.columnWidth  = this.containerWidth;
34722             if (firstItem && firstItem.attr('originalwidth') ) {
34723                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34724             }
34725             // columnWidth fall back to item of first element
34726             Roo.log("set column width?");
34727                         this.initialColumnWidth = this.columnWidth  ;
34728
34729             // if first elem has no width, default to size of container
34730             
34731         }
34732         
34733         
34734         if (this.initialColumnWidth) {
34735             this.columnWidth = this.initialColumnWidth;
34736         }
34737         
34738         
34739             
34740         // column width is fixed at the top - however if container width get's smaller we should
34741         // reduce it...
34742         
34743         // this bit calcs how man columns..
34744             
34745         var columnWidth = this.columnWidth += this.gutter;
34746       
34747         // calculate columns
34748         var containerWidth = this.containerWidth + this.gutter;
34749         
34750         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34751         // fix rounding errors, typically with gutters
34752         var excess = columnWidth - containerWidth % columnWidth;
34753         
34754         
34755         // if overshoot is less than a pixel, round up, otherwise floor it
34756         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34757         cols = Math[ mathMethod ]( cols );
34758         this.cols = Math.max( cols, 1 );
34759         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34760         
34761          // padding positioning..
34762         var totalColWidth = this.cols * this.columnWidth;
34763         var padavail = this.containerWidth - totalColWidth;
34764         // so for 2 columns - we need 3 'pads'
34765         
34766         var padNeeded = (1+this.cols) * this.padWidth;
34767         
34768         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34769         
34770         this.columnWidth += padExtra
34771         //this.padWidth = Math.floor(padavail /  ( this.cols));
34772         
34773         // adjust colum width so that padding is fixed??
34774         
34775         // we have 3 columns ... total = width * 3
34776         // we have X left over... that should be used by 
34777         
34778         //if (this.expandC) {
34779             
34780         //}
34781         
34782         
34783         
34784     },
34785     
34786     getContainerWidth : function()
34787     {
34788        /* // container is parent if fit width
34789         var container = this.isFitWidth ? this.element.parentNode : this.element;
34790         // check that this.size and size are there
34791         // IE8 triggers resize on body size change, so they might not be
34792         
34793         var size = getSize( container );  //FIXME
34794         this.containerWidth = size && size.innerWidth; //FIXME
34795         */
34796          
34797         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34798         
34799     },
34800     
34801     _getItemLayoutPosition : function( item )  // what is item?
34802     {
34803         // we resize the item to our columnWidth..
34804       
34805         item.setWidth(this.columnWidth);
34806         item.autoBoxAdjust  = false;
34807         
34808         var sz = item.getSize();
34809  
34810         // how many columns does this brick span
34811         var remainder = this.containerWidth % this.columnWidth;
34812         
34813         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34814         // round if off by 1 pixel, otherwise use ceil
34815         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34816         colSpan = Math.min( colSpan, this.cols );
34817         
34818         // normally this should be '1' as we dont' currently allow multi width columns..
34819         
34820         var colGroup = this._getColGroup( colSpan );
34821         // get the minimum Y value from the columns
34822         var minimumY = Math.min.apply( Math, colGroup );
34823         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34824         
34825         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34826          
34827         // position the brick
34828         var position = {
34829             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34830             y: this.currentSize.y + minimumY + this.padHeight
34831         };
34832         
34833         Roo.log(position);
34834         // apply setHeight to necessary columns
34835         var setHeight = minimumY + sz.height + this.padHeight;
34836         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34837         
34838         var setSpan = this.cols + 1 - colGroup.length;
34839         for ( var i = 0; i < setSpan; i++ ) {
34840           this.colYs[ shortColIndex + i ] = setHeight ;
34841         }
34842       
34843         return position;
34844     },
34845     
34846     /**
34847      * @param {Number} colSpan - number of columns the element spans
34848      * @returns {Array} colGroup
34849      */
34850     _getColGroup : function( colSpan )
34851     {
34852         if ( colSpan < 2 ) {
34853           // if brick spans only one column, use all the column Ys
34854           return this.colYs;
34855         }
34856       
34857         var colGroup = [];
34858         // how many different places could this brick fit horizontally
34859         var groupCount = this.cols + 1 - colSpan;
34860         // for each group potential horizontal position
34861         for ( var i = 0; i < groupCount; i++ ) {
34862           // make an array of colY values for that one group
34863           var groupColYs = this.colYs.slice( i, i + colSpan );
34864           // and get the max value of the array
34865           colGroup[i] = Math.max.apply( Math, groupColYs );
34866         }
34867         return colGroup;
34868     },
34869     /*
34870     _manageStamp : function( stamp )
34871     {
34872         var stampSize =  stamp.getSize();
34873         var offset = stamp.getBox();
34874         // get the columns that this stamp affects
34875         var firstX = this.isOriginLeft ? offset.x : offset.right;
34876         var lastX = firstX + stampSize.width;
34877         var firstCol = Math.floor( firstX / this.columnWidth );
34878         firstCol = Math.max( 0, firstCol );
34879         
34880         var lastCol = Math.floor( lastX / this.columnWidth );
34881         // lastCol should not go over if multiple of columnWidth #425
34882         lastCol -= lastX % this.columnWidth ? 0 : 1;
34883         lastCol = Math.min( this.cols - 1, lastCol );
34884         
34885         // set colYs to bottom of the stamp
34886         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34887             stampSize.height;
34888             
34889         for ( var i = firstCol; i <= lastCol; i++ ) {
34890           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34891         }
34892     },
34893     */
34894     
34895     _getContainerSize : function()
34896     {
34897         this.maxY = Math.max.apply( Math, this.colYs );
34898         var size = {
34899             height: this.maxY
34900         };
34901       
34902         if ( this.isFitWidth ) {
34903             size.width = this._getContainerFitWidth();
34904         }
34905       
34906         return size;
34907     },
34908     
34909     _getContainerFitWidth : function()
34910     {
34911         var unusedCols = 0;
34912         // count unused columns
34913         var i = this.cols;
34914         while ( --i ) {
34915           if ( this.colYs[i] !== 0 ) {
34916             break;
34917           }
34918           unusedCols++;
34919         }
34920         // fit container to columns that have been used
34921         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34922     },
34923     
34924     needsResizeLayout : function()
34925     {
34926         var previousWidth = this.containerWidth;
34927         this.getContainerWidth();
34928         return previousWidth !== this.containerWidth;
34929     }
34930  
34931 });
34932
34933  
34934
34935  /*
34936  * - LGPL
34937  *
34938  * element
34939  * 
34940  */
34941
34942 /**
34943  * @class Roo.bootstrap.MasonryBrick
34944  * @extends Roo.bootstrap.Component
34945  * Bootstrap MasonryBrick class
34946  * 
34947  * @constructor
34948  * Create a new MasonryBrick
34949  * @param {Object} config The config object
34950  */
34951
34952 Roo.bootstrap.MasonryBrick = function(config){
34953     
34954     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34955     
34956     Roo.bootstrap.MasonryBrick.register(this);
34957     
34958     this.addEvents({
34959         // raw events
34960         /**
34961          * @event click
34962          * When a MasonryBrick is clcik
34963          * @param {Roo.bootstrap.MasonryBrick} this
34964          * @param {Roo.EventObject} e
34965          */
34966         "click" : true
34967     });
34968 };
34969
34970 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34971     
34972     /**
34973      * @cfg {String} title
34974      */   
34975     title : '',
34976     /**
34977      * @cfg {String} html
34978      */   
34979     html : '',
34980     /**
34981      * @cfg {String} bgimage
34982      */   
34983     bgimage : '',
34984     /**
34985      * @cfg {String} videourl
34986      */   
34987     videourl : '',
34988     /**
34989      * @cfg {String} cls
34990      */   
34991     cls : '',
34992     /**
34993      * @cfg {String} href
34994      */   
34995     href : '',
34996     /**
34997      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34998      */   
34999     size : 'xs',
35000     
35001     /**
35002      * @cfg {String} placetitle (center|bottom)
35003      */   
35004     placetitle : '',
35005     
35006     /**
35007      * @cfg {Boolean} isFitContainer defalut true
35008      */   
35009     isFitContainer : true, 
35010     
35011     /**
35012      * @cfg {Boolean} preventDefault defalut false
35013      */   
35014     preventDefault : false, 
35015     
35016     /**
35017      * @cfg {Boolean} inverse defalut false
35018      */   
35019     maskInverse : false, 
35020     
35021     getAutoCreate : function()
35022     {
35023         if(!this.isFitContainer){
35024             return this.getSplitAutoCreate();
35025         }
35026         
35027         var cls = 'masonry-brick masonry-brick-full';
35028         
35029         if(this.href.length){
35030             cls += ' masonry-brick-link';
35031         }
35032         
35033         if(this.bgimage.length){
35034             cls += ' masonry-brick-image';
35035         }
35036         
35037         if(this.maskInverse){
35038             cls += ' mask-inverse';
35039         }
35040         
35041         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35042             cls += ' enable-mask';
35043         }
35044         
35045         if(this.size){
35046             cls += ' masonry-' + this.size + '-brick';
35047         }
35048         
35049         if(this.placetitle.length){
35050             
35051             switch (this.placetitle) {
35052                 case 'center' :
35053                     cls += ' masonry-center-title';
35054                     break;
35055                 case 'bottom' :
35056                     cls += ' masonry-bottom-title';
35057                     break;
35058                 default:
35059                     break;
35060             }
35061             
35062         } else {
35063             if(!this.html.length && !this.bgimage.length){
35064                 cls += ' masonry-center-title';
35065             }
35066
35067             if(!this.html.length && this.bgimage.length){
35068                 cls += ' masonry-bottom-title';
35069             }
35070         }
35071         
35072         if(this.cls){
35073             cls += ' ' + this.cls;
35074         }
35075         
35076         var cfg = {
35077             tag: (this.href.length) ? 'a' : 'div',
35078             cls: cls,
35079             cn: [
35080                 {
35081                     tag: 'div',
35082                     cls: 'masonry-brick-mask'
35083                 },
35084                 {
35085                     tag: 'div',
35086                     cls: 'masonry-brick-paragraph',
35087                     cn: []
35088                 }
35089             ]
35090         };
35091         
35092         if(this.href.length){
35093             cfg.href = this.href;
35094         }
35095         
35096         var cn = cfg.cn[1].cn;
35097         
35098         if(this.title.length){
35099             cn.push({
35100                 tag: 'h4',
35101                 cls: 'masonry-brick-title',
35102                 html: this.title
35103             });
35104         }
35105         
35106         if(this.html.length){
35107             cn.push({
35108                 tag: 'p',
35109                 cls: 'masonry-brick-text',
35110                 html: this.html
35111             });
35112         }
35113         
35114         if (!this.title.length && !this.html.length) {
35115             cfg.cn[1].cls += ' hide';
35116         }
35117         
35118         if(this.bgimage.length){
35119             cfg.cn.push({
35120                 tag: 'img',
35121                 cls: 'masonry-brick-image-view',
35122                 src: this.bgimage
35123             });
35124         }
35125         
35126         if(this.videourl.length){
35127             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35128             // youtube support only?
35129             cfg.cn.push({
35130                 tag: 'iframe',
35131                 cls: 'masonry-brick-image-view',
35132                 src: vurl,
35133                 frameborder : 0,
35134                 allowfullscreen : true
35135             });
35136         }
35137         
35138         return cfg;
35139         
35140     },
35141     
35142     getSplitAutoCreate : function()
35143     {
35144         var cls = 'masonry-brick masonry-brick-split';
35145         
35146         if(this.href.length){
35147             cls += ' masonry-brick-link';
35148         }
35149         
35150         if(this.bgimage.length){
35151             cls += ' masonry-brick-image';
35152         }
35153         
35154         if(this.size){
35155             cls += ' masonry-' + this.size + '-brick';
35156         }
35157         
35158         switch (this.placetitle) {
35159             case 'center' :
35160                 cls += ' masonry-center-title';
35161                 break;
35162             case 'bottom' :
35163                 cls += ' masonry-bottom-title';
35164                 break;
35165             default:
35166                 if(!this.bgimage.length){
35167                     cls += ' masonry-center-title';
35168                 }
35169
35170                 if(this.bgimage.length){
35171                     cls += ' masonry-bottom-title';
35172                 }
35173                 break;
35174         }
35175         
35176         if(this.cls){
35177             cls += ' ' + this.cls;
35178         }
35179         
35180         var cfg = {
35181             tag: (this.href.length) ? 'a' : 'div',
35182             cls: cls,
35183             cn: [
35184                 {
35185                     tag: 'div',
35186                     cls: 'masonry-brick-split-head',
35187                     cn: [
35188                         {
35189                             tag: 'div',
35190                             cls: 'masonry-brick-paragraph',
35191                             cn: []
35192                         }
35193                     ]
35194                 },
35195                 {
35196                     tag: 'div',
35197                     cls: 'masonry-brick-split-body',
35198                     cn: []
35199                 }
35200             ]
35201         };
35202         
35203         if(this.href.length){
35204             cfg.href = this.href;
35205         }
35206         
35207         if(this.title.length){
35208             cfg.cn[0].cn[0].cn.push({
35209                 tag: 'h4',
35210                 cls: 'masonry-brick-title',
35211                 html: this.title
35212             });
35213         }
35214         
35215         if(this.html.length){
35216             cfg.cn[1].cn.push({
35217                 tag: 'p',
35218                 cls: 'masonry-brick-text',
35219                 html: this.html
35220             });
35221         }
35222
35223         if(this.bgimage.length){
35224             cfg.cn[0].cn.push({
35225                 tag: 'img',
35226                 cls: 'masonry-brick-image-view',
35227                 src: this.bgimage
35228             });
35229         }
35230         
35231         if(this.videourl.length){
35232             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35233             // youtube support only?
35234             cfg.cn[0].cn.cn.push({
35235                 tag: 'iframe',
35236                 cls: 'masonry-brick-image-view',
35237                 src: vurl,
35238                 frameborder : 0,
35239                 allowfullscreen : true
35240             });
35241         }
35242         
35243         return cfg;
35244     },
35245     
35246     initEvents: function() 
35247     {
35248         switch (this.size) {
35249             case 'xs' :
35250                 this.x = 1;
35251                 this.y = 1;
35252                 break;
35253             case 'sm' :
35254                 this.x = 2;
35255                 this.y = 2;
35256                 break;
35257             case 'md' :
35258             case 'md-left' :
35259             case 'md-right' :
35260                 this.x = 3;
35261                 this.y = 3;
35262                 break;
35263             case 'tall' :
35264                 this.x = 2;
35265                 this.y = 3;
35266                 break;
35267             case 'wide' :
35268                 this.x = 3;
35269                 this.y = 2;
35270                 break;
35271             case 'wide-thin' :
35272                 this.x = 3;
35273                 this.y = 1;
35274                 break;
35275                         
35276             default :
35277                 break;
35278         }
35279         
35280         if(Roo.isTouch){
35281             this.el.on('touchstart', this.onTouchStart, this);
35282             this.el.on('touchmove', this.onTouchMove, this);
35283             this.el.on('touchend', this.onTouchEnd, this);
35284             this.el.on('contextmenu', this.onContextMenu, this);
35285         } else {
35286             this.el.on('mouseenter'  ,this.enter, this);
35287             this.el.on('mouseleave', this.leave, this);
35288             this.el.on('click', this.onClick, this);
35289         }
35290         
35291         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35292             this.parent().bricks.push(this);   
35293         }
35294         
35295     },
35296     
35297     onClick: function(e, el)
35298     {
35299         var time = this.endTimer - this.startTimer;
35300         // Roo.log(e.preventDefault());
35301         if(Roo.isTouch){
35302             if(time > 1000){
35303                 e.preventDefault();
35304                 return;
35305             }
35306         }
35307         
35308         if(!this.preventDefault){
35309             return;
35310         }
35311         
35312         e.preventDefault();
35313         
35314         if (this.activeClass != '') {
35315             this.selectBrick();
35316         }
35317         
35318         this.fireEvent('click', this, e);
35319     },
35320     
35321     enter: function(e, el)
35322     {
35323         e.preventDefault();
35324         
35325         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35326             return;
35327         }
35328         
35329         if(this.bgimage.length && this.html.length){
35330             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35331         }
35332     },
35333     
35334     leave: function(e, el)
35335     {
35336         e.preventDefault();
35337         
35338         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35339             return;
35340         }
35341         
35342         if(this.bgimage.length && this.html.length){
35343             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35344         }
35345     },
35346     
35347     onTouchStart: function(e, el)
35348     {
35349 //        e.preventDefault();
35350         
35351         this.touchmoved = false;
35352         
35353         if(!this.isFitContainer){
35354             return;
35355         }
35356         
35357         if(!this.bgimage.length || !this.html.length){
35358             return;
35359         }
35360         
35361         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35362         
35363         this.timer = new Date().getTime();
35364         
35365     },
35366     
35367     onTouchMove: function(e, el)
35368     {
35369         this.touchmoved = true;
35370     },
35371     
35372     onContextMenu : function(e,el)
35373     {
35374         e.preventDefault();
35375         e.stopPropagation();
35376         return false;
35377     },
35378     
35379     onTouchEnd: function(e, el)
35380     {
35381 //        e.preventDefault();
35382         
35383         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35384         
35385             this.leave(e,el);
35386             
35387             return;
35388         }
35389         
35390         if(!this.bgimage.length || !this.html.length){
35391             
35392             if(this.href.length){
35393                 window.location.href = this.href;
35394             }
35395             
35396             return;
35397         }
35398         
35399         if(!this.isFitContainer){
35400             return;
35401         }
35402         
35403         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35404         
35405         window.location.href = this.href;
35406     },
35407     
35408     //selection on single brick only
35409     selectBrick : function() {
35410         
35411         if (!this.parentId) {
35412             return;
35413         }
35414         
35415         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35416         var index = m.selectedBrick.indexOf(this.id);
35417         
35418         if ( index > -1) {
35419             m.selectedBrick.splice(index,1);
35420             this.el.removeClass(this.activeClass);
35421             return;
35422         }
35423         
35424         for(var i = 0; i < m.selectedBrick.length; i++) {
35425             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35426             b.el.removeClass(b.activeClass);
35427         }
35428         
35429         m.selectedBrick = [];
35430         
35431         m.selectedBrick.push(this.id);
35432         this.el.addClass(this.activeClass);
35433         return;
35434     },
35435     
35436     isSelected : function(){
35437         return this.el.hasClass(this.activeClass);
35438         
35439     }
35440 });
35441
35442 Roo.apply(Roo.bootstrap.MasonryBrick, {
35443     
35444     //groups: {},
35445     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35446      /**
35447     * register a Masonry Brick
35448     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35449     */
35450     
35451     register : function(brick)
35452     {
35453         //this.groups[brick.id] = brick;
35454         this.groups.add(brick.id, brick);
35455     },
35456     /**
35457     * fetch a  masonry brick based on the masonry brick ID
35458     * @param {string} the masonry brick to add
35459     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35460     */
35461     
35462     get: function(brick_id) 
35463     {
35464         // if (typeof(this.groups[brick_id]) == 'undefined') {
35465         //     return false;
35466         // }
35467         // return this.groups[brick_id] ;
35468         
35469         if(this.groups.key(brick_id)) {
35470             return this.groups.key(brick_id);
35471         }
35472         
35473         return false;
35474     }
35475     
35476     
35477     
35478 });
35479
35480  /*
35481  * - LGPL
35482  *
35483  * element
35484  * 
35485  */
35486
35487 /**
35488  * @class Roo.bootstrap.Brick
35489  * @extends Roo.bootstrap.Component
35490  * Bootstrap Brick class
35491  * 
35492  * @constructor
35493  * Create a new Brick
35494  * @param {Object} config The config object
35495  */
35496
35497 Roo.bootstrap.Brick = function(config){
35498     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35499     
35500     this.addEvents({
35501         // raw events
35502         /**
35503          * @event click
35504          * When a Brick is click
35505          * @param {Roo.bootstrap.Brick} this
35506          * @param {Roo.EventObject} e
35507          */
35508         "click" : true
35509     });
35510 };
35511
35512 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35513     
35514     /**
35515      * @cfg {String} title
35516      */   
35517     title : '',
35518     /**
35519      * @cfg {String} html
35520      */   
35521     html : '',
35522     /**
35523      * @cfg {String} bgimage
35524      */   
35525     bgimage : '',
35526     /**
35527      * @cfg {String} cls
35528      */   
35529     cls : '',
35530     /**
35531      * @cfg {String} href
35532      */   
35533     href : '',
35534     /**
35535      * @cfg {String} video
35536      */   
35537     video : '',
35538     /**
35539      * @cfg {Boolean} square
35540      */   
35541     square : true,
35542     
35543     getAutoCreate : function()
35544     {
35545         var cls = 'roo-brick';
35546         
35547         if(this.href.length){
35548             cls += ' roo-brick-link';
35549         }
35550         
35551         if(this.bgimage.length){
35552             cls += ' roo-brick-image';
35553         }
35554         
35555         if(!this.html.length && !this.bgimage.length){
35556             cls += ' roo-brick-center-title';
35557         }
35558         
35559         if(!this.html.length && this.bgimage.length){
35560             cls += ' roo-brick-bottom-title';
35561         }
35562         
35563         if(this.cls){
35564             cls += ' ' + this.cls;
35565         }
35566         
35567         var cfg = {
35568             tag: (this.href.length) ? 'a' : 'div',
35569             cls: cls,
35570             cn: [
35571                 {
35572                     tag: 'div',
35573                     cls: 'roo-brick-paragraph',
35574                     cn: []
35575                 }
35576             ]
35577         };
35578         
35579         if(this.href.length){
35580             cfg.href = this.href;
35581         }
35582         
35583         var cn = cfg.cn[0].cn;
35584         
35585         if(this.title.length){
35586             cn.push({
35587                 tag: 'h4',
35588                 cls: 'roo-brick-title',
35589                 html: this.title
35590             });
35591         }
35592         
35593         if(this.html.length){
35594             cn.push({
35595                 tag: 'p',
35596                 cls: 'roo-brick-text',
35597                 html: this.html
35598             });
35599         } else {
35600             cn.cls += ' hide';
35601         }
35602         
35603         if(this.bgimage.length){
35604             cfg.cn.push({
35605                 tag: 'img',
35606                 cls: 'roo-brick-image-view',
35607                 src: this.bgimage
35608             });
35609         }
35610         
35611         return cfg;
35612     },
35613     
35614     initEvents: function() 
35615     {
35616         if(this.title.length || this.html.length){
35617             this.el.on('mouseenter'  ,this.enter, this);
35618             this.el.on('mouseleave', this.leave, this);
35619         }
35620         
35621         Roo.EventManager.onWindowResize(this.resize, this); 
35622         
35623         if(this.bgimage.length){
35624             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35625             this.imageEl.on('load', this.onImageLoad, this);
35626             return;
35627         }
35628         
35629         this.resize();
35630     },
35631     
35632     onImageLoad : function()
35633     {
35634         this.resize();
35635     },
35636     
35637     resize : function()
35638     {
35639         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35640         
35641         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35642         
35643         if(this.bgimage.length){
35644             var image = this.el.select('.roo-brick-image-view', true).first();
35645             
35646             image.setWidth(paragraph.getWidth());
35647             
35648             if(this.square){
35649                 image.setHeight(paragraph.getWidth());
35650             }
35651             
35652             this.el.setHeight(image.getHeight());
35653             paragraph.setHeight(image.getHeight());
35654             
35655         }
35656         
35657     },
35658     
35659     enter: function(e, el)
35660     {
35661         e.preventDefault();
35662         
35663         if(this.bgimage.length){
35664             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35665             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35666         }
35667     },
35668     
35669     leave: function(e, el)
35670     {
35671         e.preventDefault();
35672         
35673         if(this.bgimage.length){
35674             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35675             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35676         }
35677     }
35678     
35679 });
35680
35681  
35682
35683  /*
35684  * - LGPL
35685  *
35686  * Number field 
35687  */
35688
35689 /**
35690  * @class Roo.bootstrap.NumberField
35691  * @extends Roo.bootstrap.Input
35692  * Bootstrap NumberField class
35693  * 
35694  * 
35695  * 
35696  * 
35697  * @constructor
35698  * Create a new NumberField
35699  * @param {Object} config The config object
35700  */
35701
35702 Roo.bootstrap.NumberField = function(config){
35703     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35704 };
35705
35706 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35707     
35708     /**
35709      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35710      */
35711     allowDecimals : true,
35712     /**
35713      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35714      */
35715     decimalSeparator : ".",
35716     /**
35717      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35718      */
35719     decimalPrecision : 2,
35720     /**
35721      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35722      */
35723     allowNegative : true,
35724     
35725     /**
35726      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35727      */
35728     allowZero: true,
35729     /**
35730      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35731      */
35732     minValue : Number.NEGATIVE_INFINITY,
35733     /**
35734      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35735      */
35736     maxValue : Number.MAX_VALUE,
35737     /**
35738      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35739      */
35740     minText : "The minimum value for this field is {0}",
35741     /**
35742      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35743      */
35744     maxText : "The maximum value for this field is {0}",
35745     /**
35746      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35747      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35748      */
35749     nanText : "{0} is not a valid number",
35750     /**
35751      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35752      */
35753     thousandsDelimiter : false,
35754     /**
35755      * @cfg {String} valueAlign alignment of value
35756      */
35757     valueAlign : "left",
35758
35759     getAutoCreate : function()
35760     {
35761         var hiddenInput = {
35762             tag: 'input',
35763             type: 'hidden',
35764             id: Roo.id(),
35765             cls: 'hidden-number-input'
35766         };
35767         
35768         if (this.name) {
35769             hiddenInput.name = this.name;
35770         }
35771         
35772         this.name = '';
35773         
35774         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35775         
35776         this.name = hiddenInput.name;
35777         
35778         if(cfg.cn.length > 0) {
35779             cfg.cn.push(hiddenInput);
35780         }
35781         
35782         return cfg;
35783     },
35784
35785     // private
35786     initEvents : function()
35787     {   
35788         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35789         
35790         var allowed = "0123456789";
35791         
35792         if(this.allowDecimals){
35793             allowed += this.decimalSeparator;
35794         }
35795         
35796         if(this.allowNegative){
35797             allowed += "-";
35798         }
35799         
35800         if(this.thousandsDelimiter) {
35801             allowed += ",";
35802         }
35803         
35804         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35805         
35806         var keyPress = function(e){
35807             
35808             var k = e.getKey();
35809             
35810             var c = e.getCharCode();
35811             
35812             if(
35813                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35814                     allowed.indexOf(String.fromCharCode(c)) === -1
35815             ){
35816                 e.stopEvent();
35817                 return;
35818             }
35819             
35820             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35821                 return;
35822             }
35823             
35824             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35825                 e.stopEvent();
35826             }
35827         };
35828         
35829         this.el.on("keypress", keyPress, this);
35830     },
35831     
35832     validateValue : function(value)
35833     {
35834         
35835         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35836             return false;
35837         }
35838         
35839         var num = this.parseValue(value);
35840         
35841         if(isNaN(num)){
35842             this.markInvalid(String.format(this.nanText, value));
35843             return false;
35844         }
35845         
35846         if(num < this.minValue){
35847             this.markInvalid(String.format(this.minText, this.minValue));
35848             return false;
35849         }
35850         
35851         if(num > this.maxValue){
35852             this.markInvalid(String.format(this.maxText, this.maxValue));
35853             return false;
35854         }
35855         
35856         return true;
35857     },
35858
35859     getValue : function()
35860     {
35861         var v = this.hiddenEl().getValue();
35862         
35863         return this.fixPrecision(this.parseValue(v));
35864     },
35865
35866     parseValue : function(value)
35867     {
35868         if(this.thousandsDelimiter) {
35869             value += "";
35870             r = new RegExp(",", "g");
35871             value = value.replace(r, "");
35872         }
35873         
35874         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35875         return isNaN(value) ? '' : value;
35876     },
35877
35878     fixPrecision : function(value)
35879     {
35880         if(this.thousandsDelimiter) {
35881             value += "";
35882             r = new RegExp(",", "g");
35883             value = value.replace(r, "");
35884         }
35885         
35886         var nan = isNaN(value);
35887         
35888         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35889             return nan ? '' : value;
35890         }
35891         return parseFloat(value).toFixed(this.decimalPrecision);
35892     },
35893
35894     setValue : function(v)
35895     {
35896         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35897         
35898         this.value = v;
35899         
35900         if(this.rendered){
35901             
35902             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35903             
35904             this.inputEl().dom.value = (v == '') ? '' :
35905                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35906             
35907             if(!this.allowZero && v === '0') {
35908                 this.hiddenEl().dom.value = '';
35909                 this.inputEl().dom.value = '';
35910             }
35911             
35912             this.validate();
35913         }
35914     },
35915
35916     decimalPrecisionFcn : function(v)
35917     {
35918         return Math.floor(v);
35919     },
35920
35921     beforeBlur : function()
35922     {
35923         var v = this.parseValue(this.getRawValue());
35924         
35925         if(v || v === 0 || v === ''){
35926             this.setValue(v);
35927         }
35928     },
35929     
35930     hiddenEl : function()
35931     {
35932         return this.el.select('input.hidden-number-input',true).first();
35933     }
35934     
35935 });
35936
35937  
35938
35939 /*
35940 * Licence: LGPL
35941 */
35942
35943 /**
35944  * @class Roo.bootstrap.DocumentSlider
35945  * @extends Roo.bootstrap.Component
35946  * Bootstrap DocumentSlider class
35947  * 
35948  * @constructor
35949  * Create a new DocumentViewer
35950  * @param {Object} config The config object
35951  */
35952
35953 Roo.bootstrap.DocumentSlider = function(config){
35954     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35955     
35956     this.files = [];
35957     
35958     this.addEvents({
35959         /**
35960          * @event initial
35961          * Fire after initEvent
35962          * @param {Roo.bootstrap.DocumentSlider} this
35963          */
35964         "initial" : true,
35965         /**
35966          * @event update
35967          * Fire after update
35968          * @param {Roo.bootstrap.DocumentSlider} this
35969          */
35970         "update" : true,
35971         /**
35972          * @event click
35973          * Fire after click
35974          * @param {Roo.bootstrap.DocumentSlider} this
35975          */
35976         "click" : true
35977     });
35978 };
35979
35980 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35981     
35982     files : false,
35983     
35984     indicator : 0,
35985     
35986     getAutoCreate : function()
35987     {
35988         var cfg = {
35989             tag : 'div',
35990             cls : 'roo-document-slider',
35991             cn : [
35992                 {
35993                     tag : 'div',
35994                     cls : 'roo-document-slider-header',
35995                     cn : [
35996                         {
35997                             tag : 'div',
35998                             cls : 'roo-document-slider-header-title'
35999                         }
36000                     ]
36001                 },
36002                 {
36003                     tag : 'div',
36004                     cls : 'roo-document-slider-body',
36005                     cn : [
36006                         {
36007                             tag : 'div',
36008                             cls : 'roo-document-slider-prev',
36009                             cn : [
36010                                 {
36011                                     tag : 'i',
36012                                     cls : 'fa fa-chevron-left'
36013                                 }
36014                             ]
36015                         },
36016                         {
36017                             tag : 'div',
36018                             cls : 'roo-document-slider-thumb',
36019                             cn : [
36020                                 {
36021                                     tag : 'img',
36022                                     cls : 'roo-document-slider-image'
36023                                 }
36024                             ]
36025                         },
36026                         {
36027                             tag : 'div',
36028                             cls : 'roo-document-slider-next',
36029                             cn : [
36030                                 {
36031                                     tag : 'i',
36032                                     cls : 'fa fa-chevron-right'
36033                                 }
36034                             ]
36035                         }
36036                     ]
36037                 }
36038             ]
36039         };
36040         
36041         return cfg;
36042     },
36043     
36044     initEvents : function()
36045     {
36046         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36047         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36048         
36049         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36050         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36051         
36052         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36053         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36054         
36055         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36056         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36057         
36058         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36059         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36060         
36061         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36062         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36063         
36064         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36065         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36066         
36067         this.thumbEl.on('click', this.onClick, this);
36068         
36069         this.prevIndicator.on('click', this.prev, this);
36070         
36071         this.nextIndicator.on('click', this.next, this);
36072         
36073     },
36074     
36075     initial : function()
36076     {
36077         if(this.files.length){
36078             this.indicator = 1;
36079             this.update()
36080         }
36081         
36082         this.fireEvent('initial', this);
36083     },
36084     
36085     update : function()
36086     {
36087         this.imageEl.attr('src', this.files[this.indicator - 1]);
36088         
36089         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36090         
36091         this.prevIndicator.show();
36092         
36093         if(this.indicator == 1){
36094             this.prevIndicator.hide();
36095         }
36096         
36097         this.nextIndicator.show();
36098         
36099         if(this.indicator == this.files.length){
36100             this.nextIndicator.hide();
36101         }
36102         
36103         this.thumbEl.scrollTo('top');
36104         
36105         this.fireEvent('update', this);
36106     },
36107     
36108     onClick : function(e)
36109     {
36110         e.preventDefault();
36111         
36112         this.fireEvent('click', this);
36113     },
36114     
36115     prev : function(e)
36116     {
36117         e.preventDefault();
36118         
36119         this.indicator = Math.max(1, this.indicator - 1);
36120         
36121         this.update();
36122     },
36123     
36124     next : function(e)
36125     {
36126         e.preventDefault();
36127         
36128         this.indicator = Math.min(this.files.length, this.indicator + 1);
36129         
36130         this.update();
36131     }
36132 });
36133 /*
36134  * - LGPL
36135  *
36136  * RadioSet
36137  *
36138  *
36139  */
36140
36141 /**
36142  * @class Roo.bootstrap.RadioSet
36143  * @extends Roo.bootstrap.Input
36144  * Bootstrap RadioSet class
36145  * @cfg {String} indicatorpos (left|right) default left
36146  * @cfg {Boolean} inline (true|false) inline the element (default true)
36147  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36148  * @constructor
36149  * Create a new RadioSet
36150  * @param {Object} config The config object
36151  */
36152
36153 Roo.bootstrap.RadioSet = function(config){
36154     
36155     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36156     
36157     this.radioes = [];
36158     
36159     Roo.bootstrap.RadioSet.register(this);
36160     
36161     this.addEvents({
36162         /**
36163         * @event check
36164         * Fires when the element is checked or unchecked.
36165         * @param {Roo.bootstrap.RadioSet} this This radio
36166         * @param {Roo.bootstrap.Radio} item The checked item
36167         */
36168        check : true,
36169        /**
36170         * @event click
36171         * Fires when the element is click.
36172         * @param {Roo.bootstrap.RadioSet} this This radio set
36173         * @param {Roo.bootstrap.Radio} item The checked item
36174         * @param {Roo.EventObject} e The event object
36175         */
36176        click : true
36177     });
36178     
36179 };
36180
36181 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36182
36183     radioes : false,
36184     
36185     inline : true,
36186     
36187     weight : '',
36188     
36189     indicatorpos : 'left',
36190     
36191     getAutoCreate : function()
36192     {
36193         var label = {
36194             tag : 'label',
36195             cls : 'roo-radio-set-label',
36196             cn : [
36197                 {
36198                     tag : 'span',
36199                     html : this.fieldLabel
36200                 }
36201             ]
36202         };
36203         if (Roo.bootstrap.version == 3) {
36204             
36205             
36206             if(this.indicatorpos == 'left'){
36207                 label.cn.unshift({
36208                     tag : 'i',
36209                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36210                     tooltip : 'This field is required'
36211                 });
36212             } else {
36213                 label.cn.push({
36214                     tag : 'i',
36215                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36216                     tooltip : 'This field is required'
36217                 });
36218             }
36219         }
36220         var items = {
36221             tag : 'div',
36222             cls : 'roo-radio-set-items'
36223         };
36224         
36225         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36226         
36227         if (align === 'left' && this.fieldLabel.length) {
36228             
36229             items = {
36230                 cls : "roo-radio-set-right", 
36231                 cn: [
36232                     items
36233                 ]
36234             };
36235             
36236             if(this.labelWidth > 12){
36237                 label.style = "width: " + this.labelWidth + 'px';
36238             }
36239             
36240             if(this.labelWidth < 13 && this.labelmd == 0){
36241                 this.labelmd = this.labelWidth;
36242             }
36243             
36244             if(this.labellg > 0){
36245                 label.cls += ' col-lg-' + this.labellg;
36246                 items.cls += ' col-lg-' + (12 - this.labellg);
36247             }
36248             
36249             if(this.labelmd > 0){
36250                 label.cls += ' col-md-' + this.labelmd;
36251                 items.cls += ' col-md-' + (12 - this.labelmd);
36252             }
36253             
36254             if(this.labelsm > 0){
36255                 label.cls += ' col-sm-' + this.labelsm;
36256                 items.cls += ' col-sm-' + (12 - this.labelsm);
36257             }
36258             
36259             if(this.labelxs > 0){
36260                 label.cls += ' col-xs-' + this.labelxs;
36261                 items.cls += ' col-xs-' + (12 - this.labelxs);
36262             }
36263         }
36264         
36265         var cfg = {
36266             tag : 'div',
36267             cls : 'roo-radio-set',
36268             cn : [
36269                 {
36270                     tag : 'input',
36271                     cls : 'roo-radio-set-input',
36272                     type : 'hidden',
36273                     name : this.name,
36274                     value : this.value ? this.value :  ''
36275                 },
36276                 label,
36277                 items
36278             ]
36279         };
36280         
36281         if(this.weight.length){
36282             cfg.cls += ' roo-radio-' + this.weight;
36283         }
36284         
36285         if(this.inline) {
36286             cfg.cls += ' roo-radio-set-inline';
36287         }
36288         
36289         var settings=this;
36290         ['xs','sm','md','lg'].map(function(size){
36291             if (settings[size]) {
36292                 cfg.cls += ' col-' + size + '-' + settings[size];
36293             }
36294         });
36295         
36296         return cfg;
36297         
36298     },
36299
36300     initEvents : function()
36301     {
36302         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36303         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36304         
36305         if(!this.fieldLabel.length){
36306             this.labelEl.hide();
36307         }
36308         
36309         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36310         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36311         
36312         this.indicator = this.indicatorEl();
36313         
36314         if(this.indicator){
36315             this.indicator.addClass('invisible');
36316         }
36317         
36318         this.originalValue = this.getValue();
36319         
36320     },
36321     
36322     inputEl: function ()
36323     {
36324         return this.el.select('.roo-radio-set-input', true).first();
36325     },
36326     
36327     getChildContainer : function()
36328     {
36329         return this.itemsEl;
36330     },
36331     
36332     register : function(item)
36333     {
36334         this.radioes.push(item);
36335         
36336     },
36337     
36338     validate : function()
36339     {   
36340         if(this.getVisibilityEl().hasClass('hidden')){
36341             return true;
36342         }
36343         
36344         var valid = false;
36345         
36346         Roo.each(this.radioes, function(i){
36347             if(!i.checked){
36348                 return;
36349             }
36350             
36351             valid = true;
36352             return false;
36353         });
36354         
36355         if(this.allowBlank) {
36356             return true;
36357         }
36358         
36359         if(this.disabled || valid){
36360             this.markValid();
36361             return true;
36362         }
36363         
36364         this.markInvalid();
36365         return false;
36366         
36367     },
36368     
36369     markValid : function()
36370     {
36371         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36372             this.indicatorEl().removeClass('visible');
36373             this.indicatorEl().addClass('invisible');
36374         }
36375         
36376         
36377         if (Roo.bootstrap.version == 3) {
36378             this.el.removeClass([this.invalidClass, this.validClass]);
36379             this.el.addClass(this.validClass);
36380         } else {
36381             this.el.removeClass(['is-invalid','is-valid']);
36382             this.el.addClass(['is-valid']);
36383         }
36384         this.fireEvent('valid', this);
36385     },
36386     
36387     markInvalid : function(msg)
36388     {
36389         if(this.allowBlank || this.disabled){
36390             return;
36391         }
36392         
36393         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36394             this.indicatorEl().removeClass('invisible');
36395             this.indicatorEl().addClass('visible');
36396         }
36397         if (Roo.bootstrap.version == 3) {
36398             this.el.removeClass([this.invalidClass, this.validClass]);
36399             this.el.addClass(this.invalidClass);
36400         } else {
36401             this.el.removeClass(['is-invalid','is-valid']);
36402             this.el.addClass(['is-invalid']);
36403         }
36404         
36405         this.fireEvent('invalid', this, msg);
36406         
36407     },
36408     
36409     setValue : function(v, suppressEvent)
36410     {   
36411         if(this.value === v){
36412             return;
36413         }
36414         
36415         this.value = v;
36416         
36417         if(this.rendered){
36418             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36419         }
36420         
36421         Roo.each(this.radioes, function(i){
36422             i.checked = false;
36423             i.el.removeClass('checked');
36424         });
36425         
36426         Roo.each(this.radioes, function(i){
36427             
36428             if(i.value === v || i.value.toString() === v.toString()){
36429                 i.checked = true;
36430                 i.el.addClass('checked');
36431                 
36432                 if(suppressEvent !== true){
36433                     this.fireEvent('check', this, i);
36434                 }
36435                 
36436                 return false;
36437             }
36438             
36439         }, this);
36440         
36441         this.validate();
36442     },
36443     
36444     clearInvalid : function(){
36445         
36446         if(!this.el || this.preventMark){
36447             return;
36448         }
36449         
36450         this.el.removeClass([this.invalidClass]);
36451         
36452         this.fireEvent('valid', this);
36453     }
36454     
36455 });
36456
36457 Roo.apply(Roo.bootstrap.RadioSet, {
36458     
36459     groups: {},
36460     
36461     register : function(set)
36462     {
36463         this.groups[set.name] = set;
36464     },
36465     
36466     get: function(name) 
36467     {
36468         if (typeof(this.groups[name]) == 'undefined') {
36469             return false;
36470         }
36471         
36472         return this.groups[name] ;
36473     }
36474     
36475 });
36476 /*
36477  * Based on:
36478  * Ext JS Library 1.1.1
36479  * Copyright(c) 2006-2007, Ext JS, LLC.
36480  *
36481  * Originally Released Under LGPL - original licence link has changed is not relivant.
36482  *
36483  * Fork - LGPL
36484  * <script type="text/javascript">
36485  */
36486
36487
36488 /**
36489  * @class Roo.bootstrap.SplitBar
36490  * @extends Roo.util.Observable
36491  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36492  * <br><br>
36493  * Usage:
36494  * <pre><code>
36495 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36496                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36497 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36498 split.minSize = 100;
36499 split.maxSize = 600;
36500 split.animate = true;
36501 split.on('moved', splitterMoved);
36502 </code></pre>
36503  * @constructor
36504  * Create a new SplitBar
36505  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36506  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36507  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36508  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36509                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36510                         position of the SplitBar).
36511  */
36512 Roo.bootstrap.SplitBar = function(cfg){
36513     
36514     /** @private */
36515     
36516     //{
36517     //  dragElement : elm
36518     //  resizingElement: el,
36519         // optional..
36520     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36521     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36522         // existingProxy ???
36523     //}
36524     
36525     this.el = Roo.get(cfg.dragElement, true);
36526     this.el.dom.unselectable = "on";
36527     /** @private */
36528     this.resizingEl = Roo.get(cfg.resizingElement, true);
36529
36530     /**
36531      * @private
36532      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36533      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36534      * @type Number
36535      */
36536     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36537     
36538     /**
36539      * The minimum size of the resizing element. (Defaults to 0)
36540      * @type Number
36541      */
36542     this.minSize = 0;
36543     
36544     /**
36545      * The maximum size of the resizing element. (Defaults to 2000)
36546      * @type Number
36547      */
36548     this.maxSize = 2000;
36549     
36550     /**
36551      * Whether to animate the transition to the new size
36552      * @type Boolean
36553      */
36554     this.animate = false;
36555     
36556     /**
36557      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36558      * @type Boolean
36559      */
36560     this.useShim = false;
36561     
36562     /** @private */
36563     this.shim = null;
36564     
36565     if(!cfg.existingProxy){
36566         /** @private */
36567         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36568     }else{
36569         this.proxy = Roo.get(cfg.existingProxy).dom;
36570     }
36571     /** @private */
36572     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36573     
36574     /** @private */
36575     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36576     
36577     /** @private */
36578     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36579     
36580     /** @private */
36581     this.dragSpecs = {};
36582     
36583     /**
36584      * @private The adapter to use to positon and resize elements
36585      */
36586     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36587     this.adapter.init(this);
36588     
36589     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36590         /** @private */
36591         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36592         this.el.addClass("roo-splitbar-h");
36593     }else{
36594         /** @private */
36595         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36596         this.el.addClass("roo-splitbar-v");
36597     }
36598     
36599     this.addEvents({
36600         /**
36601          * @event resize
36602          * Fires when the splitter is moved (alias for {@link #event-moved})
36603          * @param {Roo.bootstrap.SplitBar} this
36604          * @param {Number} newSize the new width or height
36605          */
36606         "resize" : true,
36607         /**
36608          * @event moved
36609          * Fires when the splitter is moved
36610          * @param {Roo.bootstrap.SplitBar} this
36611          * @param {Number} newSize the new width or height
36612          */
36613         "moved" : true,
36614         /**
36615          * @event beforeresize
36616          * Fires before the splitter is dragged
36617          * @param {Roo.bootstrap.SplitBar} this
36618          */
36619         "beforeresize" : true,
36620
36621         "beforeapply" : true
36622     });
36623
36624     Roo.util.Observable.call(this);
36625 };
36626
36627 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36628     onStartProxyDrag : function(x, y){
36629         this.fireEvent("beforeresize", this);
36630         if(!this.overlay){
36631             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36632             o.unselectable();
36633             o.enableDisplayMode("block");
36634             // all splitbars share the same overlay
36635             Roo.bootstrap.SplitBar.prototype.overlay = o;
36636         }
36637         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36638         this.overlay.show();
36639         Roo.get(this.proxy).setDisplayed("block");
36640         var size = this.adapter.getElementSize(this);
36641         this.activeMinSize = this.getMinimumSize();;
36642         this.activeMaxSize = this.getMaximumSize();;
36643         var c1 = size - this.activeMinSize;
36644         var c2 = Math.max(this.activeMaxSize - size, 0);
36645         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36646             this.dd.resetConstraints();
36647             this.dd.setXConstraint(
36648                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36649                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36650             );
36651             this.dd.setYConstraint(0, 0);
36652         }else{
36653             this.dd.resetConstraints();
36654             this.dd.setXConstraint(0, 0);
36655             this.dd.setYConstraint(
36656                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36657                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36658             );
36659          }
36660         this.dragSpecs.startSize = size;
36661         this.dragSpecs.startPoint = [x, y];
36662         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36663     },
36664     
36665     /** 
36666      * @private Called after the drag operation by the DDProxy
36667      */
36668     onEndProxyDrag : function(e){
36669         Roo.get(this.proxy).setDisplayed(false);
36670         var endPoint = Roo.lib.Event.getXY(e);
36671         if(this.overlay){
36672             this.overlay.hide();
36673         }
36674         var newSize;
36675         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36676             newSize = this.dragSpecs.startSize + 
36677                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36678                     endPoint[0] - this.dragSpecs.startPoint[0] :
36679                     this.dragSpecs.startPoint[0] - endPoint[0]
36680                 );
36681         }else{
36682             newSize = this.dragSpecs.startSize + 
36683                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36684                     endPoint[1] - this.dragSpecs.startPoint[1] :
36685                     this.dragSpecs.startPoint[1] - endPoint[1]
36686                 );
36687         }
36688         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36689         if(newSize != this.dragSpecs.startSize){
36690             if(this.fireEvent('beforeapply', this, newSize) !== false){
36691                 this.adapter.setElementSize(this, newSize);
36692                 this.fireEvent("moved", this, newSize);
36693                 this.fireEvent("resize", this, newSize);
36694             }
36695         }
36696     },
36697     
36698     /**
36699      * Get the adapter this SplitBar uses
36700      * @return The adapter object
36701      */
36702     getAdapter : function(){
36703         return this.adapter;
36704     },
36705     
36706     /**
36707      * Set the adapter this SplitBar uses
36708      * @param {Object} adapter A SplitBar adapter object
36709      */
36710     setAdapter : function(adapter){
36711         this.adapter = adapter;
36712         this.adapter.init(this);
36713     },
36714     
36715     /**
36716      * Gets the minimum size for the resizing element
36717      * @return {Number} The minimum size
36718      */
36719     getMinimumSize : function(){
36720         return this.minSize;
36721     },
36722     
36723     /**
36724      * Sets the minimum size for the resizing element
36725      * @param {Number} minSize The minimum size
36726      */
36727     setMinimumSize : function(minSize){
36728         this.minSize = minSize;
36729     },
36730     
36731     /**
36732      * Gets the maximum size for the resizing element
36733      * @return {Number} The maximum size
36734      */
36735     getMaximumSize : function(){
36736         return this.maxSize;
36737     },
36738     
36739     /**
36740      * Sets the maximum size for the resizing element
36741      * @param {Number} maxSize The maximum size
36742      */
36743     setMaximumSize : function(maxSize){
36744         this.maxSize = maxSize;
36745     },
36746     
36747     /**
36748      * Sets the initialize size for the resizing element
36749      * @param {Number} size The initial size
36750      */
36751     setCurrentSize : function(size){
36752         var oldAnimate = this.animate;
36753         this.animate = false;
36754         this.adapter.setElementSize(this, size);
36755         this.animate = oldAnimate;
36756     },
36757     
36758     /**
36759      * Destroy this splitbar. 
36760      * @param {Boolean} removeEl True to remove the element
36761      */
36762     destroy : function(removeEl){
36763         if(this.shim){
36764             this.shim.remove();
36765         }
36766         this.dd.unreg();
36767         this.proxy.parentNode.removeChild(this.proxy);
36768         if(removeEl){
36769             this.el.remove();
36770         }
36771     }
36772 });
36773
36774 /**
36775  * @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.
36776  */
36777 Roo.bootstrap.SplitBar.createProxy = function(dir){
36778     var proxy = new Roo.Element(document.createElement("div"));
36779     proxy.unselectable();
36780     var cls = 'roo-splitbar-proxy';
36781     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36782     document.body.appendChild(proxy.dom);
36783     return proxy.dom;
36784 };
36785
36786 /** 
36787  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36788  * Default Adapter. It assumes the splitter and resizing element are not positioned
36789  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36790  */
36791 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36792 };
36793
36794 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36795     // do nothing for now
36796     init : function(s){
36797     
36798     },
36799     /**
36800      * Called before drag operations to get the current size of the resizing element. 
36801      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36802      */
36803      getElementSize : function(s){
36804         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36805             return s.resizingEl.getWidth();
36806         }else{
36807             return s.resizingEl.getHeight();
36808         }
36809     },
36810     
36811     /**
36812      * Called after drag operations to set the size of the resizing element.
36813      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36814      * @param {Number} newSize The new size to set
36815      * @param {Function} onComplete A function to be invoked when resizing is complete
36816      */
36817     setElementSize : function(s, newSize, onComplete){
36818         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36819             if(!s.animate){
36820                 s.resizingEl.setWidth(newSize);
36821                 if(onComplete){
36822                     onComplete(s, newSize);
36823                 }
36824             }else{
36825                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36826             }
36827         }else{
36828             
36829             if(!s.animate){
36830                 s.resizingEl.setHeight(newSize);
36831                 if(onComplete){
36832                     onComplete(s, newSize);
36833                 }
36834             }else{
36835                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36836             }
36837         }
36838     }
36839 };
36840
36841 /** 
36842  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36843  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36844  * Adapter that  moves the splitter element to align with the resized sizing element. 
36845  * Used with an absolute positioned SplitBar.
36846  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36847  * document.body, make sure you assign an id to the body element.
36848  */
36849 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36850     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36851     this.container = Roo.get(container);
36852 };
36853
36854 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36855     init : function(s){
36856         this.basic.init(s);
36857     },
36858     
36859     getElementSize : function(s){
36860         return this.basic.getElementSize(s);
36861     },
36862     
36863     setElementSize : function(s, newSize, onComplete){
36864         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36865     },
36866     
36867     moveSplitter : function(s){
36868         var yes = Roo.bootstrap.SplitBar;
36869         switch(s.placement){
36870             case yes.LEFT:
36871                 s.el.setX(s.resizingEl.getRight());
36872                 break;
36873             case yes.RIGHT:
36874                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36875                 break;
36876             case yes.TOP:
36877                 s.el.setY(s.resizingEl.getBottom());
36878                 break;
36879             case yes.BOTTOM:
36880                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36881                 break;
36882         }
36883     }
36884 };
36885
36886 /**
36887  * Orientation constant - Create a vertical SplitBar
36888  * @static
36889  * @type Number
36890  */
36891 Roo.bootstrap.SplitBar.VERTICAL = 1;
36892
36893 /**
36894  * Orientation constant - Create a horizontal SplitBar
36895  * @static
36896  * @type Number
36897  */
36898 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36899
36900 /**
36901  * Placement constant - The resizing element is to the left of the splitter element
36902  * @static
36903  * @type Number
36904  */
36905 Roo.bootstrap.SplitBar.LEFT = 1;
36906
36907 /**
36908  * Placement constant - The resizing element is to the right of the splitter element
36909  * @static
36910  * @type Number
36911  */
36912 Roo.bootstrap.SplitBar.RIGHT = 2;
36913
36914 /**
36915  * Placement constant - The resizing element is positioned above the splitter element
36916  * @static
36917  * @type Number
36918  */
36919 Roo.bootstrap.SplitBar.TOP = 3;
36920
36921 /**
36922  * Placement constant - The resizing element is positioned under splitter element
36923  * @static
36924  * @type Number
36925  */
36926 Roo.bootstrap.SplitBar.BOTTOM = 4;
36927 Roo.namespace("Roo.bootstrap.layout");/*
36928  * Based on:
36929  * Ext JS Library 1.1.1
36930  * Copyright(c) 2006-2007, Ext JS, LLC.
36931  *
36932  * Originally Released Under LGPL - original licence link has changed is not relivant.
36933  *
36934  * Fork - LGPL
36935  * <script type="text/javascript">
36936  */
36937
36938 /**
36939  * @class Roo.bootstrap.layout.Manager
36940  * @extends Roo.bootstrap.Component
36941  * Base class for layout managers.
36942  */
36943 Roo.bootstrap.layout.Manager = function(config)
36944 {
36945     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36946
36947
36948
36949
36950
36951     /** false to disable window resize monitoring @type Boolean */
36952     this.monitorWindowResize = true;
36953     this.regions = {};
36954     this.addEvents({
36955         /**
36956          * @event layout
36957          * Fires when a layout is performed.
36958          * @param {Roo.LayoutManager} this
36959          */
36960         "layout" : true,
36961         /**
36962          * @event regionresized
36963          * Fires when the user resizes a region.
36964          * @param {Roo.LayoutRegion} region The resized region
36965          * @param {Number} newSize The new size (width for east/west, height for north/south)
36966          */
36967         "regionresized" : true,
36968         /**
36969          * @event regioncollapsed
36970          * Fires when a region is collapsed.
36971          * @param {Roo.LayoutRegion} region The collapsed region
36972          */
36973         "regioncollapsed" : true,
36974         /**
36975          * @event regionexpanded
36976          * Fires when a region is expanded.
36977          * @param {Roo.LayoutRegion} region The expanded region
36978          */
36979         "regionexpanded" : true
36980     });
36981     this.updating = false;
36982
36983     if (config.el) {
36984         this.el = Roo.get(config.el);
36985         this.initEvents();
36986     }
36987
36988 };
36989
36990 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36991
36992
36993     regions : null,
36994
36995     monitorWindowResize : true,
36996
36997
36998     updating : false,
36999
37000
37001     onRender : function(ct, position)
37002     {
37003         if(!this.el){
37004             this.el = Roo.get(ct);
37005             this.initEvents();
37006         }
37007         //this.fireEvent('render',this);
37008     },
37009
37010
37011     initEvents: function()
37012     {
37013
37014
37015         // ie scrollbar fix
37016         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37017             document.body.scroll = "no";
37018         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37019             this.el.position('relative');
37020         }
37021         this.id = this.el.id;
37022         this.el.addClass("roo-layout-container");
37023         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37024         if(this.el.dom != document.body ) {
37025             this.el.on('resize', this.layout,this);
37026             this.el.on('show', this.layout,this);
37027         }
37028
37029     },
37030
37031     /**
37032      * Returns true if this layout is currently being updated
37033      * @return {Boolean}
37034      */
37035     isUpdating : function(){
37036         return this.updating;
37037     },
37038
37039     /**
37040      * Suspend the LayoutManager from doing auto-layouts while
37041      * making multiple add or remove calls
37042      */
37043     beginUpdate : function(){
37044         this.updating = true;
37045     },
37046
37047     /**
37048      * Restore auto-layouts and optionally disable the manager from performing a layout
37049      * @param {Boolean} noLayout true to disable a layout update
37050      */
37051     endUpdate : function(noLayout){
37052         this.updating = false;
37053         if(!noLayout){
37054             this.layout();
37055         }
37056     },
37057
37058     layout: function(){
37059         // abstract...
37060     },
37061
37062     onRegionResized : function(region, newSize){
37063         this.fireEvent("regionresized", region, newSize);
37064         this.layout();
37065     },
37066
37067     onRegionCollapsed : function(region){
37068         this.fireEvent("regioncollapsed", region);
37069     },
37070
37071     onRegionExpanded : function(region){
37072         this.fireEvent("regionexpanded", region);
37073     },
37074
37075     /**
37076      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37077      * performs box-model adjustments.
37078      * @return {Object} The size as an object {width: (the width), height: (the height)}
37079      */
37080     getViewSize : function()
37081     {
37082         var size;
37083         if(this.el.dom != document.body){
37084             size = this.el.getSize();
37085         }else{
37086             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37087         }
37088         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37089         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37090         return size;
37091     },
37092
37093     /**
37094      * Returns the Element this layout is bound to.
37095      * @return {Roo.Element}
37096      */
37097     getEl : function(){
37098         return this.el;
37099     },
37100
37101     /**
37102      * Returns the specified region.
37103      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37104      * @return {Roo.LayoutRegion}
37105      */
37106     getRegion : function(target){
37107         return this.regions[target.toLowerCase()];
37108     },
37109
37110     onWindowResize : function(){
37111         if(this.monitorWindowResize){
37112             this.layout();
37113         }
37114     }
37115 });
37116 /*
37117  * Based on:
37118  * Ext JS Library 1.1.1
37119  * Copyright(c) 2006-2007, Ext JS, LLC.
37120  *
37121  * Originally Released Under LGPL - original licence link has changed is not relivant.
37122  *
37123  * Fork - LGPL
37124  * <script type="text/javascript">
37125  */
37126 /**
37127  * @class Roo.bootstrap.layout.Border
37128  * @extends Roo.bootstrap.layout.Manager
37129  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37130  * please see: examples/bootstrap/nested.html<br><br>
37131  
37132 <b>The container the layout is rendered into can be either the body element or any other element.
37133 If it is not the body element, the container needs to either be an absolute positioned element,
37134 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37135 the container size if it is not the body element.</b>
37136
37137 * @constructor
37138 * Create a new Border
37139 * @param {Object} config Configuration options
37140  */
37141 Roo.bootstrap.layout.Border = function(config){
37142     config = config || {};
37143     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37144     
37145     
37146     
37147     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37148         if(config[region]){
37149             config[region].region = region;
37150             this.addRegion(config[region]);
37151         }
37152     },this);
37153     
37154 };
37155
37156 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37157
37158 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37159     
37160     parent : false, // this might point to a 'nest' or a ???
37161     
37162     /**
37163      * Creates and adds a new region if it doesn't already exist.
37164      * @param {String} target The target region key (north, south, east, west or center).
37165      * @param {Object} config The regions config object
37166      * @return {BorderLayoutRegion} The new region
37167      */
37168     addRegion : function(config)
37169     {
37170         if(!this.regions[config.region]){
37171             var r = this.factory(config);
37172             this.bindRegion(r);
37173         }
37174         return this.regions[config.region];
37175     },
37176
37177     // private (kinda)
37178     bindRegion : function(r){
37179         this.regions[r.config.region] = r;
37180         
37181         r.on("visibilitychange",    this.layout, this);
37182         r.on("paneladded",          this.layout, this);
37183         r.on("panelremoved",        this.layout, this);
37184         r.on("invalidated",         this.layout, this);
37185         r.on("resized",             this.onRegionResized, this);
37186         r.on("collapsed",           this.onRegionCollapsed, this);
37187         r.on("expanded",            this.onRegionExpanded, this);
37188     },
37189
37190     /**
37191      * Performs a layout update.
37192      */
37193     layout : function()
37194     {
37195         if(this.updating) {
37196             return;
37197         }
37198         
37199         // render all the rebions if they have not been done alreayd?
37200         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37201             if(this.regions[region] && !this.regions[region].bodyEl){
37202                 this.regions[region].onRender(this.el)
37203             }
37204         },this);
37205         
37206         var size = this.getViewSize();
37207         var w = size.width;
37208         var h = size.height;
37209         var centerW = w;
37210         var centerH = h;
37211         var centerY = 0;
37212         var centerX = 0;
37213         //var x = 0, y = 0;
37214
37215         var rs = this.regions;
37216         var north = rs["north"];
37217         var south = rs["south"]; 
37218         var west = rs["west"];
37219         var east = rs["east"];
37220         var center = rs["center"];
37221         //if(this.hideOnLayout){ // not supported anymore
37222             //c.el.setStyle("display", "none");
37223         //}
37224         if(north && north.isVisible()){
37225             var b = north.getBox();
37226             var m = north.getMargins();
37227             b.width = w - (m.left+m.right);
37228             b.x = m.left;
37229             b.y = m.top;
37230             centerY = b.height + b.y + m.bottom;
37231             centerH -= centerY;
37232             north.updateBox(this.safeBox(b));
37233         }
37234         if(south && south.isVisible()){
37235             var b = south.getBox();
37236             var m = south.getMargins();
37237             b.width = w - (m.left+m.right);
37238             b.x = m.left;
37239             var totalHeight = (b.height + m.top + m.bottom);
37240             b.y = h - totalHeight + m.top;
37241             centerH -= totalHeight;
37242             south.updateBox(this.safeBox(b));
37243         }
37244         if(west && west.isVisible()){
37245             var b = west.getBox();
37246             var m = west.getMargins();
37247             b.height = centerH - (m.top+m.bottom);
37248             b.x = m.left;
37249             b.y = centerY + m.top;
37250             var totalWidth = (b.width + m.left + m.right);
37251             centerX += totalWidth;
37252             centerW -= totalWidth;
37253             west.updateBox(this.safeBox(b));
37254         }
37255         if(east && east.isVisible()){
37256             var b = east.getBox();
37257             var m = east.getMargins();
37258             b.height = centerH - (m.top+m.bottom);
37259             var totalWidth = (b.width + m.left + m.right);
37260             b.x = w - totalWidth + m.left;
37261             b.y = centerY + m.top;
37262             centerW -= totalWidth;
37263             east.updateBox(this.safeBox(b));
37264         }
37265         if(center){
37266             var m = center.getMargins();
37267             var centerBox = {
37268                 x: centerX + m.left,
37269                 y: centerY + m.top,
37270                 width: centerW - (m.left+m.right),
37271                 height: centerH - (m.top+m.bottom)
37272             };
37273             //if(this.hideOnLayout){
37274                 //center.el.setStyle("display", "block");
37275             //}
37276             center.updateBox(this.safeBox(centerBox));
37277         }
37278         this.el.repaint();
37279         this.fireEvent("layout", this);
37280     },
37281
37282     // private
37283     safeBox : function(box){
37284         box.width = Math.max(0, box.width);
37285         box.height = Math.max(0, box.height);
37286         return box;
37287     },
37288
37289     /**
37290      * Adds a ContentPanel (or subclass) to this layout.
37291      * @param {String} target The target region key (north, south, east, west or center).
37292      * @param {Roo.ContentPanel} panel The panel to add
37293      * @return {Roo.ContentPanel} The added panel
37294      */
37295     add : function(target, panel){
37296          
37297         target = target.toLowerCase();
37298         return this.regions[target].add(panel);
37299     },
37300
37301     /**
37302      * Remove a ContentPanel (or subclass) to this layout.
37303      * @param {String} target The target region key (north, south, east, west or center).
37304      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37305      * @return {Roo.ContentPanel} The removed panel
37306      */
37307     remove : function(target, panel){
37308         target = target.toLowerCase();
37309         return this.regions[target].remove(panel);
37310     },
37311
37312     /**
37313      * Searches all regions for a panel with the specified id
37314      * @param {String} panelId
37315      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37316      */
37317     findPanel : function(panelId){
37318         var rs = this.regions;
37319         for(var target in rs){
37320             if(typeof rs[target] != "function"){
37321                 var p = rs[target].getPanel(panelId);
37322                 if(p){
37323                     return p;
37324                 }
37325             }
37326         }
37327         return null;
37328     },
37329
37330     /**
37331      * Searches all regions for a panel with the specified id and activates (shows) it.
37332      * @param {String/ContentPanel} panelId The panels id or the panel itself
37333      * @return {Roo.ContentPanel} The shown panel or null
37334      */
37335     showPanel : function(panelId) {
37336       var rs = this.regions;
37337       for(var target in rs){
37338          var r = rs[target];
37339          if(typeof r != "function"){
37340             if(r.hasPanel(panelId)){
37341                return r.showPanel(panelId);
37342             }
37343          }
37344       }
37345       return null;
37346    },
37347
37348    /**
37349      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37350      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37351      */
37352    /*
37353     restoreState : function(provider){
37354         if(!provider){
37355             provider = Roo.state.Manager;
37356         }
37357         var sm = new Roo.LayoutStateManager();
37358         sm.init(this, provider);
37359     },
37360 */
37361  
37362  
37363     /**
37364      * Adds a xtype elements to the layout.
37365      * <pre><code>
37366
37367 layout.addxtype({
37368        xtype : 'ContentPanel',
37369        region: 'west',
37370        items: [ .... ]
37371    }
37372 );
37373
37374 layout.addxtype({
37375         xtype : 'NestedLayoutPanel',
37376         region: 'west',
37377         layout: {
37378            center: { },
37379            west: { }   
37380         },
37381         items : [ ... list of content panels or nested layout panels.. ]
37382    }
37383 );
37384 </code></pre>
37385      * @param {Object} cfg Xtype definition of item to add.
37386      */
37387     addxtype : function(cfg)
37388     {
37389         // basically accepts a pannel...
37390         // can accept a layout region..!?!?
37391         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37392         
37393         
37394         // theory?  children can only be panels??
37395         
37396         //if (!cfg.xtype.match(/Panel$/)) {
37397         //    return false;
37398         //}
37399         var ret = false;
37400         
37401         if (typeof(cfg.region) == 'undefined') {
37402             Roo.log("Failed to add Panel, region was not set");
37403             Roo.log(cfg);
37404             return false;
37405         }
37406         var region = cfg.region;
37407         delete cfg.region;
37408         
37409           
37410         var xitems = [];
37411         if (cfg.items) {
37412             xitems = cfg.items;
37413             delete cfg.items;
37414         }
37415         var nb = false;
37416         
37417         if ( region == 'center') {
37418             Roo.log("Center: " + cfg.title);
37419         }
37420         
37421         
37422         switch(cfg.xtype) 
37423         {
37424             case 'Content':  // ContentPanel (el, cfg)
37425             case 'Scroll':  // ContentPanel (el, cfg)
37426             case 'View': 
37427                 cfg.autoCreate = cfg.autoCreate || true;
37428                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37429                 //} else {
37430                 //    var el = this.el.createChild();
37431                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37432                 //}
37433                 
37434                 this.add(region, ret);
37435                 break;
37436             
37437             /*
37438             case 'TreePanel': // our new panel!
37439                 cfg.el = this.el.createChild();
37440                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37441                 this.add(region, ret);
37442                 break;
37443             */
37444             
37445             case 'Nest': 
37446                 // create a new Layout (which is  a Border Layout...
37447                 
37448                 var clayout = cfg.layout;
37449                 clayout.el  = this.el.createChild();
37450                 clayout.items   = clayout.items  || [];
37451                 
37452                 delete cfg.layout;
37453                 
37454                 // replace this exitems with the clayout ones..
37455                 xitems = clayout.items;
37456                  
37457                 // force background off if it's in center...
37458                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37459                     cfg.background = false;
37460                 }
37461                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37462                 
37463                 
37464                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37465                 //console.log('adding nested layout panel '  + cfg.toSource());
37466                 this.add(region, ret);
37467                 nb = {}; /// find first...
37468                 break;
37469             
37470             case 'Grid':
37471                 
37472                 // needs grid and region
37473                 
37474                 //var el = this.getRegion(region).el.createChild();
37475                 /*
37476                  *var el = this.el.createChild();
37477                 // create the grid first...
37478                 cfg.grid.container = el;
37479                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37480                 */
37481                 
37482                 if (region == 'center' && this.active ) {
37483                     cfg.background = false;
37484                 }
37485                 
37486                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37487                 
37488                 this.add(region, ret);
37489                 /*
37490                 if (cfg.background) {
37491                     // render grid on panel activation (if panel background)
37492                     ret.on('activate', function(gp) {
37493                         if (!gp.grid.rendered) {
37494                     //        gp.grid.render(el);
37495                         }
37496                     });
37497                 } else {
37498                   //  cfg.grid.render(el);
37499                 }
37500                 */
37501                 break;
37502            
37503            
37504             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37505                 // it was the old xcomponent building that caused this before.
37506                 // espeically if border is the top element in the tree.
37507                 ret = this;
37508                 break; 
37509                 
37510                     
37511                 
37512                 
37513                 
37514             default:
37515                 /*
37516                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37517                     
37518                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37519                     this.add(region, ret);
37520                 } else {
37521                 */
37522                     Roo.log(cfg);
37523                     throw "Can not add '" + cfg.xtype + "' to Border";
37524                     return null;
37525              
37526                                 
37527              
37528         }
37529         this.beginUpdate();
37530         // add children..
37531         var region = '';
37532         var abn = {};
37533         Roo.each(xitems, function(i)  {
37534             region = nb && i.region ? i.region : false;
37535             
37536             var add = ret.addxtype(i);
37537            
37538             if (region) {
37539                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37540                 if (!i.background) {
37541                     abn[region] = nb[region] ;
37542                 }
37543             }
37544             
37545         });
37546         this.endUpdate();
37547
37548         // make the last non-background panel active..
37549         //if (nb) { Roo.log(abn); }
37550         if (nb) {
37551             
37552             for(var r in abn) {
37553                 region = this.getRegion(r);
37554                 if (region) {
37555                     // tried using nb[r], but it does not work..
37556                      
37557                     region.showPanel(abn[r]);
37558                    
37559                 }
37560             }
37561         }
37562         return ret;
37563         
37564     },
37565     
37566     
37567 // private
37568     factory : function(cfg)
37569     {
37570         
37571         var validRegions = Roo.bootstrap.layout.Border.regions;
37572
37573         var target = cfg.region;
37574         cfg.mgr = this;
37575         
37576         var r = Roo.bootstrap.layout;
37577         Roo.log(target);
37578         switch(target){
37579             case "north":
37580                 return new r.North(cfg);
37581             case "south":
37582                 return new r.South(cfg);
37583             case "east":
37584                 return new r.East(cfg);
37585             case "west":
37586                 return new r.West(cfg);
37587             case "center":
37588                 return new r.Center(cfg);
37589         }
37590         throw 'Layout region "'+target+'" not supported.';
37591     }
37592     
37593     
37594 });
37595  /*
37596  * Based on:
37597  * Ext JS Library 1.1.1
37598  * Copyright(c) 2006-2007, Ext JS, LLC.
37599  *
37600  * Originally Released Under LGPL - original licence link has changed is not relivant.
37601  *
37602  * Fork - LGPL
37603  * <script type="text/javascript">
37604  */
37605  
37606 /**
37607  * @class Roo.bootstrap.layout.Basic
37608  * @extends Roo.util.Observable
37609  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37610  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37611  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37612  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37613  * @cfg {string}   region  the region that it inhabits..
37614  * @cfg {bool}   skipConfig skip config?
37615  * 
37616
37617  */
37618 Roo.bootstrap.layout.Basic = function(config){
37619     
37620     this.mgr = config.mgr;
37621     
37622     this.position = config.region;
37623     
37624     var skipConfig = config.skipConfig;
37625     
37626     this.events = {
37627         /**
37628          * @scope Roo.BasicLayoutRegion
37629          */
37630         
37631         /**
37632          * @event beforeremove
37633          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37634          * @param {Roo.LayoutRegion} this
37635          * @param {Roo.ContentPanel} panel The panel
37636          * @param {Object} e The cancel event object
37637          */
37638         "beforeremove" : true,
37639         /**
37640          * @event invalidated
37641          * Fires when the layout for this region is changed.
37642          * @param {Roo.LayoutRegion} this
37643          */
37644         "invalidated" : true,
37645         /**
37646          * @event visibilitychange
37647          * Fires when this region is shown or hidden 
37648          * @param {Roo.LayoutRegion} this
37649          * @param {Boolean} visibility true or false
37650          */
37651         "visibilitychange" : true,
37652         /**
37653          * @event paneladded
37654          * Fires when a panel is added. 
37655          * @param {Roo.LayoutRegion} this
37656          * @param {Roo.ContentPanel} panel The panel
37657          */
37658         "paneladded" : true,
37659         /**
37660          * @event panelremoved
37661          * Fires when a panel is removed. 
37662          * @param {Roo.LayoutRegion} this
37663          * @param {Roo.ContentPanel} panel The panel
37664          */
37665         "panelremoved" : true,
37666         /**
37667          * @event beforecollapse
37668          * Fires when this region before collapse.
37669          * @param {Roo.LayoutRegion} this
37670          */
37671         "beforecollapse" : true,
37672         /**
37673          * @event collapsed
37674          * Fires when this region is collapsed.
37675          * @param {Roo.LayoutRegion} this
37676          */
37677         "collapsed" : true,
37678         /**
37679          * @event expanded
37680          * Fires when this region is expanded.
37681          * @param {Roo.LayoutRegion} this
37682          */
37683         "expanded" : true,
37684         /**
37685          * @event slideshow
37686          * Fires when this region is slid into view.
37687          * @param {Roo.LayoutRegion} this
37688          */
37689         "slideshow" : true,
37690         /**
37691          * @event slidehide
37692          * Fires when this region slides out of view. 
37693          * @param {Roo.LayoutRegion} this
37694          */
37695         "slidehide" : true,
37696         /**
37697          * @event panelactivated
37698          * Fires when a panel is activated. 
37699          * @param {Roo.LayoutRegion} this
37700          * @param {Roo.ContentPanel} panel The activated panel
37701          */
37702         "panelactivated" : true,
37703         /**
37704          * @event resized
37705          * Fires when the user resizes this region. 
37706          * @param {Roo.LayoutRegion} this
37707          * @param {Number} newSize The new size (width for east/west, height for north/south)
37708          */
37709         "resized" : true
37710     };
37711     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37712     this.panels = new Roo.util.MixedCollection();
37713     this.panels.getKey = this.getPanelId.createDelegate(this);
37714     this.box = null;
37715     this.activePanel = null;
37716     // ensure listeners are added...
37717     
37718     if (config.listeners || config.events) {
37719         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37720             listeners : config.listeners || {},
37721             events : config.events || {}
37722         });
37723     }
37724     
37725     if(skipConfig !== true){
37726         this.applyConfig(config);
37727     }
37728 };
37729
37730 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37731 {
37732     getPanelId : function(p){
37733         return p.getId();
37734     },
37735     
37736     applyConfig : function(config){
37737         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37738         this.config = config;
37739         
37740     },
37741     
37742     /**
37743      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37744      * the width, for horizontal (north, south) the height.
37745      * @param {Number} newSize The new width or height
37746      */
37747     resizeTo : function(newSize){
37748         var el = this.el ? this.el :
37749                  (this.activePanel ? this.activePanel.getEl() : null);
37750         if(el){
37751             switch(this.position){
37752                 case "east":
37753                 case "west":
37754                     el.setWidth(newSize);
37755                     this.fireEvent("resized", this, newSize);
37756                 break;
37757                 case "north":
37758                 case "south":
37759                     el.setHeight(newSize);
37760                     this.fireEvent("resized", this, newSize);
37761                 break;                
37762             }
37763         }
37764     },
37765     
37766     getBox : function(){
37767         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37768     },
37769     
37770     getMargins : function(){
37771         return this.margins;
37772     },
37773     
37774     updateBox : function(box){
37775         this.box = box;
37776         var el = this.activePanel.getEl();
37777         el.dom.style.left = box.x + "px";
37778         el.dom.style.top = box.y + "px";
37779         this.activePanel.setSize(box.width, box.height);
37780     },
37781     
37782     /**
37783      * Returns the container element for this region.
37784      * @return {Roo.Element}
37785      */
37786     getEl : function(){
37787         return this.activePanel;
37788     },
37789     
37790     /**
37791      * Returns true if this region is currently visible.
37792      * @return {Boolean}
37793      */
37794     isVisible : function(){
37795         return this.activePanel ? true : false;
37796     },
37797     
37798     setActivePanel : function(panel){
37799         panel = this.getPanel(panel);
37800         if(this.activePanel && this.activePanel != panel){
37801             this.activePanel.setActiveState(false);
37802             this.activePanel.getEl().setLeftTop(-10000,-10000);
37803         }
37804         this.activePanel = panel;
37805         panel.setActiveState(true);
37806         if(this.box){
37807             panel.setSize(this.box.width, this.box.height);
37808         }
37809         this.fireEvent("panelactivated", this, panel);
37810         this.fireEvent("invalidated");
37811     },
37812     
37813     /**
37814      * Show the specified panel.
37815      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37816      * @return {Roo.ContentPanel} The shown panel or null
37817      */
37818     showPanel : function(panel){
37819         panel = this.getPanel(panel);
37820         if(panel){
37821             this.setActivePanel(panel);
37822         }
37823         return panel;
37824     },
37825     
37826     /**
37827      * Get the active panel for this region.
37828      * @return {Roo.ContentPanel} The active panel or null
37829      */
37830     getActivePanel : function(){
37831         return this.activePanel;
37832     },
37833     
37834     /**
37835      * Add the passed ContentPanel(s)
37836      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37837      * @return {Roo.ContentPanel} The panel added (if only one was added)
37838      */
37839     add : function(panel){
37840         if(arguments.length > 1){
37841             for(var i = 0, len = arguments.length; i < len; i++) {
37842                 this.add(arguments[i]);
37843             }
37844             return null;
37845         }
37846         if(this.hasPanel(panel)){
37847             this.showPanel(panel);
37848             return panel;
37849         }
37850         var el = panel.getEl();
37851         if(el.dom.parentNode != this.mgr.el.dom){
37852             this.mgr.el.dom.appendChild(el.dom);
37853         }
37854         if(panel.setRegion){
37855             panel.setRegion(this);
37856         }
37857         this.panels.add(panel);
37858         el.setStyle("position", "absolute");
37859         if(!panel.background){
37860             this.setActivePanel(panel);
37861             if(this.config.initialSize && this.panels.getCount()==1){
37862                 this.resizeTo(this.config.initialSize);
37863             }
37864         }
37865         this.fireEvent("paneladded", this, panel);
37866         return panel;
37867     },
37868     
37869     /**
37870      * Returns true if the panel is in this region.
37871      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37872      * @return {Boolean}
37873      */
37874     hasPanel : function(panel){
37875         if(typeof panel == "object"){ // must be panel obj
37876             panel = panel.getId();
37877         }
37878         return this.getPanel(panel) ? true : false;
37879     },
37880     
37881     /**
37882      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37883      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37884      * @param {Boolean} preservePanel Overrides the config preservePanel option
37885      * @return {Roo.ContentPanel} The panel that was removed
37886      */
37887     remove : function(panel, preservePanel){
37888         panel = this.getPanel(panel);
37889         if(!panel){
37890             return null;
37891         }
37892         var e = {};
37893         this.fireEvent("beforeremove", this, panel, e);
37894         if(e.cancel === true){
37895             return null;
37896         }
37897         var panelId = panel.getId();
37898         this.panels.removeKey(panelId);
37899         return panel;
37900     },
37901     
37902     /**
37903      * Returns the panel specified or null if it's not in this region.
37904      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37905      * @return {Roo.ContentPanel}
37906      */
37907     getPanel : function(id){
37908         if(typeof id == "object"){ // must be panel obj
37909             return id;
37910         }
37911         return this.panels.get(id);
37912     },
37913     
37914     /**
37915      * Returns this regions position (north/south/east/west/center).
37916      * @return {String} 
37917      */
37918     getPosition: function(){
37919         return this.position;    
37920     }
37921 });/*
37922  * Based on:
37923  * Ext JS Library 1.1.1
37924  * Copyright(c) 2006-2007, Ext JS, LLC.
37925  *
37926  * Originally Released Under LGPL - original licence link has changed is not relivant.
37927  *
37928  * Fork - LGPL
37929  * <script type="text/javascript">
37930  */
37931  
37932 /**
37933  * @class Roo.bootstrap.layout.Region
37934  * @extends Roo.bootstrap.layout.Basic
37935  * This class represents a region in a layout manager.
37936  
37937  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37938  * @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})
37939  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37940  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37941  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37942  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37943  * @cfg {String}    title           The title for the region (overrides panel titles)
37944  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37945  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37946  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37947  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37948  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37949  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37950  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37951  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37952  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37953  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37954
37955  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37956  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37957  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37958  * @cfg {Number}    width           For East/West panels
37959  * @cfg {Number}    height          For North/South panels
37960  * @cfg {Boolean}   split           To show the splitter
37961  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37962  * 
37963  * @cfg {string}   cls             Extra CSS classes to add to region
37964  * 
37965  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37966  * @cfg {string}   region  the region that it inhabits..
37967  *
37968
37969  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37970  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37971
37972  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37973  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37974  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37975  */
37976 Roo.bootstrap.layout.Region = function(config)
37977 {
37978     this.applyConfig(config);
37979
37980     var mgr = config.mgr;
37981     var pos = config.region;
37982     config.skipConfig = true;
37983     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37984     
37985     if (mgr.el) {
37986         this.onRender(mgr.el);   
37987     }
37988      
37989     this.visible = true;
37990     this.collapsed = false;
37991     this.unrendered_panels = [];
37992 };
37993
37994 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37995
37996     position: '', // set by wrapper (eg. north/south etc..)
37997     unrendered_panels : null,  // unrendered panels.
37998     
37999     tabPosition : false,
38000     
38001     mgr: false, // points to 'Border'
38002     
38003     
38004     createBody : function(){
38005         /** This region's body element 
38006         * @type Roo.Element */
38007         this.bodyEl = this.el.createChild({
38008                 tag: "div",
38009                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38010         });
38011     },
38012
38013     onRender: function(ctr, pos)
38014     {
38015         var dh = Roo.DomHelper;
38016         /** This region's container element 
38017         * @type Roo.Element */
38018         this.el = dh.append(ctr.dom, {
38019                 tag: "div",
38020                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38021             }, true);
38022         /** This region's title element 
38023         * @type Roo.Element */
38024     
38025         this.titleEl = dh.append(this.el.dom,  {
38026                 tag: "div",
38027                 unselectable: "on",
38028                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38029                 children:[
38030                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38031                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38032                 ]
38033             }, true);
38034         
38035         this.titleEl.enableDisplayMode();
38036         /** This region's title text element 
38037         * @type HTMLElement */
38038         this.titleTextEl = this.titleEl.dom.firstChild;
38039         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38040         /*
38041         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38042         this.closeBtn.enableDisplayMode();
38043         this.closeBtn.on("click", this.closeClicked, this);
38044         this.closeBtn.hide();
38045     */
38046         this.createBody(this.config);
38047         if(this.config.hideWhenEmpty){
38048             this.hide();
38049             this.on("paneladded", this.validateVisibility, this);
38050             this.on("panelremoved", this.validateVisibility, this);
38051         }
38052         if(this.autoScroll){
38053             this.bodyEl.setStyle("overflow", "auto");
38054         }else{
38055             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38056         }
38057         //if(c.titlebar !== false){
38058             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38059                 this.titleEl.hide();
38060             }else{
38061                 this.titleEl.show();
38062                 if(this.config.title){
38063                     this.titleTextEl.innerHTML = this.config.title;
38064                 }
38065             }
38066         //}
38067         if(this.config.collapsed){
38068             this.collapse(true);
38069         }
38070         if(this.config.hidden){
38071             this.hide();
38072         }
38073         
38074         if (this.unrendered_panels && this.unrendered_panels.length) {
38075             for (var i =0;i< this.unrendered_panels.length; i++) {
38076                 this.add(this.unrendered_panels[i]);
38077             }
38078             this.unrendered_panels = null;
38079             
38080         }
38081         
38082     },
38083     
38084     applyConfig : function(c)
38085     {
38086         /*
38087          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38088             var dh = Roo.DomHelper;
38089             if(c.titlebar !== false){
38090                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38091                 this.collapseBtn.on("click", this.collapse, this);
38092                 this.collapseBtn.enableDisplayMode();
38093                 /*
38094                 if(c.showPin === true || this.showPin){
38095                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38096                     this.stickBtn.enableDisplayMode();
38097                     this.stickBtn.on("click", this.expand, this);
38098                     this.stickBtn.hide();
38099                 }
38100                 
38101             }
38102             */
38103             /** This region's collapsed element
38104             * @type Roo.Element */
38105             /*
38106              *
38107             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38108                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38109             ]}, true);
38110             
38111             if(c.floatable !== false){
38112                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38113                this.collapsedEl.on("click", this.collapseClick, this);
38114             }
38115
38116             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38117                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38118                    id: "message", unselectable: "on", style:{"float":"left"}});
38119                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38120              }
38121             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38122             this.expandBtn.on("click", this.expand, this);
38123             
38124         }
38125         
38126         if(this.collapseBtn){
38127             this.collapseBtn.setVisible(c.collapsible == true);
38128         }
38129         
38130         this.cmargins = c.cmargins || this.cmargins ||
38131                          (this.position == "west" || this.position == "east" ?
38132                              {top: 0, left: 2, right:2, bottom: 0} :
38133                              {top: 2, left: 0, right:0, bottom: 2});
38134         */
38135         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38136         
38137         
38138         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38139         
38140         this.autoScroll = c.autoScroll || false;
38141         
38142         
38143        
38144         
38145         this.duration = c.duration || .30;
38146         this.slideDuration = c.slideDuration || .45;
38147         this.config = c;
38148        
38149     },
38150     /**
38151      * Returns true if this region is currently visible.
38152      * @return {Boolean}
38153      */
38154     isVisible : function(){
38155         return this.visible;
38156     },
38157
38158     /**
38159      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38160      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38161      */
38162     //setCollapsedTitle : function(title){
38163     //    title = title || "&#160;";
38164      //   if(this.collapsedTitleTextEl){
38165       //      this.collapsedTitleTextEl.innerHTML = title;
38166        // }
38167     //},
38168
38169     getBox : function(){
38170         var b;
38171       //  if(!this.collapsed){
38172             b = this.el.getBox(false, true);
38173        // }else{
38174           //  b = this.collapsedEl.getBox(false, true);
38175         //}
38176         return b;
38177     },
38178
38179     getMargins : function(){
38180         return this.margins;
38181         //return this.collapsed ? this.cmargins : this.margins;
38182     },
38183 /*
38184     highlight : function(){
38185         this.el.addClass("x-layout-panel-dragover");
38186     },
38187
38188     unhighlight : function(){
38189         this.el.removeClass("x-layout-panel-dragover");
38190     },
38191 */
38192     updateBox : function(box)
38193     {
38194         if (!this.bodyEl) {
38195             return; // not rendered yet..
38196         }
38197         
38198         this.box = box;
38199         if(!this.collapsed){
38200             this.el.dom.style.left = box.x + "px";
38201             this.el.dom.style.top = box.y + "px";
38202             this.updateBody(box.width, box.height);
38203         }else{
38204             this.collapsedEl.dom.style.left = box.x + "px";
38205             this.collapsedEl.dom.style.top = box.y + "px";
38206             this.collapsedEl.setSize(box.width, box.height);
38207         }
38208         if(this.tabs){
38209             this.tabs.autoSizeTabs();
38210         }
38211     },
38212
38213     updateBody : function(w, h)
38214     {
38215         if(w !== null){
38216             this.el.setWidth(w);
38217             w -= this.el.getBorderWidth("rl");
38218             if(this.config.adjustments){
38219                 w += this.config.adjustments[0];
38220             }
38221         }
38222         if(h !== null && h > 0){
38223             this.el.setHeight(h);
38224             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38225             h -= this.el.getBorderWidth("tb");
38226             if(this.config.adjustments){
38227                 h += this.config.adjustments[1];
38228             }
38229             this.bodyEl.setHeight(h);
38230             if(this.tabs){
38231                 h = this.tabs.syncHeight(h);
38232             }
38233         }
38234         if(this.panelSize){
38235             w = w !== null ? w : this.panelSize.width;
38236             h = h !== null ? h : this.panelSize.height;
38237         }
38238         if(this.activePanel){
38239             var el = this.activePanel.getEl();
38240             w = w !== null ? w : el.getWidth();
38241             h = h !== null ? h : el.getHeight();
38242             this.panelSize = {width: w, height: h};
38243             this.activePanel.setSize(w, h);
38244         }
38245         if(Roo.isIE && this.tabs){
38246             this.tabs.el.repaint();
38247         }
38248     },
38249
38250     /**
38251      * Returns the container element for this region.
38252      * @return {Roo.Element}
38253      */
38254     getEl : function(){
38255         return this.el;
38256     },
38257
38258     /**
38259      * Hides this region.
38260      */
38261     hide : function(){
38262         //if(!this.collapsed){
38263             this.el.dom.style.left = "-2000px";
38264             this.el.hide();
38265         //}else{
38266          //   this.collapsedEl.dom.style.left = "-2000px";
38267          //   this.collapsedEl.hide();
38268        // }
38269         this.visible = false;
38270         this.fireEvent("visibilitychange", this, false);
38271     },
38272
38273     /**
38274      * Shows this region if it was previously hidden.
38275      */
38276     show : function(){
38277         //if(!this.collapsed){
38278             this.el.show();
38279         //}else{
38280         //    this.collapsedEl.show();
38281        // }
38282         this.visible = true;
38283         this.fireEvent("visibilitychange", this, true);
38284     },
38285 /*
38286     closeClicked : function(){
38287         if(this.activePanel){
38288             this.remove(this.activePanel);
38289         }
38290     },
38291
38292     collapseClick : function(e){
38293         if(this.isSlid){
38294            e.stopPropagation();
38295            this.slideIn();
38296         }else{
38297            e.stopPropagation();
38298            this.slideOut();
38299         }
38300     },
38301 */
38302     /**
38303      * Collapses this region.
38304      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38305      */
38306     /*
38307     collapse : function(skipAnim, skipCheck = false){
38308         if(this.collapsed) {
38309             return;
38310         }
38311         
38312         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38313             
38314             this.collapsed = true;
38315             if(this.split){
38316                 this.split.el.hide();
38317             }
38318             if(this.config.animate && skipAnim !== true){
38319                 this.fireEvent("invalidated", this);
38320                 this.animateCollapse();
38321             }else{
38322                 this.el.setLocation(-20000,-20000);
38323                 this.el.hide();
38324                 this.collapsedEl.show();
38325                 this.fireEvent("collapsed", this);
38326                 this.fireEvent("invalidated", this);
38327             }
38328         }
38329         
38330     },
38331 */
38332     animateCollapse : function(){
38333         // overridden
38334     },
38335
38336     /**
38337      * Expands this region if it was previously collapsed.
38338      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38339      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38340      */
38341     /*
38342     expand : function(e, skipAnim){
38343         if(e) {
38344             e.stopPropagation();
38345         }
38346         if(!this.collapsed || this.el.hasActiveFx()) {
38347             return;
38348         }
38349         if(this.isSlid){
38350             this.afterSlideIn();
38351             skipAnim = true;
38352         }
38353         this.collapsed = false;
38354         if(this.config.animate && skipAnim !== true){
38355             this.animateExpand();
38356         }else{
38357             this.el.show();
38358             if(this.split){
38359                 this.split.el.show();
38360             }
38361             this.collapsedEl.setLocation(-2000,-2000);
38362             this.collapsedEl.hide();
38363             this.fireEvent("invalidated", this);
38364             this.fireEvent("expanded", this);
38365         }
38366     },
38367 */
38368     animateExpand : function(){
38369         // overridden
38370     },
38371
38372     initTabs : function()
38373     {
38374         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38375         
38376         var ts = new Roo.bootstrap.panel.Tabs({
38377             el: this.bodyEl.dom,
38378             region : this,
38379             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38380             disableTooltips: this.config.disableTabTips,
38381             toolbar : this.config.toolbar
38382         });
38383         
38384         if(this.config.hideTabs){
38385             ts.stripWrap.setDisplayed(false);
38386         }
38387         this.tabs = ts;
38388         ts.resizeTabs = this.config.resizeTabs === true;
38389         ts.minTabWidth = this.config.minTabWidth || 40;
38390         ts.maxTabWidth = this.config.maxTabWidth || 250;
38391         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38392         ts.monitorResize = false;
38393         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38394         ts.bodyEl.addClass('roo-layout-tabs-body');
38395         this.panels.each(this.initPanelAsTab, this);
38396     },
38397
38398     initPanelAsTab : function(panel){
38399         var ti = this.tabs.addTab(
38400             panel.getEl().id,
38401             panel.getTitle(),
38402             null,
38403             this.config.closeOnTab && panel.isClosable(),
38404             panel.tpl
38405         );
38406         if(panel.tabTip !== undefined){
38407             ti.setTooltip(panel.tabTip);
38408         }
38409         ti.on("activate", function(){
38410               this.setActivePanel(panel);
38411         }, this);
38412         
38413         if(this.config.closeOnTab){
38414             ti.on("beforeclose", function(t, e){
38415                 e.cancel = true;
38416                 this.remove(panel);
38417             }, this);
38418         }
38419         
38420         panel.tabItem = ti;
38421         
38422         return ti;
38423     },
38424
38425     updatePanelTitle : function(panel, title)
38426     {
38427         if(this.activePanel == panel){
38428             this.updateTitle(title);
38429         }
38430         if(this.tabs){
38431             var ti = this.tabs.getTab(panel.getEl().id);
38432             ti.setText(title);
38433             if(panel.tabTip !== undefined){
38434                 ti.setTooltip(panel.tabTip);
38435             }
38436         }
38437     },
38438
38439     updateTitle : function(title){
38440         if(this.titleTextEl && !this.config.title){
38441             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38442         }
38443     },
38444
38445     setActivePanel : function(panel)
38446     {
38447         panel = this.getPanel(panel);
38448         if(this.activePanel && this.activePanel != panel){
38449             if(this.activePanel.setActiveState(false) === false){
38450                 return;
38451             }
38452         }
38453         this.activePanel = panel;
38454         panel.setActiveState(true);
38455         if(this.panelSize){
38456             panel.setSize(this.panelSize.width, this.panelSize.height);
38457         }
38458         if(this.closeBtn){
38459             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38460         }
38461         this.updateTitle(panel.getTitle());
38462         if(this.tabs){
38463             this.fireEvent("invalidated", this);
38464         }
38465         this.fireEvent("panelactivated", this, panel);
38466     },
38467
38468     /**
38469      * Shows the specified panel.
38470      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38471      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38472      */
38473     showPanel : function(panel)
38474     {
38475         panel = this.getPanel(panel);
38476         if(panel){
38477             if(this.tabs){
38478                 var tab = this.tabs.getTab(panel.getEl().id);
38479                 if(tab.isHidden()){
38480                     this.tabs.unhideTab(tab.id);
38481                 }
38482                 tab.activate();
38483             }else{
38484                 this.setActivePanel(panel);
38485             }
38486         }
38487         return panel;
38488     },
38489
38490     /**
38491      * Get the active panel for this region.
38492      * @return {Roo.ContentPanel} The active panel or null
38493      */
38494     getActivePanel : function(){
38495         return this.activePanel;
38496     },
38497
38498     validateVisibility : function(){
38499         if(this.panels.getCount() < 1){
38500             this.updateTitle("&#160;");
38501             this.closeBtn.hide();
38502             this.hide();
38503         }else{
38504             if(!this.isVisible()){
38505                 this.show();
38506             }
38507         }
38508     },
38509
38510     /**
38511      * Adds the passed ContentPanel(s) to this region.
38512      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38513      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38514      */
38515     add : function(panel)
38516     {
38517         if(arguments.length > 1){
38518             for(var i = 0, len = arguments.length; i < len; i++) {
38519                 this.add(arguments[i]);
38520             }
38521             return null;
38522         }
38523         
38524         // if we have not been rendered yet, then we can not really do much of this..
38525         if (!this.bodyEl) {
38526             this.unrendered_panels.push(panel);
38527             return panel;
38528         }
38529         
38530         
38531         
38532         
38533         if(this.hasPanel(panel)){
38534             this.showPanel(panel);
38535             return panel;
38536         }
38537         panel.setRegion(this);
38538         this.panels.add(panel);
38539        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38540             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38541             // and hide them... ???
38542             this.bodyEl.dom.appendChild(panel.getEl().dom);
38543             if(panel.background !== true){
38544                 this.setActivePanel(panel);
38545             }
38546             this.fireEvent("paneladded", this, panel);
38547             return panel;
38548         }
38549         */
38550         if(!this.tabs){
38551             this.initTabs();
38552         }else{
38553             this.initPanelAsTab(panel);
38554         }
38555         
38556         
38557         if(panel.background !== true){
38558             this.tabs.activate(panel.getEl().id);
38559         }
38560         this.fireEvent("paneladded", this, panel);
38561         return panel;
38562     },
38563
38564     /**
38565      * Hides the tab for the specified panel.
38566      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38567      */
38568     hidePanel : function(panel){
38569         if(this.tabs && (panel = this.getPanel(panel))){
38570             this.tabs.hideTab(panel.getEl().id);
38571         }
38572     },
38573
38574     /**
38575      * Unhides the tab for a previously hidden panel.
38576      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38577      */
38578     unhidePanel : function(panel){
38579         if(this.tabs && (panel = this.getPanel(panel))){
38580             this.tabs.unhideTab(panel.getEl().id);
38581         }
38582     },
38583
38584     clearPanels : function(){
38585         while(this.panels.getCount() > 0){
38586              this.remove(this.panels.first());
38587         }
38588     },
38589
38590     /**
38591      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38593      * @param {Boolean} preservePanel Overrides the config preservePanel option
38594      * @return {Roo.ContentPanel} The panel that was removed
38595      */
38596     remove : function(panel, preservePanel)
38597     {
38598         panel = this.getPanel(panel);
38599         if(!panel){
38600             return null;
38601         }
38602         var e = {};
38603         this.fireEvent("beforeremove", this, panel, e);
38604         if(e.cancel === true){
38605             return null;
38606         }
38607         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38608         var panelId = panel.getId();
38609         this.panels.removeKey(panelId);
38610         if(preservePanel){
38611             document.body.appendChild(panel.getEl().dom);
38612         }
38613         if(this.tabs){
38614             this.tabs.removeTab(panel.getEl().id);
38615         }else if (!preservePanel){
38616             this.bodyEl.dom.removeChild(panel.getEl().dom);
38617         }
38618         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38619             var p = this.panels.first();
38620             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38621             tempEl.appendChild(p.getEl().dom);
38622             this.bodyEl.update("");
38623             this.bodyEl.dom.appendChild(p.getEl().dom);
38624             tempEl = null;
38625             this.updateTitle(p.getTitle());
38626             this.tabs = null;
38627             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38628             this.setActivePanel(p);
38629         }
38630         panel.setRegion(null);
38631         if(this.activePanel == panel){
38632             this.activePanel = null;
38633         }
38634         if(this.config.autoDestroy !== false && preservePanel !== true){
38635             try{panel.destroy();}catch(e){}
38636         }
38637         this.fireEvent("panelremoved", this, panel);
38638         return panel;
38639     },
38640
38641     /**
38642      * Returns the TabPanel component used by this region
38643      * @return {Roo.TabPanel}
38644      */
38645     getTabs : function(){
38646         return this.tabs;
38647     },
38648
38649     createTool : function(parentEl, className){
38650         var btn = Roo.DomHelper.append(parentEl, {
38651             tag: "div",
38652             cls: "x-layout-tools-button",
38653             children: [ {
38654                 tag: "div",
38655                 cls: "roo-layout-tools-button-inner " + className,
38656                 html: "&#160;"
38657             }]
38658         }, true);
38659         btn.addClassOnOver("roo-layout-tools-button-over");
38660         return btn;
38661     }
38662 });/*
38663  * Based on:
38664  * Ext JS Library 1.1.1
38665  * Copyright(c) 2006-2007, Ext JS, LLC.
38666  *
38667  * Originally Released Under LGPL - original licence link has changed is not relivant.
38668  *
38669  * Fork - LGPL
38670  * <script type="text/javascript">
38671  */
38672  
38673
38674
38675 /**
38676  * @class Roo.SplitLayoutRegion
38677  * @extends Roo.LayoutRegion
38678  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38679  */
38680 Roo.bootstrap.layout.Split = function(config){
38681     this.cursor = config.cursor;
38682     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38683 };
38684
38685 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38686 {
38687     splitTip : "Drag to resize.",
38688     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38689     useSplitTips : false,
38690
38691     applyConfig : function(config){
38692         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38693     },
38694     
38695     onRender : function(ctr,pos) {
38696         
38697         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38698         if(!this.config.split){
38699             return;
38700         }
38701         if(!this.split){
38702             
38703             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38704                             tag: "div",
38705                             id: this.el.id + "-split",
38706                             cls: "roo-layout-split roo-layout-split-"+this.position,
38707                             html: "&#160;"
38708             });
38709             /** The SplitBar for this region 
38710             * @type Roo.SplitBar */
38711             // does not exist yet...
38712             Roo.log([this.position, this.orientation]);
38713             
38714             this.split = new Roo.bootstrap.SplitBar({
38715                 dragElement : splitEl,
38716                 resizingElement: this.el,
38717                 orientation : this.orientation
38718             });
38719             
38720             this.split.on("moved", this.onSplitMove, this);
38721             this.split.useShim = this.config.useShim === true;
38722             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38723             if(this.useSplitTips){
38724                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38725             }
38726             //if(config.collapsible){
38727             //    this.split.el.on("dblclick", this.collapse,  this);
38728             //}
38729         }
38730         if(typeof this.config.minSize != "undefined"){
38731             this.split.minSize = this.config.minSize;
38732         }
38733         if(typeof this.config.maxSize != "undefined"){
38734             this.split.maxSize = this.config.maxSize;
38735         }
38736         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38737             this.hideSplitter();
38738         }
38739         
38740     },
38741
38742     getHMaxSize : function(){
38743          var cmax = this.config.maxSize || 10000;
38744          var center = this.mgr.getRegion("center");
38745          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38746     },
38747
38748     getVMaxSize : function(){
38749          var cmax = this.config.maxSize || 10000;
38750          var center = this.mgr.getRegion("center");
38751          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38752     },
38753
38754     onSplitMove : function(split, newSize){
38755         this.fireEvent("resized", this, newSize);
38756     },
38757     
38758     /** 
38759      * Returns the {@link Roo.SplitBar} for this region.
38760      * @return {Roo.SplitBar}
38761      */
38762     getSplitBar : function(){
38763         return this.split;
38764     },
38765     
38766     hide : function(){
38767         this.hideSplitter();
38768         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38769     },
38770
38771     hideSplitter : function(){
38772         if(this.split){
38773             this.split.el.setLocation(-2000,-2000);
38774             this.split.el.hide();
38775         }
38776     },
38777
38778     show : function(){
38779         if(this.split){
38780             this.split.el.show();
38781         }
38782         Roo.bootstrap.layout.Split.superclass.show.call(this);
38783     },
38784     
38785     beforeSlide: function(){
38786         if(Roo.isGecko){// firefox overflow auto bug workaround
38787             this.bodyEl.clip();
38788             if(this.tabs) {
38789                 this.tabs.bodyEl.clip();
38790             }
38791             if(this.activePanel){
38792                 this.activePanel.getEl().clip();
38793                 
38794                 if(this.activePanel.beforeSlide){
38795                     this.activePanel.beforeSlide();
38796                 }
38797             }
38798         }
38799     },
38800     
38801     afterSlide : function(){
38802         if(Roo.isGecko){// firefox overflow auto bug workaround
38803             this.bodyEl.unclip();
38804             if(this.tabs) {
38805                 this.tabs.bodyEl.unclip();
38806             }
38807             if(this.activePanel){
38808                 this.activePanel.getEl().unclip();
38809                 if(this.activePanel.afterSlide){
38810                     this.activePanel.afterSlide();
38811                 }
38812             }
38813         }
38814     },
38815
38816     initAutoHide : function(){
38817         if(this.autoHide !== false){
38818             if(!this.autoHideHd){
38819                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38820                 this.autoHideHd = {
38821                     "mouseout": function(e){
38822                         if(!e.within(this.el, true)){
38823                             st.delay(500);
38824                         }
38825                     },
38826                     "mouseover" : function(e){
38827                         st.cancel();
38828                     },
38829                     scope : this
38830                 };
38831             }
38832             this.el.on(this.autoHideHd);
38833         }
38834     },
38835
38836     clearAutoHide : function(){
38837         if(this.autoHide !== false){
38838             this.el.un("mouseout", this.autoHideHd.mouseout);
38839             this.el.un("mouseover", this.autoHideHd.mouseover);
38840         }
38841     },
38842
38843     clearMonitor : function(){
38844         Roo.get(document).un("click", this.slideInIf, this);
38845     },
38846
38847     // these names are backwards but not changed for compat
38848     slideOut : function(){
38849         if(this.isSlid || this.el.hasActiveFx()){
38850             return;
38851         }
38852         this.isSlid = true;
38853         if(this.collapseBtn){
38854             this.collapseBtn.hide();
38855         }
38856         this.closeBtnState = this.closeBtn.getStyle('display');
38857         this.closeBtn.hide();
38858         if(this.stickBtn){
38859             this.stickBtn.show();
38860         }
38861         this.el.show();
38862         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38863         this.beforeSlide();
38864         this.el.setStyle("z-index", 10001);
38865         this.el.slideIn(this.getSlideAnchor(), {
38866             callback: function(){
38867                 this.afterSlide();
38868                 this.initAutoHide();
38869                 Roo.get(document).on("click", this.slideInIf, this);
38870                 this.fireEvent("slideshow", this);
38871             },
38872             scope: this,
38873             block: true
38874         });
38875     },
38876
38877     afterSlideIn : function(){
38878         this.clearAutoHide();
38879         this.isSlid = false;
38880         this.clearMonitor();
38881         this.el.setStyle("z-index", "");
38882         if(this.collapseBtn){
38883             this.collapseBtn.show();
38884         }
38885         this.closeBtn.setStyle('display', this.closeBtnState);
38886         if(this.stickBtn){
38887             this.stickBtn.hide();
38888         }
38889         this.fireEvent("slidehide", this);
38890     },
38891
38892     slideIn : function(cb){
38893         if(!this.isSlid || this.el.hasActiveFx()){
38894             Roo.callback(cb);
38895             return;
38896         }
38897         this.isSlid = false;
38898         this.beforeSlide();
38899         this.el.slideOut(this.getSlideAnchor(), {
38900             callback: function(){
38901                 this.el.setLeftTop(-10000, -10000);
38902                 this.afterSlide();
38903                 this.afterSlideIn();
38904                 Roo.callback(cb);
38905             },
38906             scope: this,
38907             block: true
38908         });
38909     },
38910     
38911     slideInIf : function(e){
38912         if(!e.within(this.el)){
38913             this.slideIn();
38914         }
38915     },
38916
38917     animateCollapse : function(){
38918         this.beforeSlide();
38919         this.el.setStyle("z-index", 20000);
38920         var anchor = this.getSlideAnchor();
38921         this.el.slideOut(anchor, {
38922             callback : function(){
38923                 this.el.setStyle("z-index", "");
38924                 this.collapsedEl.slideIn(anchor, {duration:.3});
38925                 this.afterSlide();
38926                 this.el.setLocation(-10000,-10000);
38927                 this.el.hide();
38928                 this.fireEvent("collapsed", this);
38929             },
38930             scope: this,
38931             block: true
38932         });
38933     },
38934
38935     animateExpand : function(){
38936         this.beforeSlide();
38937         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38938         this.el.setStyle("z-index", 20000);
38939         this.collapsedEl.hide({
38940             duration:.1
38941         });
38942         this.el.slideIn(this.getSlideAnchor(), {
38943             callback : function(){
38944                 this.el.setStyle("z-index", "");
38945                 this.afterSlide();
38946                 if(this.split){
38947                     this.split.el.show();
38948                 }
38949                 this.fireEvent("invalidated", this);
38950                 this.fireEvent("expanded", this);
38951             },
38952             scope: this,
38953             block: true
38954         });
38955     },
38956
38957     anchors : {
38958         "west" : "left",
38959         "east" : "right",
38960         "north" : "top",
38961         "south" : "bottom"
38962     },
38963
38964     sanchors : {
38965         "west" : "l",
38966         "east" : "r",
38967         "north" : "t",
38968         "south" : "b"
38969     },
38970
38971     canchors : {
38972         "west" : "tl-tr",
38973         "east" : "tr-tl",
38974         "north" : "tl-bl",
38975         "south" : "bl-tl"
38976     },
38977
38978     getAnchor : function(){
38979         return this.anchors[this.position];
38980     },
38981
38982     getCollapseAnchor : function(){
38983         return this.canchors[this.position];
38984     },
38985
38986     getSlideAnchor : function(){
38987         return this.sanchors[this.position];
38988     },
38989
38990     getAlignAdj : function(){
38991         var cm = this.cmargins;
38992         switch(this.position){
38993             case "west":
38994                 return [0, 0];
38995             break;
38996             case "east":
38997                 return [0, 0];
38998             break;
38999             case "north":
39000                 return [0, 0];
39001             break;
39002             case "south":
39003                 return [0, 0];
39004             break;
39005         }
39006     },
39007
39008     getExpandAdj : function(){
39009         var c = this.collapsedEl, cm = this.cmargins;
39010         switch(this.position){
39011             case "west":
39012                 return [-(cm.right+c.getWidth()+cm.left), 0];
39013             break;
39014             case "east":
39015                 return [cm.right+c.getWidth()+cm.left, 0];
39016             break;
39017             case "north":
39018                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39019             break;
39020             case "south":
39021                 return [0, cm.top+cm.bottom+c.getHeight()];
39022             break;
39023         }
39024     }
39025 });/*
39026  * Based on:
39027  * Ext JS Library 1.1.1
39028  * Copyright(c) 2006-2007, Ext JS, LLC.
39029  *
39030  * Originally Released Under LGPL - original licence link has changed is not relivant.
39031  *
39032  * Fork - LGPL
39033  * <script type="text/javascript">
39034  */
39035 /*
39036  * These classes are private internal classes
39037  */
39038 Roo.bootstrap.layout.Center = function(config){
39039     config.region = "center";
39040     Roo.bootstrap.layout.Region.call(this, config);
39041     this.visible = true;
39042     this.minWidth = config.minWidth || 20;
39043     this.minHeight = config.minHeight || 20;
39044 };
39045
39046 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39047     hide : function(){
39048         // center panel can't be hidden
39049     },
39050     
39051     show : function(){
39052         // center panel can't be hidden
39053     },
39054     
39055     getMinWidth: function(){
39056         return this.minWidth;
39057     },
39058     
39059     getMinHeight: function(){
39060         return this.minHeight;
39061     }
39062 });
39063
39064
39065
39066
39067  
39068
39069
39070
39071
39072
39073
39074 Roo.bootstrap.layout.North = function(config)
39075 {
39076     config.region = 'north';
39077     config.cursor = 'n-resize';
39078     
39079     Roo.bootstrap.layout.Split.call(this, config);
39080     
39081     
39082     if(this.split){
39083         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39084         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39085         this.split.el.addClass("roo-layout-split-v");
39086     }
39087     var size = config.initialSize || config.height;
39088     if(typeof size != "undefined"){
39089         this.el.setHeight(size);
39090     }
39091 };
39092 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39093 {
39094     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39095     
39096     
39097     
39098     getBox : function(){
39099         if(this.collapsed){
39100             return this.collapsedEl.getBox();
39101         }
39102         var box = this.el.getBox();
39103         if(this.split){
39104             box.height += this.split.el.getHeight();
39105         }
39106         return box;
39107     },
39108     
39109     updateBox : function(box){
39110         if(this.split && !this.collapsed){
39111             box.height -= this.split.el.getHeight();
39112             this.split.el.setLeft(box.x);
39113             this.split.el.setTop(box.y+box.height);
39114             this.split.el.setWidth(box.width);
39115         }
39116         if(this.collapsed){
39117             this.updateBody(box.width, null);
39118         }
39119         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39120     }
39121 });
39122
39123
39124
39125
39126
39127 Roo.bootstrap.layout.South = function(config){
39128     config.region = 'south';
39129     config.cursor = 's-resize';
39130     Roo.bootstrap.layout.Split.call(this, config);
39131     if(this.split){
39132         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39133         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39134         this.split.el.addClass("roo-layout-split-v");
39135     }
39136     var size = config.initialSize || config.height;
39137     if(typeof size != "undefined"){
39138         this.el.setHeight(size);
39139     }
39140 };
39141
39142 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39143     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39144     getBox : function(){
39145         if(this.collapsed){
39146             return this.collapsedEl.getBox();
39147         }
39148         var box = this.el.getBox();
39149         if(this.split){
39150             var sh = this.split.el.getHeight();
39151             box.height += sh;
39152             box.y -= sh;
39153         }
39154         return box;
39155     },
39156     
39157     updateBox : function(box){
39158         if(this.split && !this.collapsed){
39159             var sh = this.split.el.getHeight();
39160             box.height -= sh;
39161             box.y += sh;
39162             this.split.el.setLeft(box.x);
39163             this.split.el.setTop(box.y-sh);
39164             this.split.el.setWidth(box.width);
39165         }
39166         if(this.collapsed){
39167             this.updateBody(box.width, null);
39168         }
39169         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39170     }
39171 });
39172
39173 Roo.bootstrap.layout.East = function(config){
39174     config.region = "east";
39175     config.cursor = "e-resize";
39176     Roo.bootstrap.layout.Split.call(this, config);
39177     if(this.split){
39178         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39179         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39180         this.split.el.addClass("roo-layout-split-h");
39181     }
39182     var size = config.initialSize || config.width;
39183     if(typeof size != "undefined"){
39184         this.el.setWidth(size);
39185     }
39186 };
39187 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39188     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39189     getBox : function(){
39190         if(this.collapsed){
39191             return this.collapsedEl.getBox();
39192         }
39193         var box = this.el.getBox();
39194         if(this.split){
39195             var sw = this.split.el.getWidth();
39196             box.width += sw;
39197             box.x -= sw;
39198         }
39199         return box;
39200     },
39201
39202     updateBox : function(box){
39203         if(this.split && !this.collapsed){
39204             var sw = this.split.el.getWidth();
39205             box.width -= sw;
39206             this.split.el.setLeft(box.x);
39207             this.split.el.setTop(box.y);
39208             this.split.el.setHeight(box.height);
39209             box.x += sw;
39210         }
39211         if(this.collapsed){
39212             this.updateBody(null, box.height);
39213         }
39214         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39215     }
39216 });
39217
39218 Roo.bootstrap.layout.West = function(config){
39219     config.region = "west";
39220     config.cursor = "w-resize";
39221     
39222     Roo.bootstrap.layout.Split.call(this, config);
39223     if(this.split){
39224         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39225         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39226         this.split.el.addClass("roo-layout-split-h");
39227     }
39228     
39229 };
39230 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39231     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39232     
39233     onRender: function(ctr, pos)
39234     {
39235         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39236         var size = this.config.initialSize || this.config.width;
39237         if(typeof size != "undefined"){
39238             this.el.setWidth(size);
39239         }
39240     },
39241     
39242     getBox : function(){
39243         if(this.collapsed){
39244             return this.collapsedEl.getBox();
39245         }
39246         var box = this.el.getBox();
39247         if(this.split){
39248             box.width += this.split.el.getWidth();
39249         }
39250         return box;
39251     },
39252     
39253     updateBox : function(box){
39254         if(this.split && !this.collapsed){
39255             var sw = this.split.el.getWidth();
39256             box.width -= sw;
39257             this.split.el.setLeft(box.x+box.width);
39258             this.split.el.setTop(box.y);
39259             this.split.el.setHeight(box.height);
39260         }
39261         if(this.collapsed){
39262             this.updateBody(null, box.height);
39263         }
39264         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39265     }
39266 });Roo.namespace("Roo.bootstrap.panel");/*
39267  * Based on:
39268  * Ext JS Library 1.1.1
39269  * Copyright(c) 2006-2007, Ext JS, LLC.
39270  *
39271  * Originally Released Under LGPL - original licence link has changed is not relivant.
39272  *
39273  * Fork - LGPL
39274  * <script type="text/javascript">
39275  */
39276 /**
39277  * @class Roo.ContentPanel
39278  * @extends Roo.util.Observable
39279  * A basic ContentPanel element.
39280  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39281  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39282  * @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
39283  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39284  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39285  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39286  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39287  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39288  * @cfg {String} title          The title for this panel
39289  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39290  * @cfg {String} url            Calls {@link #setUrl} with this value
39291  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39292  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39293  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39294  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39295  * @cfg {Boolean} badges render the badges
39296  * @cfg {String} cls  extra classes to use  
39297  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39298
39299  * @constructor
39300  * Create a new ContentPanel.
39301  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39302  * @param {String/Object} config A string to set only the title or a config object
39303  * @param {String} content (optional) Set the HTML content for this panel
39304  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39305  */
39306 Roo.bootstrap.panel.Content = function( config){
39307     
39308     this.tpl = config.tpl || false;
39309     
39310     var el = config.el;
39311     var content = config.content;
39312
39313     if(config.autoCreate){ // xtype is available if this is called from factory
39314         el = Roo.id();
39315     }
39316     this.el = Roo.get(el);
39317     if(!this.el && config && config.autoCreate){
39318         if(typeof config.autoCreate == "object"){
39319             if(!config.autoCreate.id){
39320                 config.autoCreate.id = config.id||el;
39321             }
39322             this.el = Roo.DomHelper.append(document.body,
39323                         config.autoCreate, true);
39324         }else{
39325             var elcfg =  {
39326                 tag: "div",
39327                 cls: (config.cls || '') +
39328                     (config.background ? ' bg-' + config.background : '') +
39329                     " roo-layout-inactive-content",
39330                 id: config.id||el
39331             };
39332             if (config.html) {
39333                 elcfg.html = config.html;
39334                 
39335             }
39336                         
39337             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39338         }
39339     } 
39340     this.closable = false;
39341     this.loaded = false;
39342     this.active = false;
39343    
39344       
39345     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39346         
39347         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39348         
39349         this.wrapEl = this.el; //this.el.wrap();
39350         var ti = [];
39351         if (config.toolbar.items) {
39352             ti = config.toolbar.items ;
39353             delete config.toolbar.items ;
39354         }
39355         
39356         var nitems = [];
39357         this.toolbar.render(this.wrapEl, 'before');
39358         for(var i =0;i < ti.length;i++) {
39359           //  Roo.log(['add child', items[i]]);
39360             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39361         }
39362         this.toolbar.items = nitems;
39363         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39364         delete config.toolbar;
39365         
39366     }
39367     /*
39368     // xtype created footer. - not sure if will work as we normally have to render first..
39369     if (this.footer && !this.footer.el && this.footer.xtype) {
39370         if (!this.wrapEl) {
39371             this.wrapEl = this.el.wrap();
39372         }
39373     
39374         this.footer.container = this.wrapEl.createChild();
39375          
39376         this.footer = Roo.factory(this.footer, Roo);
39377         
39378     }
39379     */
39380     
39381      if(typeof config == "string"){
39382         this.title = config;
39383     }else{
39384         Roo.apply(this, config);
39385     }
39386     
39387     if(this.resizeEl){
39388         this.resizeEl = Roo.get(this.resizeEl, true);
39389     }else{
39390         this.resizeEl = this.el;
39391     }
39392     // handle view.xtype
39393     
39394  
39395     
39396     
39397     this.addEvents({
39398         /**
39399          * @event activate
39400          * Fires when this panel is activated. 
39401          * @param {Roo.ContentPanel} this
39402          */
39403         "activate" : true,
39404         /**
39405          * @event deactivate
39406          * Fires when this panel is activated. 
39407          * @param {Roo.ContentPanel} this
39408          */
39409         "deactivate" : true,
39410
39411         /**
39412          * @event resize
39413          * Fires when this panel is resized if fitToFrame is true.
39414          * @param {Roo.ContentPanel} this
39415          * @param {Number} width The width after any component adjustments
39416          * @param {Number} height The height after any component adjustments
39417          */
39418         "resize" : true,
39419         
39420          /**
39421          * @event render
39422          * Fires when this tab is created
39423          * @param {Roo.ContentPanel} this
39424          */
39425         "render" : true
39426         
39427         
39428         
39429     });
39430     
39431
39432     
39433     
39434     if(this.autoScroll){
39435         this.resizeEl.setStyle("overflow", "auto");
39436     } else {
39437         // fix randome scrolling
39438         //this.el.on('scroll', function() {
39439         //    Roo.log('fix random scolling');
39440         //    this.scrollTo('top',0); 
39441         //});
39442     }
39443     content = content || this.content;
39444     if(content){
39445         this.setContent(content);
39446     }
39447     if(config && config.url){
39448         this.setUrl(this.url, this.params, this.loadOnce);
39449     }
39450     
39451     
39452     
39453     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39454     
39455     if (this.view && typeof(this.view.xtype) != 'undefined') {
39456         this.view.el = this.el.appendChild(document.createElement("div"));
39457         this.view = Roo.factory(this.view); 
39458         this.view.render  &&  this.view.render(false, '');  
39459     }
39460     
39461     
39462     this.fireEvent('render', this);
39463 };
39464
39465 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39466     
39467     cls : '',
39468     background : '',
39469     
39470     tabTip : '',
39471     
39472     setRegion : function(region){
39473         this.region = region;
39474         this.setActiveClass(region && !this.background);
39475     },
39476     
39477     
39478     setActiveClass: function(state)
39479     {
39480         if(state){
39481            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39482            this.el.setStyle('position','relative');
39483         }else{
39484            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39485            this.el.setStyle('position', 'absolute');
39486         } 
39487     },
39488     
39489     /**
39490      * Returns the toolbar for this Panel if one was configured. 
39491      * @return {Roo.Toolbar} 
39492      */
39493     getToolbar : function(){
39494         return this.toolbar;
39495     },
39496     
39497     setActiveState : function(active)
39498     {
39499         this.active = active;
39500         this.setActiveClass(active);
39501         if(!active){
39502             if(this.fireEvent("deactivate", this) === false){
39503                 return false;
39504             }
39505             return true;
39506         }
39507         this.fireEvent("activate", this);
39508         return true;
39509     },
39510     /**
39511      * Updates this panel's element
39512      * @param {String} content The new content
39513      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39514     */
39515     setContent : function(content, loadScripts){
39516         this.el.update(content, loadScripts);
39517     },
39518
39519     ignoreResize : function(w, h){
39520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39521             return true;
39522         }else{
39523             this.lastSize = {width: w, height: h};
39524             return false;
39525         }
39526     },
39527     /**
39528      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39529      * @return {Roo.UpdateManager} The UpdateManager
39530      */
39531     getUpdateManager : function(){
39532         return this.el.getUpdateManager();
39533     },
39534      /**
39535      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39536      * @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:
39537 <pre><code>
39538 panel.load({
39539     url: "your-url.php",
39540     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39541     callback: yourFunction,
39542     scope: yourObject, //(optional scope)
39543     discardUrl: false,
39544     nocache: false,
39545     text: "Loading...",
39546     timeout: 30,
39547     scripts: false
39548 });
39549 </code></pre>
39550      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39551      * 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.
39552      * @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}
39553      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39554      * @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.
39555      * @return {Roo.ContentPanel} this
39556      */
39557     load : function(){
39558         var um = this.el.getUpdateManager();
39559         um.update.apply(um, arguments);
39560         return this;
39561     },
39562
39563
39564     /**
39565      * 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.
39566      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39567      * @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)
39568      * @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)
39569      * @return {Roo.UpdateManager} The UpdateManager
39570      */
39571     setUrl : function(url, params, loadOnce){
39572         if(this.refreshDelegate){
39573             this.removeListener("activate", this.refreshDelegate);
39574         }
39575         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39576         this.on("activate", this.refreshDelegate);
39577         return this.el.getUpdateManager();
39578     },
39579     
39580     _handleRefresh : function(url, params, loadOnce){
39581         if(!loadOnce || !this.loaded){
39582             var updater = this.el.getUpdateManager();
39583             updater.update(url, params, this._setLoaded.createDelegate(this));
39584         }
39585     },
39586     
39587     _setLoaded : function(){
39588         this.loaded = true;
39589     }, 
39590     
39591     /**
39592      * Returns this panel's id
39593      * @return {String} 
39594      */
39595     getId : function(){
39596         return this.el.id;
39597     },
39598     
39599     /** 
39600      * Returns this panel's element - used by regiosn to add.
39601      * @return {Roo.Element} 
39602      */
39603     getEl : function(){
39604         return this.wrapEl || this.el;
39605     },
39606     
39607    
39608     
39609     adjustForComponents : function(width, height)
39610     {
39611         //Roo.log('adjustForComponents ');
39612         if(this.resizeEl != this.el){
39613             width -= this.el.getFrameWidth('lr');
39614             height -= this.el.getFrameWidth('tb');
39615         }
39616         if(this.toolbar){
39617             var te = this.toolbar.getEl();
39618             te.setWidth(width);
39619             height -= te.getHeight();
39620         }
39621         if(this.footer){
39622             var te = this.footer.getEl();
39623             te.setWidth(width);
39624             height -= te.getHeight();
39625         }
39626         
39627         
39628         if(this.adjustments){
39629             width += this.adjustments[0];
39630             height += this.adjustments[1];
39631         }
39632         return {"width": width, "height": height};
39633     },
39634     
39635     setSize : function(width, height){
39636         if(this.fitToFrame && !this.ignoreResize(width, height)){
39637             if(this.fitContainer && this.resizeEl != this.el){
39638                 this.el.setSize(width, height);
39639             }
39640             var size = this.adjustForComponents(width, height);
39641             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39642             this.fireEvent('resize', this, size.width, size.height);
39643         }
39644     },
39645     
39646     /**
39647      * Returns this panel's title
39648      * @return {String} 
39649      */
39650     getTitle : function(){
39651         
39652         if (typeof(this.title) != 'object') {
39653             return this.title;
39654         }
39655         
39656         var t = '';
39657         for (var k in this.title) {
39658             if (!this.title.hasOwnProperty(k)) {
39659                 continue;
39660             }
39661             
39662             if (k.indexOf('-') >= 0) {
39663                 var s = k.split('-');
39664                 for (var i = 0; i<s.length; i++) {
39665                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39666                 }
39667             } else {
39668                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39669             }
39670         }
39671         return t;
39672     },
39673     
39674     /**
39675      * Set this panel's title
39676      * @param {String} title
39677      */
39678     setTitle : function(title){
39679         this.title = title;
39680         if(this.region){
39681             this.region.updatePanelTitle(this, title);
39682         }
39683     },
39684     
39685     /**
39686      * Returns true is this panel was configured to be closable
39687      * @return {Boolean} 
39688      */
39689     isClosable : function(){
39690         return this.closable;
39691     },
39692     
39693     beforeSlide : function(){
39694         this.el.clip();
39695         this.resizeEl.clip();
39696     },
39697     
39698     afterSlide : function(){
39699         this.el.unclip();
39700         this.resizeEl.unclip();
39701     },
39702     
39703     /**
39704      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39705      *   Will fail silently if the {@link #setUrl} method has not been called.
39706      *   This does not activate the panel, just updates its content.
39707      */
39708     refresh : function(){
39709         if(this.refreshDelegate){
39710            this.loaded = false;
39711            this.refreshDelegate();
39712         }
39713     },
39714     
39715     /**
39716      * Destroys this panel
39717      */
39718     destroy : function(){
39719         this.el.removeAllListeners();
39720         var tempEl = document.createElement("span");
39721         tempEl.appendChild(this.el.dom);
39722         tempEl.innerHTML = "";
39723         this.el.remove();
39724         this.el = null;
39725     },
39726     
39727     /**
39728      * form - if the content panel contains a form - this is a reference to it.
39729      * @type {Roo.form.Form}
39730      */
39731     form : false,
39732     /**
39733      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39734      *    This contains a reference to it.
39735      * @type {Roo.View}
39736      */
39737     view : false,
39738     
39739       /**
39740      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39741      * <pre><code>
39742
39743 layout.addxtype({
39744        xtype : 'Form',
39745        items: [ .... ]
39746    }
39747 );
39748
39749 </code></pre>
39750      * @param {Object} cfg Xtype definition of item to add.
39751      */
39752     
39753     
39754     getChildContainer: function () {
39755         return this.getEl();
39756     }
39757     
39758     
39759     /*
39760         var  ret = new Roo.factory(cfg);
39761         return ret;
39762         
39763         
39764         // add form..
39765         if (cfg.xtype.match(/^Form$/)) {
39766             
39767             var el;
39768             //if (this.footer) {
39769             //    el = this.footer.container.insertSibling(false, 'before');
39770             //} else {
39771                 el = this.el.createChild();
39772             //}
39773
39774             this.form = new  Roo.form.Form(cfg);
39775             
39776             
39777             if ( this.form.allItems.length) {
39778                 this.form.render(el.dom);
39779             }
39780             return this.form;
39781         }
39782         // should only have one of theses..
39783         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39784             // views.. should not be just added - used named prop 'view''
39785             
39786             cfg.el = this.el.appendChild(document.createElement("div"));
39787             // factory?
39788             
39789             var ret = new Roo.factory(cfg);
39790              
39791              ret.render && ret.render(false, ''); // render blank..
39792             this.view = ret;
39793             return ret;
39794         }
39795         return false;
39796     }
39797     \*/
39798 });
39799  
39800 /**
39801  * @class Roo.bootstrap.panel.Grid
39802  * @extends Roo.bootstrap.panel.Content
39803  * @constructor
39804  * Create a new GridPanel.
39805  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39806  * @param {Object} config A the config object
39807   
39808  */
39809
39810
39811
39812 Roo.bootstrap.panel.Grid = function(config)
39813 {
39814     
39815       
39816     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39817         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39818
39819     config.el = this.wrapper;
39820     //this.el = this.wrapper;
39821     
39822       if (config.container) {
39823         // ctor'ed from a Border/panel.grid
39824         
39825         
39826         this.wrapper.setStyle("overflow", "hidden");
39827         this.wrapper.addClass('roo-grid-container');
39828
39829     }
39830     
39831     
39832     if(config.toolbar){
39833         var tool_el = this.wrapper.createChild();    
39834         this.toolbar = Roo.factory(config.toolbar);
39835         var ti = [];
39836         if (config.toolbar.items) {
39837             ti = config.toolbar.items ;
39838             delete config.toolbar.items ;
39839         }
39840         
39841         var nitems = [];
39842         this.toolbar.render(tool_el);
39843         for(var i =0;i < ti.length;i++) {
39844           //  Roo.log(['add child', items[i]]);
39845             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39846         }
39847         this.toolbar.items = nitems;
39848         
39849         delete config.toolbar;
39850     }
39851     
39852     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39853     config.grid.scrollBody = true;;
39854     config.grid.monitorWindowResize = false; // turn off autosizing
39855     config.grid.autoHeight = false;
39856     config.grid.autoWidth = false;
39857     
39858     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39859     
39860     if (config.background) {
39861         // render grid on panel activation (if panel background)
39862         this.on('activate', function(gp) {
39863             if (!gp.grid.rendered) {
39864                 gp.grid.render(this.wrapper);
39865                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39866             }
39867         });
39868             
39869     } else {
39870         this.grid.render(this.wrapper);
39871         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39872
39873     }
39874     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39875     // ??? needed ??? config.el = this.wrapper;
39876     
39877     
39878     
39879   
39880     // xtype created footer. - not sure if will work as we normally have to render first..
39881     if (this.footer && !this.footer.el && this.footer.xtype) {
39882         
39883         var ctr = this.grid.getView().getFooterPanel(true);
39884         this.footer.dataSource = this.grid.dataSource;
39885         this.footer = Roo.factory(this.footer, Roo);
39886         this.footer.render(ctr);
39887         
39888     }
39889     
39890     
39891     
39892     
39893      
39894 };
39895
39896 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39897     getId : function(){
39898         return this.grid.id;
39899     },
39900     
39901     /**
39902      * Returns the grid for this panel
39903      * @return {Roo.bootstrap.Table} 
39904      */
39905     getGrid : function(){
39906         return this.grid;    
39907     },
39908     
39909     setSize : function(width, height){
39910         if(!this.ignoreResize(width, height)){
39911             var grid = this.grid;
39912             var size = this.adjustForComponents(width, height);
39913             // tfoot is not a footer?
39914           
39915             
39916             var gridel = grid.getGridEl();
39917             gridel.setSize(size.width, size.height);
39918             
39919             var tbd = grid.getGridEl().select('tbody', true).first();
39920             var thd = grid.getGridEl().select('thead',true).first();
39921             var tbf= grid.getGridEl().select('tfoot', true).first();
39922
39923             if (tbf) {
39924                 size.height -= thd.getHeight();
39925             }
39926             if (thd) {
39927                 size.height -= thd.getHeight();
39928             }
39929             
39930             tbd.setSize(size.width, size.height );
39931             // this is for the account management tab -seems to work there.
39932             var thd = grid.getGridEl().select('thead',true).first();
39933             //if (tbd) {
39934             //    tbd.setSize(size.width, size.height - thd.getHeight());
39935             //}
39936              
39937             grid.autoSize();
39938         }
39939     },
39940      
39941     
39942     
39943     beforeSlide : function(){
39944         this.grid.getView().scroller.clip();
39945     },
39946     
39947     afterSlide : function(){
39948         this.grid.getView().scroller.unclip();
39949     },
39950     
39951     destroy : function(){
39952         this.grid.destroy();
39953         delete this.grid;
39954         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39955     }
39956 });
39957
39958 /**
39959  * @class Roo.bootstrap.panel.Nest
39960  * @extends Roo.bootstrap.panel.Content
39961  * @constructor
39962  * Create a new Panel, that can contain a layout.Border.
39963  * 
39964  * 
39965  * @param {Roo.BorderLayout} layout The layout for this panel
39966  * @param {String/Object} config A string to set only the title or a config object
39967  */
39968 Roo.bootstrap.panel.Nest = function(config)
39969 {
39970     // construct with only one argument..
39971     /* FIXME - implement nicer consturctors
39972     if (layout.layout) {
39973         config = layout;
39974         layout = config.layout;
39975         delete config.layout;
39976     }
39977     if (layout.xtype && !layout.getEl) {
39978         // then layout needs constructing..
39979         layout = Roo.factory(layout, Roo);
39980     }
39981     */
39982     
39983     config.el =  config.layout.getEl();
39984     
39985     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39986     
39987     config.layout.monitorWindowResize = false; // turn off autosizing
39988     this.layout = config.layout;
39989     this.layout.getEl().addClass("roo-layout-nested-layout");
39990     this.layout.parent = this;
39991     
39992     
39993     
39994     
39995 };
39996
39997 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39998
39999     setSize : function(width, height){
40000         if(!this.ignoreResize(width, height)){
40001             var size = this.adjustForComponents(width, height);
40002             var el = this.layout.getEl();
40003             if (size.height < 1) {
40004                 el.setWidth(size.width);   
40005             } else {
40006                 el.setSize(size.width, size.height);
40007             }
40008             var touch = el.dom.offsetWidth;
40009             this.layout.layout();
40010             // ie requires a double layout on the first pass
40011             if(Roo.isIE && !this.initialized){
40012                 this.initialized = true;
40013                 this.layout.layout();
40014             }
40015         }
40016     },
40017     
40018     // activate all subpanels if not currently active..
40019     
40020     setActiveState : function(active){
40021         this.active = active;
40022         this.setActiveClass(active);
40023         
40024         if(!active){
40025             this.fireEvent("deactivate", this);
40026             return;
40027         }
40028         
40029         this.fireEvent("activate", this);
40030         // not sure if this should happen before or after..
40031         if (!this.layout) {
40032             return; // should not happen..
40033         }
40034         var reg = false;
40035         for (var r in this.layout.regions) {
40036             reg = this.layout.getRegion(r);
40037             if (reg.getActivePanel()) {
40038                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40039                 reg.setActivePanel(reg.getActivePanel());
40040                 continue;
40041             }
40042             if (!reg.panels.length) {
40043                 continue;
40044             }
40045             reg.showPanel(reg.getPanel(0));
40046         }
40047         
40048         
40049         
40050         
40051     },
40052     
40053     /**
40054      * Returns the nested BorderLayout for this panel
40055      * @return {Roo.BorderLayout} 
40056      */
40057     getLayout : function(){
40058         return this.layout;
40059     },
40060     
40061      /**
40062      * Adds a xtype elements to the layout of the nested panel
40063      * <pre><code>
40064
40065 panel.addxtype({
40066        xtype : 'ContentPanel',
40067        region: 'west',
40068        items: [ .... ]
40069    }
40070 );
40071
40072 panel.addxtype({
40073         xtype : 'NestedLayoutPanel',
40074         region: 'west',
40075         layout: {
40076            center: { },
40077            west: { }   
40078         },
40079         items : [ ... list of content panels or nested layout panels.. ]
40080    }
40081 );
40082 </code></pre>
40083      * @param {Object} cfg Xtype definition of item to add.
40084      */
40085     addxtype : function(cfg) {
40086         return this.layout.addxtype(cfg);
40087     
40088     }
40089 });/*
40090  * Based on:
40091  * Ext JS Library 1.1.1
40092  * Copyright(c) 2006-2007, Ext JS, LLC.
40093  *
40094  * Originally Released Under LGPL - original licence link has changed is not relivant.
40095  *
40096  * Fork - LGPL
40097  * <script type="text/javascript">
40098  */
40099 /**
40100  * @class Roo.TabPanel
40101  * @extends Roo.util.Observable
40102  * A lightweight tab container.
40103  * <br><br>
40104  * Usage:
40105  * <pre><code>
40106 // basic tabs 1, built from existing content
40107 var tabs = new Roo.TabPanel("tabs1");
40108 tabs.addTab("script", "View Script");
40109 tabs.addTab("markup", "View Markup");
40110 tabs.activate("script");
40111
40112 // more advanced tabs, built from javascript
40113 var jtabs = new Roo.TabPanel("jtabs");
40114 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40115
40116 // set up the UpdateManager
40117 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40118 var updater = tab2.getUpdateManager();
40119 updater.setDefaultUrl("ajax1.htm");
40120 tab2.on('activate', updater.refresh, updater, true);
40121
40122 // Use setUrl for Ajax loading
40123 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40124 tab3.setUrl("ajax2.htm", null, true);
40125
40126 // Disabled tab
40127 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40128 tab4.disable();
40129
40130 jtabs.activate("jtabs-1");
40131  * </code></pre>
40132  * @constructor
40133  * Create a new TabPanel.
40134  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40135  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40136  */
40137 Roo.bootstrap.panel.Tabs = function(config){
40138     /**
40139     * The container element for this TabPanel.
40140     * @type Roo.Element
40141     */
40142     this.el = Roo.get(config.el);
40143     delete config.el;
40144     if(config){
40145         if(typeof config == "boolean"){
40146             this.tabPosition = config ? "bottom" : "top";
40147         }else{
40148             Roo.apply(this, config);
40149         }
40150     }
40151     
40152     if(this.tabPosition == "bottom"){
40153         // if tabs are at the bottom = create the body first.
40154         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40155         this.el.addClass("roo-tabs-bottom");
40156     }
40157     // next create the tabs holders
40158     
40159     if (this.tabPosition == "west"){
40160         
40161         var reg = this.region; // fake it..
40162         while (reg) {
40163             if (!reg.mgr.parent) {
40164                 break;
40165             }
40166             reg = reg.mgr.parent.region;
40167         }
40168         Roo.log("got nest?");
40169         Roo.log(reg);
40170         if (reg.mgr.getRegion('west')) {
40171             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40172             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40173             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40174             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40175             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40176         
40177             
40178         }
40179         
40180         
40181     } else {
40182      
40183         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40184         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40185         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40186         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40187     }
40188     
40189     
40190     if(Roo.isIE){
40191         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40192     }
40193     
40194     // finally - if tabs are at the top, then create the body last..
40195     if(this.tabPosition != "bottom"){
40196         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40197          * @type Roo.Element
40198          */
40199         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40200         this.el.addClass("roo-tabs-top");
40201     }
40202     this.items = [];
40203
40204     this.bodyEl.setStyle("position", "relative");
40205
40206     this.active = null;
40207     this.activateDelegate = this.activate.createDelegate(this);
40208
40209     this.addEvents({
40210         /**
40211          * @event tabchange
40212          * Fires when the active tab changes
40213          * @param {Roo.TabPanel} this
40214          * @param {Roo.TabPanelItem} activePanel The new active tab
40215          */
40216         "tabchange": true,
40217         /**
40218          * @event beforetabchange
40219          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40220          * @param {Roo.TabPanel} this
40221          * @param {Object} e Set cancel to true on this object to cancel the tab change
40222          * @param {Roo.TabPanelItem} tab The tab being changed to
40223          */
40224         "beforetabchange" : true
40225     });
40226
40227     Roo.EventManager.onWindowResize(this.onResize, this);
40228     this.cpad = this.el.getPadding("lr");
40229     this.hiddenCount = 0;
40230
40231
40232     // toolbar on the tabbar support...
40233     if (this.toolbar) {
40234         alert("no toolbar support yet");
40235         this.toolbar  = false;
40236         /*
40237         var tcfg = this.toolbar;
40238         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40239         this.toolbar = new Roo.Toolbar(tcfg);
40240         if (Roo.isSafari) {
40241             var tbl = tcfg.container.child('table', true);
40242             tbl.setAttribute('width', '100%');
40243         }
40244         */
40245         
40246     }
40247    
40248
40249
40250     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40251 };
40252
40253 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40254     /*
40255      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40256      */
40257     tabPosition : "top",
40258     /*
40259      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40260      */
40261     currentTabWidth : 0,
40262     /*
40263      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40264      */
40265     minTabWidth : 40,
40266     /*
40267      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40268      */
40269     maxTabWidth : 250,
40270     /*
40271      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40272      */
40273     preferredTabWidth : 175,
40274     /*
40275      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40276      */
40277     resizeTabs : false,
40278     /*
40279      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40280      */
40281     monitorResize : true,
40282     /*
40283      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40284      */
40285     toolbar : false,  // set by caller..
40286     
40287     region : false, /// set by caller
40288     
40289     disableTooltips : true, // not used yet...
40290
40291     /**
40292      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40293      * @param {String} id The id of the div to use <b>or create</b>
40294      * @param {String} text The text for the tab
40295      * @param {String} content (optional) Content to put in the TabPanelItem body
40296      * @param {Boolean} closable (optional) True to create a close icon on the tab
40297      * @return {Roo.TabPanelItem} The created TabPanelItem
40298      */
40299     addTab : function(id, text, content, closable, tpl)
40300     {
40301         var item = new Roo.bootstrap.panel.TabItem({
40302             panel: this,
40303             id : id,
40304             text : text,
40305             closable : closable,
40306             tpl : tpl
40307         });
40308         this.addTabItem(item);
40309         if(content){
40310             item.setContent(content);
40311         }
40312         return item;
40313     },
40314
40315     /**
40316      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40317      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40318      * @return {Roo.TabPanelItem}
40319      */
40320     getTab : function(id){
40321         return this.items[id];
40322     },
40323
40324     /**
40325      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40326      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40327      */
40328     hideTab : function(id){
40329         var t = this.items[id];
40330         if(!t.isHidden()){
40331            t.setHidden(true);
40332            this.hiddenCount++;
40333            this.autoSizeTabs();
40334         }
40335     },
40336
40337     /**
40338      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40339      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40340      */
40341     unhideTab : function(id){
40342         var t = this.items[id];
40343         if(t.isHidden()){
40344            t.setHidden(false);
40345            this.hiddenCount--;
40346            this.autoSizeTabs();
40347         }
40348     },
40349
40350     /**
40351      * Adds an existing {@link Roo.TabPanelItem}.
40352      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40353      */
40354     addTabItem : function(item)
40355     {
40356         this.items[item.id] = item;
40357         this.items.push(item);
40358         this.autoSizeTabs();
40359       //  if(this.resizeTabs){
40360     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40361   //         this.autoSizeTabs();
40362 //        }else{
40363 //            item.autoSize();
40364        // }
40365     },
40366
40367     /**
40368      * Removes a {@link Roo.TabPanelItem}.
40369      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40370      */
40371     removeTab : function(id){
40372         var items = this.items;
40373         var tab = items[id];
40374         if(!tab) { return; }
40375         var index = items.indexOf(tab);
40376         if(this.active == tab && items.length > 1){
40377             var newTab = this.getNextAvailable(index);
40378             if(newTab) {
40379                 newTab.activate();
40380             }
40381         }
40382         this.stripEl.dom.removeChild(tab.pnode.dom);
40383         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40384             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40385         }
40386         items.splice(index, 1);
40387         delete this.items[tab.id];
40388         tab.fireEvent("close", tab);
40389         tab.purgeListeners();
40390         this.autoSizeTabs();
40391     },
40392
40393     getNextAvailable : function(start){
40394         var items = this.items;
40395         var index = start;
40396         // look for a next tab that will slide over to
40397         // replace the one being removed
40398         while(index < items.length){
40399             var item = items[++index];
40400             if(item && !item.isHidden()){
40401                 return item;
40402             }
40403         }
40404         // if one isn't found select the previous tab (on the left)
40405         index = start;
40406         while(index >= 0){
40407             var item = items[--index];
40408             if(item && !item.isHidden()){
40409                 return item;
40410             }
40411         }
40412         return null;
40413     },
40414
40415     /**
40416      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40417      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40418      */
40419     disableTab : function(id){
40420         var tab = this.items[id];
40421         if(tab && this.active != tab){
40422             tab.disable();
40423         }
40424     },
40425
40426     /**
40427      * Enables a {@link Roo.TabPanelItem} that is disabled.
40428      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40429      */
40430     enableTab : function(id){
40431         var tab = this.items[id];
40432         tab.enable();
40433     },
40434
40435     /**
40436      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40437      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40438      * @return {Roo.TabPanelItem} The TabPanelItem.
40439      */
40440     activate : function(id)
40441     {
40442         //Roo.log('activite:'  + id);
40443         
40444         var tab = this.items[id];
40445         if(!tab){
40446             return null;
40447         }
40448         if(tab == this.active || tab.disabled){
40449             return tab;
40450         }
40451         var e = {};
40452         this.fireEvent("beforetabchange", this, e, tab);
40453         if(e.cancel !== true && !tab.disabled){
40454             if(this.active){
40455                 this.active.hide();
40456             }
40457             this.active = this.items[id];
40458             this.active.show();
40459             this.fireEvent("tabchange", this, this.active);
40460         }
40461         return tab;
40462     },
40463
40464     /**
40465      * Gets the active {@link Roo.TabPanelItem}.
40466      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40467      */
40468     getActiveTab : function(){
40469         return this.active;
40470     },
40471
40472     /**
40473      * Updates the tab body element to fit the height of the container element
40474      * for overflow scrolling
40475      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40476      */
40477     syncHeight : function(targetHeight){
40478         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40479         var bm = this.bodyEl.getMargins();
40480         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40481         this.bodyEl.setHeight(newHeight);
40482         return newHeight;
40483     },
40484
40485     onResize : function(){
40486         if(this.monitorResize){
40487             this.autoSizeTabs();
40488         }
40489     },
40490
40491     /**
40492      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40493      */
40494     beginUpdate : function(){
40495         this.updating = true;
40496     },
40497
40498     /**
40499      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40500      */
40501     endUpdate : function(){
40502         this.updating = false;
40503         this.autoSizeTabs();
40504     },
40505
40506     /**
40507      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40508      */
40509     autoSizeTabs : function()
40510     {
40511         var count = this.items.length;
40512         var vcount = count - this.hiddenCount;
40513         
40514         if (vcount < 2) {
40515             this.stripEl.hide();
40516         } else {
40517             this.stripEl.show();
40518         }
40519         
40520         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40521             return;
40522         }
40523         
40524         
40525         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40526         var availWidth = Math.floor(w / vcount);
40527         var b = this.stripBody;
40528         if(b.getWidth() > w){
40529             var tabs = this.items;
40530             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40531             if(availWidth < this.minTabWidth){
40532                 /*if(!this.sleft){    // incomplete scrolling code
40533                     this.createScrollButtons();
40534                 }
40535                 this.showScroll();
40536                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40537             }
40538         }else{
40539             if(this.currentTabWidth < this.preferredTabWidth){
40540                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40541             }
40542         }
40543     },
40544
40545     /**
40546      * Returns the number of tabs in this TabPanel.
40547      * @return {Number}
40548      */
40549      getCount : function(){
40550          return this.items.length;
40551      },
40552
40553     /**
40554      * Resizes all the tabs to the passed width
40555      * @param {Number} The new width
40556      */
40557     setTabWidth : function(width){
40558         this.currentTabWidth = width;
40559         for(var i = 0, len = this.items.length; i < len; i++) {
40560                 if(!this.items[i].isHidden()) {
40561                 this.items[i].setWidth(width);
40562             }
40563         }
40564     },
40565
40566     /**
40567      * Destroys this TabPanel
40568      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40569      */
40570     destroy : function(removeEl){
40571         Roo.EventManager.removeResizeListener(this.onResize, this);
40572         for(var i = 0, len = this.items.length; i < len; i++){
40573             this.items[i].purgeListeners();
40574         }
40575         if(removeEl === true){
40576             this.el.update("");
40577             this.el.remove();
40578         }
40579     },
40580     
40581     createStrip : function(container)
40582     {
40583         var strip = document.createElement("nav");
40584         strip.className = Roo.bootstrap.version == 4 ?
40585             "navbar-light bg-light" : 
40586             "navbar navbar-default"; //"x-tabs-wrap";
40587         container.appendChild(strip);
40588         return strip;
40589     },
40590     
40591     createStripList : function(strip)
40592     {
40593         // div wrapper for retard IE
40594         // returns the "tr" element.
40595         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40596         //'<div class="x-tabs-strip-wrap">'+
40597           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40598           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40599         return strip.firstChild; //.firstChild.firstChild.firstChild;
40600     },
40601     createBody : function(container)
40602     {
40603         var body = document.createElement("div");
40604         Roo.id(body, "tab-body");
40605         //Roo.fly(body).addClass("x-tabs-body");
40606         Roo.fly(body).addClass("tab-content");
40607         container.appendChild(body);
40608         return body;
40609     },
40610     createItemBody :function(bodyEl, id){
40611         var body = Roo.getDom(id);
40612         if(!body){
40613             body = document.createElement("div");
40614             body.id = id;
40615         }
40616         //Roo.fly(body).addClass("x-tabs-item-body");
40617         Roo.fly(body).addClass("tab-pane");
40618          bodyEl.insertBefore(body, bodyEl.firstChild);
40619         return body;
40620     },
40621     /** @private */
40622     createStripElements :  function(stripEl, text, closable, tpl)
40623     {
40624         var td = document.createElement("li"); // was td..
40625         td.className = 'nav-item';
40626         
40627         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40628         
40629         
40630         stripEl.appendChild(td);
40631         /*if(closable){
40632             td.className = "x-tabs-closable";
40633             if(!this.closeTpl){
40634                 this.closeTpl = new Roo.Template(
40635                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40636                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40637                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40638                 );
40639             }
40640             var el = this.closeTpl.overwrite(td, {"text": text});
40641             var close = el.getElementsByTagName("div")[0];
40642             var inner = el.getElementsByTagName("em")[0];
40643             return {"el": el, "close": close, "inner": inner};
40644         } else {
40645         */
40646         // not sure what this is..
40647 //            if(!this.tabTpl){
40648                 //this.tabTpl = new Roo.Template(
40649                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40650                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40651                 //);
40652 //                this.tabTpl = new Roo.Template(
40653 //                   '<a href="#">' +
40654 //                   '<span unselectable="on"' +
40655 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40656 //                            ' >{text}</span></a>'
40657 //                );
40658 //                
40659 //            }
40660
40661
40662             var template = tpl || this.tabTpl || false;
40663             
40664             if(!template){
40665                 template =  new Roo.Template(
40666                         Roo.bootstrap.version == 4 ? 
40667                             (
40668                                 '<a class="nav-link" href="#" unselectable="on"' +
40669                                      (this.disableTooltips ? '' : ' title="{text}"') +
40670                                      ' >{text}</a>'
40671                             ) : (
40672                                 '<a class="nav-link" href="#">' +
40673                                 '<span unselectable="on"' +
40674                                          (this.disableTooltips ? '' : ' title="{text}"') +
40675                                     ' >{text}</span></a>'
40676                             )
40677                 );
40678             }
40679             
40680             switch (typeof(template)) {
40681                 case 'object' :
40682                     break;
40683                 case 'string' :
40684                     template = new Roo.Template(template);
40685                     break;
40686                 default :
40687                     break;
40688             }
40689             
40690             var el = template.overwrite(td, {"text": text});
40691             
40692             var inner = el.getElementsByTagName("span")[0];
40693             
40694             return {"el": el, "inner": inner};
40695             
40696     }
40697         
40698     
40699 });
40700
40701 /**
40702  * @class Roo.TabPanelItem
40703  * @extends Roo.util.Observable
40704  * Represents an individual item (tab plus body) in a TabPanel.
40705  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40706  * @param {String} id The id of this TabPanelItem
40707  * @param {String} text The text for the tab of this TabPanelItem
40708  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40709  */
40710 Roo.bootstrap.panel.TabItem = function(config){
40711     /**
40712      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40713      * @type Roo.TabPanel
40714      */
40715     this.tabPanel = config.panel;
40716     /**
40717      * The id for this TabPanelItem
40718      * @type String
40719      */
40720     this.id = config.id;
40721     /** @private */
40722     this.disabled = false;
40723     /** @private */
40724     this.text = config.text;
40725     /** @private */
40726     this.loaded = false;
40727     this.closable = config.closable;
40728
40729     /**
40730      * The body element for this TabPanelItem.
40731      * @type Roo.Element
40732      */
40733     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40734     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40735     this.bodyEl.setStyle("display", "block");
40736     this.bodyEl.setStyle("zoom", "1");
40737     //this.hideAction();
40738
40739     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40740     /** @private */
40741     this.el = Roo.get(els.el);
40742     this.inner = Roo.get(els.inner, true);
40743      this.textEl = Roo.bootstrap.version == 4 ?
40744         this.el : Roo.get(this.el.dom.firstChild, true);
40745
40746     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40747     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40748
40749     
40750 //    this.el.on("mousedown", this.onTabMouseDown, this);
40751     this.el.on("click", this.onTabClick, this);
40752     /** @private */
40753     if(config.closable){
40754         var c = Roo.get(els.close, true);
40755         c.dom.title = this.closeText;
40756         c.addClassOnOver("close-over");
40757         c.on("click", this.closeClick, this);
40758      }
40759
40760     this.addEvents({
40761          /**
40762          * @event activate
40763          * Fires when this tab becomes the active tab.
40764          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40765          * @param {Roo.TabPanelItem} this
40766          */
40767         "activate": true,
40768         /**
40769          * @event beforeclose
40770          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40771          * @param {Roo.TabPanelItem} this
40772          * @param {Object} e Set cancel to true on this object to cancel the close.
40773          */
40774         "beforeclose": true,
40775         /**
40776          * @event close
40777          * Fires when this tab is closed.
40778          * @param {Roo.TabPanelItem} this
40779          */
40780          "close": true,
40781         /**
40782          * @event deactivate
40783          * Fires when this tab is no longer the active tab.
40784          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40785          * @param {Roo.TabPanelItem} this
40786          */
40787          "deactivate" : true
40788     });
40789     this.hidden = false;
40790
40791     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40792 };
40793
40794 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40795            {
40796     purgeListeners : function(){
40797        Roo.util.Observable.prototype.purgeListeners.call(this);
40798        this.el.removeAllListeners();
40799     },
40800     /**
40801      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40802      */
40803     show : function(){
40804         this.status_node.addClass("active");
40805         this.showAction();
40806         if(Roo.isOpera){
40807             this.tabPanel.stripWrap.repaint();
40808         }
40809         this.fireEvent("activate", this.tabPanel, this);
40810     },
40811
40812     /**
40813      * Returns true if this tab is the active tab.
40814      * @return {Boolean}
40815      */
40816     isActive : function(){
40817         return this.tabPanel.getActiveTab() == this;
40818     },
40819
40820     /**
40821      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40822      */
40823     hide : function(){
40824         this.status_node.removeClass("active");
40825         this.hideAction();
40826         this.fireEvent("deactivate", this.tabPanel, this);
40827     },
40828
40829     hideAction : function(){
40830         this.bodyEl.hide();
40831         this.bodyEl.setStyle("position", "absolute");
40832         this.bodyEl.setLeft("-20000px");
40833         this.bodyEl.setTop("-20000px");
40834     },
40835
40836     showAction : function(){
40837         this.bodyEl.setStyle("position", "relative");
40838         this.bodyEl.setTop("");
40839         this.bodyEl.setLeft("");
40840         this.bodyEl.show();
40841     },
40842
40843     /**
40844      * Set the tooltip for the tab.
40845      * @param {String} tooltip The tab's tooltip
40846      */
40847     setTooltip : function(text){
40848         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40849             this.textEl.dom.qtip = text;
40850             this.textEl.dom.removeAttribute('title');
40851         }else{
40852             this.textEl.dom.title = text;
40853         }
40854     },
40855
40856     onTabClick : function(e){
40857         e.preventDefault();
40858         this.tabPanel.activate(this.id);
40859     },
40860
40861     onTabMouseDown : function(e){
40862         e.preventDefault();
40863         this.tabPanel.activate(this.id);
40864     },
40865 /*
40866     getWidth : function(){
40867         return this.inner.getWidth();
40868     },
40869
40870     setWidth : function(width){
40871         var iwidth = width - this.linode.getPadding("lr");
40872         this.inner.setWidth(iwidth);
40873         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40874         this.linode.setWidth(width);
40875     },
40876 */
40877     /**
40878      * Show or hide the tab
40879      * @param {Boolean} hidden True to hide or false to show.
40880      */
40881     setHidden : function(hidden){
40882         this.hidden = hidden;
40883         this.linode.setStyle("display", hidden ? "none" : "");
40884     },
40885
40886     /**
40887      * Returns true if this tab is "hidden"
40888      * @return {Boolean}
40889      */
40890     isHidden : function(){
40891         return this.hidden;
40892     },
40893
40894     /**
40895      * Returns the text for this tab
40896      * @return {String}
40897      */
40898     getText : function(){
40899         return this.text;
40900     },
40901     /*
40902     autoSize : function(){
40903         //this.el.beginMeasure();
40904         this.textEl.setWidth(1);
40905         /*
40906          *  #2804 [new] Tabs in Roojs
40907          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40908          */
40909         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40910         //this.el.endMeasure();
40911     //},
40912
40913     /**
40914      * Sets the text for the tab (Note: this also sets the tooltip text)
40915      * @param {String} text The tab's text and tooltip
40916      */
40917     setText : function(text){
40918         this.text = text;
40919         this.textEl.update(text);
40920         this.setTooltip(text);
40921         //if(!this.tabPanel.resizeTabs){
40922         //    this.autoSize();
40923         //}
40924     },
40925     /**
40926      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40927      */
40928     activate : function(){
40929         this.tabPanel.activate(this.id);
40930     },
40931
40932     /**
40933      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40934      */
40935     disable : function(){
40936         if(this.tabPanel.active != this){
40937             this.disabled = true;
40938             this.status_node.addClass("disabled");
40939         }
40940     },
40941
40942     /**
40943      * Enables this TabPanelItem if it was previously disabled.
40944      */
40945     enable : function(){
40946         this.disabled = false;
40947         this.status_node.removeClass("disabled");
40948     },
40949
40950     /**
40951      * Sets the content for this TabPanelItem.
40952      * @param {String} content The content
40953      * @param {Boolean} loadScripts true to look for and load scripts
40954      */
40955     setContent : function(content, loadScripts){
40956         this.bodyEl.update(content, loadScripts);
40957     },
40958
40959     /**
40960      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40961      * @return {Roo.UpdateManager} The UpdateManager
40962      */
40963     getUpdateManager : function(){
40964         return this.bodyEl.getUpdateManager();
40965     },
40966
40967     /**
40968      * Set a URL to be used to load the content for this TabPanelItem.
40969      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40970      * @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)
40971      * @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)
40972      * @return {Roo.UpdateManager} The UpdateManager
40973      */
40974     setUrl : function(url, params, loadOnce){
40975         if(this.refreshDelegate){
40976             this.un('activate', this.refreshDelegate);
40977         }
40978         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40979         this.on("activate", this.refreshDelegate);
40980         return this.bodyEl.getUpdateManager();
40981     },
40982
40983     /** @private */
40984     _handleRefresh : function(url, params, loadOnce){
40985         if(!loadOnce || !this.loaded){
40986             var updater = this.bodyEl.getUpdateManager();
40987             updater.update(url, params, this._setLoaded.createDelegate(this));
40988         }
40989     },
40990
40991     /**
40992      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40993      *   Will fail silently if the setUrl method has not been called.
40994      *   This does not activate the panel, just updates its content.
40995      */
40996     refresh : function(){
40997         if(this.refreshDelegate){
40998            this.loaded = false;
40999            this.refreshDelegate();
41000         }
41001     },
41002
41003     /** @private */
41004     _setLoaded : function(){
41005         this.loaded = true;
41006     },
41007
41008     /** @private */
41009     closeClick : function(e){
41010         var o = {};
41011         e.stopEvent();
41012         this.fireEvent("beforeclose", this, o);
41013         if(o.cancel !== true){
41014             this.tabPanel.removeTab(this.id);
41015         }
41016     },
41017     /**
41018      * The text displayed in the tooltip for the close icon.
41019      * @type String
41020      */
41021     closeText : "Close this tab"
41022 });
41023 /**
41024 *    This script refer to:
41025 *    Title: International Telephone Input
41026 *    Author: Jack O'Connor
41027 *    Code version:  v12.1.12
41028 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41029 **/
41030
41031 Roo.bootstrap.PhoneInputData = function() {
41032     var d = [
41033       [
41034         "Afghanistan (‫افغانستان‬‎)",
41035         "af",
41036         "93"
41037       ],
41038       [
41039         "Albania (Shqipëri)",
41040         "al",
41041         "355"
41042       ],
41043       [
41044         "Algeria (‫الجزائر‬‎)",
41045         "dz",
41046         "213"
41047       ],
41048       [
41049         "American Samoa",
41050         "as",
41051         "1684"
41052       ],
41053       [
41054         "Andorra",
41055         "ad",
41056         "376"
41057       ],
41058       [
41059         "Angola",
41060         "ao",
41061         "244"
41062       ],
41063       [
41064         "Anguilla",
41065         "ai",
41066         "1264"
41067       ],
41068       [
41069         "Antigua and Barbuda",
41070         "ag",
41071         "1268"
41072       ],
41073       [
41074         "Argentina",
41075         "ar",
41076         "54"
41077       ],
41078       [
41079         "Armenia (Հայաստան)",
41080         "am",
41081         "374"
41082       ],
41083       [
41084         "Aruba",
41085         "aw",
41086         "297"
41087       ],
41088       [
41089         "Australia",
41090         "au",
41091         "61",
41092         0
41093       ],
41094       [
41095         "Austria (Österreich)",
41096         "at",
41097         "43"
41098       ],
41099       [
41100         "Azerbaijan (Azərbaycan)",
41101         "az",
41102         "994"
41103       ],
41104       [
41105         "Bahamas",
41106         "bs",
41107         "1242"
41108       ],
41109       [
41110         "Bahrain (‫البحرين‬‎)",
41111         "bh",
41112         "973"
41113       ],
41114       [
41115         "Bangladesh (বাংলাদেশ)",
41116         "bd",
41117         "880"
41118       ],
41119       [
41120         "Barbados",
41121         "bb",
41122         "1246"
41123       ],
41124       [
41125         "Belarus (Беларусь)",
41126         "by",
41127         "375"
41128       ],
41129       [
41130         "Belgium (België)",
41131         "be",
41132         "32"
41133       ],
41134       [
41135         "Belize",
41136         "bz",
41137         "501"
41138       ],
41139       [
41140         "Benin (Bénin)",
41141         "bj",
41142         "229"
41143       ],
41144       [
41145         "Bermuda",
41146         "bm",
41147         "1441"
41148       ],
41149       [
41150         "Bhutan (འབྲུག)",
41151         "bt",
41152         "975"
41153       ],
41154       [
41155         "Bolivia",
41156         "bo",
41157         "591"
41158       ],
41159       [
41160         "Bosnia and Herzegovina (Босна и Херцеговина)",
41161         "ba",
41162         "387"
41163       ],
41164       [
41165         "Botswana",
41166         "bw",
41167         "267"
41168       ],
41169       [
41170         "Brazil (Brasil)",
41171         "br",
41172         "55"
41173       ],
41174       [
41175         "British Indian Ocean Territory",
41176         "io",
41177         "246"
41178       ],
41179       [
41180         "British Virgin Islands",
41181         "vg",
41182         "1284"
41183       ],
41184       [
41185         "Brunei",
41186         "bn",
41187         "673"
41188       ],
41189       [
41190         "Bulgaria (България)",
41191         "bg",
41192         "359"
41193       ],
41194       [
41195         "Burkina Faso",
41196         "bf",
41197         "226"
41198       ],
41199       [
41200         "Burundi (Uburundi)",
41201         "bi",
41202         "257"
41203       ],
41204       [
41205         "Cambodia (កម្ពុជា)",
41206         "kh",
41207         "855"
41208       ],
41209       [
41210         "Cameroon (Cameroun)",
41211         "cm",
41212         "237"
41213       ],
41214       [
41215         "Canada",
41216         "ca",
41217         "1",
41218         1,
41219         ["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"]
41220       ],
41221       [
41222         "Cape Verde (Kabu Verdi)",
41223         "cv",
41224         "238"
41225       ],
41226       [
41227         "Caribbean Netherlands",
41228         "bq",
41229         "599",
41230         1
41231       ],
41232       [
41233         "Cayman Islands",
41234         "ky",
41235         "1345"
41236       ],
41237       [
41238         "Central African Republic (République centrafricaine)",
41239         "cf",
41240         "236"
41241       ],
41242       [
41243         "Chad (Tchad)",
41244         "td",
41245         "235"
41246       ],
41247       [
41248         "Chile",
41249         "cl",
41250         "56"
41251       ],
41252       [
41253         "China (中国)",
41254         "cn",
41255         "86"
41256       ],
41257       [
41258         "Christmas Island",
41259         "cx",
41260         "61",
41261         2
41262       ],
41263       [
41264         "Cocos (Keeling) Islands",
41265         "cc",
41266         "61",
41267         1
41268       ],
41269       [
41270         "Colombia",
41271         "co",
41272         "57"
41273       ],
41274       [
41275         "Comoros (‫جزر القمر‬‎)",
41276         "km",
41277         "269"
41278       ],
41279       [
41280         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41281         "cd",
41282         "243"
41283       ],
41284       [
41285         "Congo (Republic) (Congo-Brazzaville)",
41286         "cg",
41287         "242"
41288       ],
41289       [
41290         "Cook Islands",
41291         "ck",
41292         "682"
41293       ],
41294       [
41295         "Costa Rica",
41296         "cr",
41297         "506"
41298       ],
41299       [
41300         "Côte d’Ivoire",
41301         "ci",
41302         "225"
41303       ],
41304       [
41305         "Croatia (Hrvatska)",
41306         "hr",
41307         "385"
41308       ],
41309       [
41310         "Cuba",
41311         "cu",
41312         "53"
41313       ],
41314       [
41315         "Curaçao",
41316         "cw",
41317         "599",
41318         0
41319       ],
41320       [
41321         "Cyprus (Κύπρος)",
41322         "cy",
41323         "357"
41324       ],
41325       [
41326         "Czech Republic (Česká republika)",
41327         "cz",
41328         "420"
41329       ],
41330       [
41331         "Denmark (Danmark)",
41332         "dk",
41333         "45"
41334       ],
41335       [
41336         "Djibouti",
41337         "dj",
41338         "253"
41339       ],
41340       [
41341         "Dominica",
41342         "dm",
41343         "1767"
41344       ],
41345       [
41346         "Dominican Republic (República Dominicana)",
41347         "do",
41348         "1",
41349         2,
41350         ["809", "829", "849"]
41351       ],
41352       [
41353         "Ecuador",
41354         "ec",
41355         "593"
41356       ],
41357       [
41358         "Egypt (‫مصر‬‎)",
41359         "eg",
41360         "20"
41361       ],
41362       [
41363         "El Salvador",
41364         "sv",
41365         "503"
41366       ],
41367       [
41368         "Equatorial Guinea (Guinea Ecuatorial)",
41369         "gq",
41370         "240"
41371       ],
41372       [
41373         "Eritrea",
41374         "er",
41375         "291"
41376       ],
41377       [
41378         "Estonia (Eesti)",
41379         "ee",
41380         "372"
41381       ],
41382       [
41383         "Ethiopia",
41384         "et",
41385         "251"
41386       ],
41387       [
41388         "Falkland Islands (Islas Malvinas)",
41389         "fk",
41390         "500"
41391       ],
41392       [
41393         "Faroe Islands (Føroyar)",
41394         "fo",
41395         "298"
41396       ],
41397       [
41398         "Fiji",
41399         "fj",
41400         "679"
41401       ],
41402       [
41403         "Finland (Suomi)",
41404         "fi",
41405         "358",
41406         0
41407       ],
41408       [
41409         "France",
41410         "fr",
41411         "33"
41412       ],
41413       [
41414         "French Guiana (Guyane française)",
41415         "gf",
41416         "594"
41417       ],
41418       [
41419         "French Polynesia (Polynésie française)",
41420         "pf",
41421         "689"
41422       ],
41423       [
41424         "Gabon",
41425         "ga",
41426         "241"
41427       ],
41428       [
41429         "Gambia",
41430         "gm",
41431         "220"
41432       ],
41433       [
41434         "Georgia (საქართველო)",
41435         "ge",
41436         "995"
41437       ],
41438       [
41439         "Germany (Deutschland)",
41440         "de",
41441         "49"
41442       ],
41443       [
41444         "Ghana (Gaana)",
41445         "gh",
41446         "233"
41447       ],
41448       [
41449         "Gibraltar",
41450         "gi",
41451         "350"
41452       ],
41453       [
41454         "Greece (Ελλάδα)",
41455         "gr",
41456         "30"
41457       ],
41458       [
41459         "Greenland (Kalaallit Nunaat)",
41460         "gl",
41461         "299"
41462       ],
41463       [
41464         "Grenada",
41465         "gd",
41466         "1473"
41467       ],
41468       [
41469         "Guadeloupe",
41470         "gp",
41471         "590",
41472         0
41473       ],
41474       [
41475         "Guam",
41476         "gu",
41477         "1671"
41478       ],
41479       [
41480         "Guatemala",
41481         "gt",
41482         "502"
41483       ],
41484       [
41485         "Guernsey",
41486         "gg",
41487         "44",
41488         1
41489       ],
41490       [
41491         "Guinea (Guinée)",
41492         "gn",
41493         "224"
41494       ],
41495       [
41496         "Guinea-Bissau (Guiné Bissau)",
41497         "gw",
41498         "245"
41499       ],
41500       [
41501         "Guyana",
41502         "gy",
41503         "592"
41504       ],
41505       [
41506         "Haiti",
41507         "ht",
41508         "509"
41509       ],
41510       [
41511         "Honduras",
41512         "hn",
41513         "504"
41514       ],
41515       [
41516         "Hong Kong (香港)",
41517         "hk",
41518         "852"
41519       ],
41520       [
41521         "Hungary (Magyarország)",
41522         "hu",
41523         "36"
41524       ],
41525       [
41526         "Iceland (Ísland)",
41527         "is",
41528         "354"
41529       ],
41530       [
41531         "India (भारत)",
41532         "in",
41533         "91"
41534       ],
41535       [
41536         "Indonesia",
41537         "id",
41538         "62"
41539       ],
41540       [
41541         "Iran (‫ایران‬‎)",
41542         "ir",
41543         "98"
41544       ],
41545       [
41546         "Iraq (‫العراق‬‎)",
41547         "iq",
41548         "964"
41549       ],
41550       [
41551         "Ireland",
41552         "ie",
41553         "353"
41554       ],
41555       [
41556         "Isle of Man",
41557         "im",
41558         "44",
41559         2
41560       ],
41561       [
41562         "Israel (‫ישראל‬‎)",
41563         "il",
41564         "972"
41565       ],
41566       [
41567         "Italy (Italia)",
41568         "it",
41569         "39",
41570         0
41571       ],
41572       [
41573         "Jamaica",
41574         "jm",
41575         "1876"
41576       ],
41577       [
41578         "Japan (日本)",
41579         "jp",
41580         "81"
41581       ],
41582       [
41583         "Jersey",
41584         "je",
41585         "44",
41586         3
41587       ],
41588       [
41589         "Jordan (‫الأردن‬‎)",
41590         "jo",
41591         "962"
41592       ],
41593       [
41594         "Kazakhstan (Казахстан)",
41595         "kz",
41596         "7",
41597         1
41598       ],
41599       [
41600         "Kenya",
41601         "ke",
41602         "254"
41603       ],
41604       [
41605         "Kiribati",
41606         "ki",
41607         "686"
41608       ],
41609       [
41610         "Kosovo",
41611         "xk",
41612         "383"
41613       ],
41614       [
41615         "Kuwait (‫الكويت‬‎)",
41616         "kw",
41617         "965"
41618       ],
41619       [
41620         "Kyrgyzstan (Кыргызстан)",
41621         "kg",
41622         "996"
41623       ],
41624       [
41625         "Laos (ລາວ)",
41626         "la",
41627         "856"
41628       ],
41629       [
41630         "Latvia (Latvija)",
41631         "lv",
41632         "371"
41633       ],
41634       [
41635         "Lebanon (‫لبنان‬‎)",
41636         "lb",
41637         "961"
41638       ],
41639       [
41640         "Lesotho",
41641         "ls",
41642         "266"
41643       ],
41644       [
41645         "Liberia",
41646         "lr",
41647         "231"
41648       ],
41649       [
41650         "Libya (‫ليبيا‬‎)",
41651         "ly",
41652         "218"
41653       ],
41654       [
41655         "Liechtenstein",
41656         "li",
41657         "423"
41658       ],
41659       [
41660         "Lithuania (Lietuva)",
41661         "lt",
41662         "370"
41663       ],
41664       [
41665         "Luxembourg",
41666         "lu",
41667         "352"
41668       ],
41669       [
41670         "Macau (澳門)",
41671         "mo",
41672         "853"
41673       ],
41674       [
41675         "Macedonia (FYROM) (Македонија)",
41676         "mk",
41677         "389"
41678       ],
41679       [
41680         "Madagascar (Madagasikara)",
41681         "mg",
41682         "261"
41683       ],
41684       [
41685         "Malawi",
41686         "mw",
41687         "265"
41688       ],
41689       [
41690         "Malaysia",
41691         "my",
41692         "60"
41693       ],
41694       [
41695         "Maldives",
41696         "mv",
41697         "960"
41698       ],
41699       [
41700         "Mali",
41701         "ml",
41702         "223"
41703       ],
41704       [
41705         "Malta",
41706         "mt",
41707         "356"
41708       ],
41709       [
41710         "Marshall Islands",
41711         "mh",
41712         "692"
41713       ],
41714       [
41715         "Martinique",
41716         "mq",
41717         "596"
41718       ],
41719       [
41720         "Mauritania (‫موريتانيا‬‎)",
41721         "mr",
41722         "222"
41723       ],
41724       [
41725         "Mauritius (Moris)",
41726         "mu",
41727         "230"
41728       ],
41729       [
41730         "Mayotte",
41731         "yt",
41732         "262",
41733         1
41734       ],
41735       [
41736         "Mexico (México)",
41737         "mx",
41738         "52"
41739       ],
41740       [
41741         "Micronesia",
41742         "fm",
41743         "691"
41744       ],
41745       [
41746         "Moldova (Republica Moldova)",
41747         "md",
41748         "373"
41749       ],
41750       [
41751         "Monaco",
41752         "mc",
41753         "377"
41754       ],
41755       [
41756         "Mongolia (Монгол)",
41757         "mn",
41758         "976"
41759       ],
41760       [
41761         "Montenegro (Crna Gora)",
41762         "me",
41763         "382"
41764       ],
41765       [
41766         "Montserrat",
41767         "ms",
41768         "1664"
41769       ],
41770       [
41771         "Morocco (‫المغرب‬‎)",
41772         "ma",
41773         "212",
41774         0
41775       ],
41776       [
41777         "Mozambique (Moçambique)",
41778         "mz",
41779         "258"
41780       ],
41781       [
41782         "Myanmar (Burma) (မြန်မာ)",
41783         "mm",
41784         "95"
41785       ],
41786       [
41787         "Namibia (Namibië)",
41788         "na",
41789         "264"
41790       ],
41791       [
41792         "Nauru",
41793         "nr",
41794         "674"
41795       ],
41796       [
41797         "Nepal (नेपाल)",
41798         "np",
41799         "977"
41800       ],
41801       [
41802         "Netherlands (Nederland)",
41803         "nl",
41804         "31"
41805       ],
41806       [
41807         "New Caledonia (Nouvelle-Calédonie)",
41808         "nc",
41809         "687"
41810       ],
41811       [
41812         "New Zealand",
41813         "nz",
41814         "64"
41815       ],
41816       [
41817         "Nicaragua",
41818         "ni",
41819         "505"
41820       ],
41821       [
41822         "Niger (Nijar)",
41823         "ne",
41824         "227"
41825       ],
41826       [
41827         "Nigeria",
41828         "ng",
41829         "234"
41830       ],
41831       [
41832         "Niue",
41833         "nu",
41834         "683"
41835       ],
41836       [
41837         "Norfolk Island",
41838         "nf",
41839         "672"
41840       ],
41841       [
41842         "North Korea (조선 민주주의 인민 공화국)",
41843         "kp",
41844         "850"
41845       ],
41846       [
41847         "Northern Mariana Islands",
41848         "mp",
41849         "1670"
41850       ],
41851       [
41852         "Norway (Norge)",
41853         "no",
41854         "47",
41855         0
41856       ],
41857       [
41858         "Oman (‫عُمان‬‎)",
41859         "om",
41860         "968"
41861       ],
41862       [
41863         "Pakistan (‫پاکستان‬‎)",
41864         "pk",
41865         "92"
41866       ],
41867       [
41868         "Palau",
41869         "pw",
41870         "680"
41871       ],
41872       [
41873         "Palestine (‫فلسطين‬‎)",
41874         "ps",
41875         "970"
41876       ],
41877       [
41878         "Panama (Panamá)",
41879         "pa",
41880         "507"
41881       ],
41882       [
41883         "Papua New Guinea",
41884         "pg",
41885         "675"
41886       ],
41887       [
41888         "Paraguay",
41889         "py",
41890         "595"
41891       ],
41892       [
41893         "Peru (Perú)",
41894         "pe",
41895         "51"
41896       ],
41897       [
41898         "Philippines",
41899         "ph",
41900         "63"
41901       ],
41902       [
41903         "Poland (Polska)",
41904         "pl",
41905         "48"
41906       ],
41907       [
41908         "Portugal",
41909         "pt",
41910         "351"
41911       ],
41912       [
41913         "Puerto Rico",
41914         "pr",
41915         "1",
41916         3,
41917         ["787", "939"]
41918       ],
41919       [
41920         "Qatar (‫قطر‬‎)",
41921         "qa",
41922         "974"
41923       ],
41924       [
41925         "Réunion (La Réunion)",
41926         "re",
41927         "262",
41928         0
41929       ],
41930       [
41931         "Romania (România)",
41932         "ro",
41933         "40"
41934       ],
41935       [
41936         "Russia (Россия)",
41937         "ru",
41938         "7",
41939         0
41940       ],
41941       [
41942         "Rwanda",
41943         "rw",
41944         "250"
41945       ],
41946       [
41947         "Saint Barthélemy",
41948         "bl",
41949         "590",
41950         1
41951       ],
41952       [
41953         "Saint Helena",
41954         "sh",
41955         "290"
41956       ],
41957       [
41958         "Saint Kitts and Nevis",
41959         "kn",
41960         "1869"
41961       ],
41962       [
41963         "Saint Lucia",
41964         "lc",
41965         "1758"
41966       ],
41967       [
41968         "Saint Martin (Saint-Martin (partie française))",
41969         "mf",
41970         "590",
41971         2
41972       ],
41973       [
41974         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41975         "pm",
41976         "508"
41977       ],
41978       [
41979         "Saint Vincent and the Grenadines",
41980         "vc",
41981         "1784"
41982       ],
41983       [
41984         "Samoa",
41985         "ws",
41986         "685"
41987       ],
41988       [
41989         "San Marino",
41990         "sm",
41991         "378"
41992       ],
41993       [
41994         "São Tomé and Príncipe (São Tomé e Príncipe)",
41995         "st",
41996         "239"
41997       ],
41998       [
41999         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42000         "sa",
42001         "966"
42002       ],
42003       [
42004         "Senegal (Sénégal)",
42005         "sn",
42006         "221"
42007       ],
42008       [
42009         "Serbia (Србија)",
42010         "rs",
42011         "381"
42012       ],
42013       [
42014         "Seychelles",
42015         "sc",
42016         "248"
42017       ],
42018       [
42019         "Sierra Leone",
42020         "sl",
42021         "232"
42022       ],
42023       [
42024         "Singapore",
42025         "sg",
42026         "65"
42027       ],
42028       [
42029         "Sint Maarten",
42030         "sx",
42031         "1721"
42032       ],
42033       [
42034         "Slovakia (Slovensko)",
42035         "sk",
42036         "421"
42037       ],
42038       [
42039         "Slovenia (Slovenija)",
42040         "si",
42041         "386"
42042       ],
42043       [
42044         "Solomon Islands",
42045         "sb",
42046         "677"
42047       ],
42048       [
42049         "Somalia (Soomaaliya)",
42050         "so",
42051         "252"
42052       ],
42053       [
42054         "South Africa",
42055         "za",
42056         "27"
42057       ],
42058       [
42059         "South Korea (대한민국)",
42060         "kr",
42061         "82"
42062       ],
42063       [
42064         "South Sudan (‫جنوب السودان‬‎)",
42065         "ss",
42066         "211"
42067       ],
42068       [
42069         "Spain (España)",
42070         "es",
42071         "34"
42072       ],
42073       [
42074         "Sri Lanka (ශ්‍රී ලංකාව)",
42075         "lk",
42076         "94"
42077       ],
42078       [
42079         "Sudan (‫السودان‬‎)",
42080         "sd",
42081         "249"
42082       ],
42083       [
42084         "Suriname",
42085         "sr",
42086         "597"
42087       ],
42088       [
42089         "Svalbard and Jan Mayen",
42090         "sj",
42091         "47",
42092         1
42093       ],
42094       [
42095         "Swaziland",
42096         "sz",
42097         "268"
42098       ],
42099       [
42100         "Sweden (Sverige)",
42101         "se",
42102         "46"
42103       ],
42104       [
42105         "Switzerland (Schweiz)",
42106         "ch",
42107         "41"
42108       ],
42109       [
42110         "Syria (‫سوريا‬‎)",
42111         "sy",
42112         "963"
42113       ],
42114       [
42115         "Taiwan (台灣)",
42116         "tw",
42117         "886"
42118       ],
42119       [
42120         "Tajikistan",
42121         "tj",
42122         "992"
42123       ],
42124       [
42125         "Tanzania",
42126         "tz",
42127         "255"
42128       ],
42129       [
42130         "Thailand (ไทย)",
42131         "th",
42132         "66"
42133       ],
42134       [
42135         "Timor-Leste",
42136         "tl",
42137         "670"
42138       ],
42139       [
42140         "Togo",
42141         "tg",
42142         "228"
42143       ],
42144       [
42145         "Tokelau",
42146         "tk",
42147         "690"
42148       ],
42149       [
42150         "Tonga",
42151         "to",
42152         "676"
42153       ],
42154       [
42155         "Trinidad and Tobago",
42156         "tt",
42157         "1868"
42158       ],
42159       [
42160         "Tunisia (‫تونس‬‎)",
42161         "tn",
42162         "216"
42163       ],
42164       [
42165         "Turkey (Türkiye)",
42166         "tr",
42167         "90"
42168       ],
42169       [
42170         "Turkmenistan",
42171         "tm",
42172         "993"
42173       ],
42174       [
42175         "Turks and Caicos Islands",
42176         "tc",
42177         "1649"
42178       ],
42179       [
42180         "Tuvalu",
42181         "tv",
42182         "688"
42183       ],
42184       [
42185         "U.S. Virgin Islands",
42186         "vi",
42187         "1340"
42188       ],
42189       [
42190         "Uganda",
42191         "ug",
42192         "256"
42193       ],
42194       [
42195         "Ukraine (Україна)",
42196         "ua",
42197         "380"
42198       ],
42199       [
42200         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42201         "ae",
42202         "971"
42203       ],
42204       [
42205         "United Kingdom",
42206         "gb",
42207         "44",
42208         0
42209       ],
42210       [
42211         "United States",
42212         "us",
42213         "1",
42214         0
42215       ],
42216       [
42217         "Uruguay",
42218         "uy",
42219         "598"
42220       ],
42221       [
42222         "Uzbekistan (Oʻzbekiston)",
42223         "uz",
42224         "998"
42225       ],
42226       [
42227         "Vanuatu",
42228         "vu",
42229         "678"
42230       ],
42231       [
42232         "Vatican City (Città del Vaticano)",
42233         "va",
42234         "39",
42235         1
42236       ],
42237       [
42238         "Venezuela",
42239         "ve",
42240         "58"
42241       ],
42242       [
42243         "Vietnam (Việt Nam)",
42244         "vn",
42245         "84"
42246       ],
42247       [
42248         "Wallis and Futuna (Wallis-et-Futuna)",
42249         "wf",
42250         "681"
42251       ],
42252       [
42253         "Western Sahara (‫الصحراء الغربية‬‎)",
42254         "eh",
42255         "212",
42256         1
42257       ],
42258       [
42259         "Yemen (‫اليمن‬‎)",
42260         "ye",
42261         "967"
42262       ],
42263       [
42264         "Zambia",
42265         "zm",
42266         "260"
42267       ],
42268       [
42269         "Zimbabwe",
42270         "zw",
42271         "263"
42272       ],
42273       [
42274         "Åland Islands",
42275         "ax",
42276         "358",
42277         1
42278       ]
42279   ];
42280   
42281   return d;
42282 }/**
42283 *    This script refer to:
42284 *    Title: International Telephone Input
42285 *    Author: Jack O'Connor
42286 *    Code version:  v12.1.12
42287 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42288 **/
42289
42290 /**
42291  * @class Roo.bootstrap.PhoneInput
42292  * @extends Roo.bootstrap.TriggerField
42293  * An input with International dial-code selection
42294  
42295  * @cfg {String} defaultDialCode default '+852'
42296  * @cfg {Array} preferedCountries default []
42297   
42298  * @constructor
42299  * Create a new PhoneInput.
42300  * @param {Object} config Configuration options
42301  */
42302
42303 Roo.bootstrap.PhoneInput = function(config) {
42304     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42305 };
42306
42307 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42308         
42309         listWidth: undefined,
42310         
42311         selectedClass: 'active',
42312         
42313         invalidClass : "has-warning",
42314         
42315         validClass: 'has-success',
42316         
42317         allowed: '0123456789',
42318         
42319         max_length: 15,
42320         
42321         /**
42322          * @cfg {String} defaultDialCode The default dial code when initializing the input
42323          */
42324         defaultDialCode: '+852',
42325         
42326         /**
42327          * @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
42328          */
42329         preferedCountries: false,
42330         
42331         getAutoCreate : function()
42332         {
42333             var data = Roo.bootstrap.PhoneInputData();
42334             var align = this.labelAlign || this.parentLabelAlign();
42335             var id = Roo.id();
42336             
42337             this.allCountries = [];
42338             this.dialCodeMapping = [];
42339             
42340             for (var i = 0; i < data.length; i++) {
42341               var c = data[i];
42342               this.allCountries[i] = {
42343                 name: c[0],
42344                 iso2: c[1],
42345                 dialCode: c[2],
42346                 priority: c[3] || 0,
42347                 areaCodes: c[4] || null
42348               };
42349               this.dialCodeMapping[c[2]] = {
42350                   name: c[0],
42351                   iso2: c[1],
42352                   priority: c[3] || 0,
42353                   areaCodes: c[4] || null
42354               };
42355             }
42356             
42357             var cfg = {
42358                 cls: 'form-group',
42359                 cn: []
42360             };
42361             
42362             var input =  {
42363                 tag: 'input',
42364                 id : id,
42365                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42366                 maxlength: this.max_length,
42367                 cls : 'form-control tel-input',
42368                 autocomplete: 'new-password'
42369             };
42370             
42371             var hiddenInput = {
42372                 tag: 'input',
42373                 type: 'hidden',
42374                 cls: 'hidden-tel-input'
42375             };
42376             
42377             if (this.name) {
42378                 hiddenInput.name = this.name;
42379             }
42380             
42381             if (this.disabled) {
42382                 input.disabled = true;
42383             }
42384             
42385             var flag_container = {
42386                 tag: 'div',
42387                 cls: 'flag-box',
42388                 cn: [
42389                     {
42390                         tag: 'div',
42391                         cls: 'flag'
42392                     },
42393                     {
42394                         tag: 'div',
42395                         cls: 'caret'
42396                     }
42397                 ]
42398             };
42399             
42400             var box = {
42401                 tag: 'div',
42402                 cls: this.hasFeedback ? 'has-feedback' : '',
42403                 cn: [
42404                     hiddenInput,
42405                     input,
42406                     {
42407                         tag: 'input',
42408                         cls: 'dial-code-holder',
42409                         disabled: true
42410                     }
42411                 ]
42412             };
42413             
42414             var container = {
42415                 cls: 'roo-select2-container input-group',
42416                 cn: [
42417                     flag_container,
42418                     box
42419                 ]
42420             };
42421             
42422             if (this.fieldLabel.length) {
42423                 var indicator = {
42424                     tag: 'i',
42425                     tooltip: 'This field is required'
42426                 };
42427                 
42428                 var label = {
42429                     tag: 'label',
42430                     'for':  id,
42431                     cls: 'control-label',
42432                     cn: []
42433                 };
42434                 
42435                 var label_text = {
42436                     tag: 'span',
42437                     html: this.fieldLabel
42438                 };
42439                 
42440                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42441                 label.cn = [
42442                     indicator,
42443                     label_text
42444                 ];
42445                 
42446                 if(this.indicatorpos == 'right') {
42447                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42448                     label.cn = [
42449                         label_text,
42450                         indicator
42451                     ];
42452                 }
42453                 
42454                 if(align == 'left') {
42455                     container = {
42456                         tag: 'div',
42457                         cn: [
42458                             container
42459                         ]
42460                     };
42461                     
42462                     if(this.labelWidth > 12){
42463                         label.style = "width: " + this.labelWidth + 'px';
42464                     }
42465                     if(this.labelWidth < 13 && this.labelmd == 0){
42466                         this.labelmd = this.labelWidth;
42467                     }
42468                     if(this.labellg > 0){
42469                         label.cls += ' col-lg-' + this.labellg;
42470                         input.cls += ' col-lg-' + (12 - this.labellg);
42471                     }
42472                     if(this.labelmd > 0){
42473                         label.cls += ' col-md-' + this.labelmd;
42474                         container.cls += ' col-md-' + (12 - this.labelmd);
42475                     }
42476                     if(this.labelsm > 0){
42477                         label.cls += ' col-sm-' + this.labelsm;
42478                         container.cls += ' col-sm-' + (12 - this.labelsm);
42479                     }
42480                     if(this.labelxs > 0){
42481                         label.cls += ' col-xs-' + this.labelxs;
42482                         container.cls += ' col-xs-' + (12 - this.labelxs);
42483                     }
42484                 }
42485             }
42486             
42487             cfg.cn = [
42488                 label,
42489                 container
42490             ];
42491             
42492             var settings = this;
42493             
42494             ['xs','sm','md','lg'].map(function(size){
42495                 if (settings[size]) {
42496                     cfg.cls += ' col-' + size + '-' + settings[size];
42497                 }
42498             });
42499             
42500             this.store = new Roo.data.Store({
42501                 proxy : new Roo.data.MemoryProxy({}),
42502                 reader : new Roo.data.JsonReader({
42503                     fields : [
42504                         {
42505                             'name' : 'name',
42506                             'type' : 'string'
42507                         },
42508                         {
42509                             'name' : 'iso2',
42510                             'type' : 'string'
42511                         },
42512                         {
42513                             'name' : 'dialCode',
42514                             'type' : 'string'
42515                         },
42516                         {
42517                             'name' : 'priority',
42518                             'type' : 'string'
42519                         },
42520                         {
42521                             'name' : 'areaCodes',
42522                             'type' : 'string'
42523                         }
42524                     ]
42525                 })
42526             });
42527             
42528             if(!this.preferedCountries) {
42529                 this.preferedCountries = [
42530                     'hk',
42531                     'gb',
42532                     'us'
42533                 ];
42534             }
42535             
42536             var p = this.preferedCountries.reverse();
42537             
42538             if(p) {
42539                 for (var i = 0; i < p.length; i++) {
42540                     for (var j = 0; j < this.allCountries.length; j++) {
42541                         if(this.allCountries[j].iso2 == p[i]) {
42542                             var t = this.allCountries[j];
42543                             this.allCountries.splice(j,1);
42544                             this.allCountries.unshift(t);
42545                         }
42546                     } 
42547                 }
42548             }
42549             
42550             this.store.proxy.data = {
42551                 success: true,
42552                 data: this.allCountries
42553             };
42554             
42555             return cfg;
42556         },
42557         
42558         initEvents : function()
42559         {
42560             this.createList();
42561             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42562             
42563             this.indicator = this.indicatorEl();
42564             this.flag = this.flagEl();
42565             this.dialCodeHolder = this.dialCodeHolderEl();
42566             
42567             this.trigger = this.el.select('div.flag-box',true).first();
42568             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42569             
42570             var _this = this;
42571             
42572             (function(){
42573                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42574                 _this.list.setWidth(lw);
42575             }).defer(100);
42576             
42577             this.list.on('mouseover', this.onViewOver, this);
42578             this.list.on('mousemove', this.onViewMove, this);
42579             this.inputEl().on("keyup", this.onKeyUp, this);
42580             this.inputEl().on("keypress", this.onKeyPress, this);
42581             
42582             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42583
42584             this.view = new Roo.View(this.list, this.tpl, {
42585                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42586             });
42587             
42588             this.view.on('click', this.onViewClick, this);
42589             this.setValue(this.defaultDialCode);
42590         },
42591         
42592         onTriggerClick : function(e)
42593         {
42594             Roo.log('trigger click');
42595             if(this.disabled){
42596                 return;
42597             }
42598             
42599             if(this.isExpanded()){
42600                 this.collapse();
42601                 this.hasFocus = false;
42602             }else {
42603                 this.store.load({});
42604                 this.hasFocus = true;
42605                 this.expand();
42606             }
42607         },
42608         
42609         isExpanded : function()
42610         {
42611             return this.list.isVisible();
42612         },
42613         
42614         collapse : function()
42615         {
42616             if(!this.isExpanded()){
42617                 return;
42618             }
42619             this.list.hide();
42620             Roo.get(document).un('mousedown', this.collapseIf, this);
42621             Roo.get(document).un('mousewheel', this.collapseIf, this);
42622             this.fireEvent('collapse', this);
42623             this.validate();
42624         },
42625         
42626         expand : function()
42627         {
42628             Roo.log('expand');
42629
42630             if(this.isExpanded() || !this.hasFocus){
42631                 return;
42632             }
42633             
42634             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42635             this.list.setWidth(lw);
42636             
42637             this.list.show();
42638             this.restrictHeight();
42639             
42640             Roo.get(document).on('mousedown', this.collapseIf, this);
42641             Roo.get(document).on('mousewheel', this.collapseIf, this);
42642             
42643             this.fireEvent('expand', this);
42644         },
42645         
42646         restrictHeight : function()
42647         {
42648             this.list.alignTo(this.inputEl(), this.listAlign);
42649             this.list.alignTo(this.inputEl(), this.listAlign);
42650         },
42651         
42652         onViewOver : function(e, t)
42653         {
42654             if(this.inKeyMode){
42655                 return;
42656             }
42657             var item = this.view.findItemFromChild(t);
42658             
42659             if(item){
42660                 var index = this.view.indexOf(item);
42661                 this.select(index, false);
42662             }
42663         },
42664
42665         // private
42666         onViewClick : function(view, doFocus, el, e)
42667         {
42668             var index = this.view.getSelectedIndexes()[0];
42669             
42670             var r = this.store.getAt(index);
42671             
42672             if(r){
42673                 this.onSelect(r, index);
42674             }
42675             if(doFocus !== false && !this.blockFocus){
42676                 this.inputEl().focus();
42677             }
42678         },
42679         
42680         onViewMove : function(e, t)
42681         {
42682             this.inKeyMode = false;
42683         },
42684         
42685         select : function(index, scrollIntoView)
42686         {
42687             this.selectedIndex = index;
42688             this.view.select(index);
42689             if(scrollIntoView !== false){
42690                 var el = this.view.getNode(index);
42691                 if(el){
42692                     this.list.scrollChildIntoView(el, false);
42693                 }
42694             }
42695         },
42696         
42697         createList : function()
42698         {
42699             this.list = Roo.get(document.body).createChild({
42700                 tag: 'ul',
42701                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42702                 style: 'display:none'
42703             });
42704             
42705             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42706         },
42707         
42708         collapseIf : function(e)
42709         {
42710             var in_combo  = e.within(this.el);
42711             var in_list =  e.within(this.list);
42712             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42713             
42714             if (in_combo || in_list || is_list) {
42715                 return;
42716             }
42717             this.collapse();
42718         },
42719         
42720         onSelect : function(record, index)
42721         {
42722             if(this.fireEvent('beforeselect', this, record, index) !== false){
42723                 
42724                 this.setFlagClass(record.data.iso2);
42725                 this.setDialCode(record.data.dialCode);
42726                 this.hasFocus = false;
42727                 this.collapse();
42728                 this.fireEvent('select', this, record, index);
42729             }
42730         },
42731         
42732         flagEl : function()
42733         {
42734             var flag = this.el.select('div.flag',true).first();
42735             if(!flag){
42736                 return false;
42737             }
42738             return flag;
42739         },
42740         
42741         dialCodeHolderEl : function()
42742         {
42743             var d = this.el.select('input.dial-code-holder',true).first();
42744             if(!d){
42745                 return false;
42746             }
42747             return d;
42748         },
42749         
42750         setDialCode : function(v)
42751         {
42752             this.dialCodeHolder.dom.value = '+'+v;
42753         },
42754         
42755         setFlagClass : function(n)
42756         {
42757             this.flag.dom.className = 'flag '+n;
42758         },
42759         
42760         getValue : function()
42761         {
42762             var v = this.inputEl().getValue();
42763             if(this.dialCodeHolder) {
42764                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42765             }
42766             return v;
42767         },
42768         
42769         setValue : function(v)
42770         {
42771             var d = this.getDialCode(v);
42772             
42773             //invalid dial code
42774             if(v.length == 0 || !d || d.length == 0) {
42775                 if(this.rendered){
42776                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42777                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42778                 }
42779                 return;
42780             }
42781             
42782             //valid dial code
42783             this.setFlagClass(this.dialCodeMapping[d].iso2);
42784             this.setDialCode(d);
42785             this.inputEl().dom.value = v.replace('+'+d,'');
42786             this.hiddenEl().dom.value = this.getValue();
42787             
42788             this.validate();
42789         },
42790         
42791         getDialCode : function(v)
42792         {
42793             v = v ||  '';
42794             
42795             if (v.length == 0) {
42796                 return this.dialCodeHolder.dom.value;
42797             }
42798             
42799             var dialCode = "";
42800             if (v.charAt(0) != "+") {
42801                 return false;
42802             }
42803             var numericChars = "";
42804             for (var i = 1; i < v.length; i++) {
42805               var c = v.charAt(i);
42806               if (!isNaN(c)) {
42807                 numericChars += c;
42808                 if (this.dialCodeMapping[numericChars]) {
42809                   dialCode = v.substr(1, i);
42810                 }
42811                 if (numericChars.length == 4) {
42812                   break;
42813                 }
42814               }
42815             }
42816             return dialCode;
42817         },
42818         
42819         reset : function()
42820         {
42821             this.setValue(this.defaultDialCode);
42822             this.validate();
42823         },
42824         
42825         hiddenEl : function()
42826         {
42827             return this.el.select('input.hidden-tel-input',true).first();
42828         },
42829         
42830         // after setting val
42831         onKeyUp : function(e){
42832             this.setValue(this.getValue());
42833         },
42834         
42835         onKeyPress : function(e){
42836             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42837                 e.stopEvent();
42838             }
42839         }
42840         
42841 });
42842 /**
42843  * @class Roo.bootstrap.MoneyField
42844  * @extends Roo.bootstrap.ComboBox
42845  * Bootstrap MoneyField class
42846  * 
42847  * @constructor
42848  * Create a new MoneyField.
42849  * @param {Object} config Configuration options
42850  */
42851
42852 Roo.bootstrap.MoneyField = function(config) {
42853     
42854     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42855     
42856 };
42857
42858 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42859     
42860     /**
42861      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42862      */
42863     allowDecimals : true,
42864     /**
42865      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42866      */
42867     decimalSeparator : ".",
42868     /**
42869      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42870      */
42871     decimalPrecision : 0,
42872     /**
42873      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42874      */
42875     allowNegative : true,
42876     /**
42877      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42878      */
42879     allowZero: true,
42880     /**
42881      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42882      */
42883     minValue : Number.NEGATIVE_INFINITY,
42884     /**
42885      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42886      */
42887     maxValue : Number.MAX_VALUE,
42888     /**
42889      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42890      */
42891     minText : "The minimum value for this field is {0}",
42892     /**
42893      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42894      */
42895     maxText : "The maximum value for this field is {0}",
42896     /**
42897      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42898      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42899      */
42900     nanText : "{0} is not a valid number",
42901     /**
42902      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42903      */
42904     castInt : true,
42905     /**
42906      * @cfg {String} defaults currency of the MoneyField
42907      * value should be in lkey
42908      */
42909     defaultCurrency : false,
42910     /**
42911      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42912      */
42913     thousandsDelimiter : false,
42914     /**
42915      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42916      */
42917     max_length: false,
42918     
42919     inputlg : 9,
42920     inputmd : 9,
42921     inputsm : 9,
42922     inputxs : 6,
42923     
42924     store : false,
42925     
42926     getAutoCreate : function()
42927     {
42928         var align = this.labelAlign || this.parentLabelAlign();
42929         
42930         var id = Roo.id();
42931
42932         var cfg = {
42933             cls: 'form-group',
42934             cn: []
42935         };
42936
42937         var input =  {
42938             tag: 'input',
42939             id : id,
42940             cls : 'form-control roo-money-amount-input',
42941             autocomplete: 'new-password'
42942         };
42943         
42944         var hiddenInput = {
42945             tag: 'input',
42946             type: 'hidden',
42947             id: Roo.id(),
42948             cls: 'hidden-number-input'
42949         };
42950         
42951         if(this.max_length) {
42952             input.maxlength = this.max_length; 
42953         }
42954         
42955         if (this.name) {
42956             hiddenInput.name = this.name;
42957         }
42958
42959         if (this.disabled) {
42960             input.disabled = true;
42961         }
42962
42963         var clg = 12 - this.inputlg;
42964         var cmd = 12 - this.inputmd;
42965         var csm = 12 - this.inputsm;
42966         var cxs = 12 - this.inputxs;
42967         
42968         var container = {
42969             tag : 'div',
42970             cls : 'row roo-money-field',
42971             cn : [
42972                 {
42973                     tag : 'div',
42974                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42975                     cn : [
42976                         {
42977                             tag : 'div',
42978                             cls: 'roo-select2-container input-group',
42979                             cn: [
42980                                 {
42981                                     tag : 'input',
42982                                     cls : 'form-control roo-money-currency-input',
42983                                     autocomplete: 'new-password',
42984                                     readOnly : 1,
42985                                     name : this.currencyName
42986                                 },
42987                                 {
42988                                     tag :'span',
42989                                     cls : 'input-group-addon',
42990                                     cn : [
42991                                         {
42992                                             tag: 'span',
42993                                             cls: 'caret'
42994                                         }
42995                                     ]
42996                                 }
42997                             ]
42998                         }
42999                     ]
43000                 },
43001                 {
43002                     tag : 'div',
43003                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43004                     cn : [
43005                         {
43006                             tag: 'div',
43007                             cls: this.hasFeedback ? 'has-feedback' : '',
43008                             cn: [
43009                                 input
43010                             ]
43011                         }
43012                     ]
43013                 }
43014             ]
43015             
43016         };
43017         
43018         if (this.fieldLabel.length) {
43019             var indicator = {
43020                 tag: 'i',
43021                 tooltip: 'This field is required'
43022             };
43023
43024             var label = {
43025                 tag: 'label',
43026                 'for':  id,
43027                 cls: 'control-label',
43028                 cn: []
43029             };
43030
43031             var label_text = {
43032                 tag: 'span',
43033                 html: this.fieldLabel
43034             };
43035
43036             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43037             label.cn = [
43038                 indicator,
43039                 label_text
43040             ];
43041
43042             if(this.indicatorpos == 'right') {
43043                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43044                 label.cn = [
43045                     label_text,
43046                     indicator
43047                 ];
43048             }
43049
43050             if(align == 'left') {
43051                 container = {
43052                     tag: 'div',
43053                     cn: [
43054                         container
43055                     ]
43056                 };
43057
43058                 if(this.labelWidth > 12){
43059                     label.style = "width: " + this.labelWidth + 'px';
43060                 }
43061                 if(this.labelWidth < 13 && this.labelmd == 0){
43062                     this.labelmd = this.labelWidth;
43063                 }
43064                 if(this.labellg > 0){
43065                     label.cls += ' col-lg-' + this.labellg;
43066                     input.cls += ' col-lg-' + (12 - this.labellg);
43067                 }
43068                 if(this.labelmd > 0){
43069                     label.cls += ' col-md-' + this.labelmd;
43070                     container.cls += ' col-md-' + (12 - this.labelmd);
43071                 }
43072                 if(this.labelsm > 0){
43073                     label.cls += ' col-sm-' + this.labelsm;
43074                     container.cls += ' col-sm-' + (12 - this.labelsm);
43075                 }
43076                 if(this.labelxs > 0){
43077                     label.cls += ' col-xs-' + this.labelxs;
43078                     container.cls += ' col-xs-' + (12 - this.labelxs);
43079                 }
43080             }
43081         }
43082
43083         cfg.cn = [
43084             label,
43085             container,
43086             hiddenInput
43087         ];
43088         
43089         var settings = this;
43090
43091         ['xs','sm','md','lg'].map(function(size){
43092             if (settings[size]) {
43093                 cfg.cls += ' col-' + size + '-' + settings[size];
43094             }
43095         });
43096         
43097         return cfg;
43098     },
43099     
43100     initEvents : function()
43101     {
43102         this.indicator = this.indicatorEl();
43103         
43104         this.initCurrencyEvent();
43105         
43106         this.initNumberEvent();
43107     },
43108     
43109     initCurrencyEvent : function()
43110     {
43111         if (!this.store) {
43112             throw "can not find store for combo";
43113         }
43114         
43115         this.store = Roo.factory(this.store, Roo.data);
43116         this.store.parent = this;
43117         
43118         this.createList();
43119         
43120         this.triggerEl = this.el.select('.input-group-addon', true).first();
43121         
43122         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43123         
43124         var _this = this;
43125         
43126         (function(){
43127             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43128             _this.list.setWidth(lw);
43129         }).defer(100);
43130         
43131         this.list.on('mouseover', this.onViewOver, this);
43132         this.list.on('mousemove', this.onViewMove, this);
43133         this.list.on('scroll', this.onViewScroll, this);
43134         
43135         if(!this.tpl){
43136             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43137         }
43138         
43139         this.view = new Roo.View(this.list, this.tpl, {
43140             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43141         });
43142         
43143         this.view.on('click', this.onViewClick, this);
43144         
43145         this.store.on('beforeload', this.onBeforeLoad, this);
43146         this.store.on('load', this.onLoad, this);
43147         this.store.on('loadexception', this.onLoadException, this);
43148         
43149         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43150             "up" : function(e){
43151                 this.inKeyMode = true;
43152                 this.selectPrev();
43153             },
43154
43155             "down" : function(e){
43156                 if(!this.isExpanded()){
43157                     this.onTriggerClick();
43158                 }else{
43159                     this.inKeyMode = true;
43160                     this.selectNext();
43161                 }
43162             },
43163
43164             "enter" : function(e){
43165                 this.collapse();
43166                 
43167                 if(this.fireEvent("specialkey", this, e)){
43168                     this.onViewClick(false);
43169                 }
43170                 
43171                 return true;
43172             },
43173
43174             "esc" : function(e){
43175                 this.collapse();
43176             },
43177
43178             "tab" : function(e){
43179                 this.collapse();
43180                 
43181                 if(this.fireEvent("specialkey", this, e)){
43182                     this.onViewClick(false);
43183                 }
43184                 
43185                 return true;
43186             },
43187
43188             scope : this,
43189
43190             doRelay : function(foo, bar, hname){
43191                 if(hname == 'down' || this.scope.isExpanded()){
43192                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43193                 }
43194                 return true;
43195             },
43196
43197             forceKeyDown: true
43198         });
43199         
43200         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43201         
43202     },
43203     
43204     initNumberEvent : function(e)
43205     {
43206         this.inputEl().on("keydown" , this.fireKey,  this);
43207         this.inputEl().on("focus", this.onFocus,  this);
43208         this.inputEl().on("blur", this.onBlur,  this);
43209         
43210         this.inputEl().relayEvent('keyup', this);
43211         
43212         if(this.indicator){
43213             this.indicator.addClass('invisible');
43214         }
43215  
43216         this.originalValue = this.getValue();
43217         
43218         if(this.validationEvent == 'keyup'){
43219             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43220             this.inputEl().on('keyup', this.filterValidation, this);
43221         }
43222         else if(this.validationEvent !== false){
43223             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43224         }
43225         
43226         if(this.selectOnFocus){
43227             this.on("focus", this.preFocus, this);
43228             
43229         }
43230         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43231             this.inputEl().on("keypress", this.filterKeys, this);
43232         } else {
43233             this.inputEl().relayEvent('keypress', this);
43234         }
43235         
43236         var allowed = "0123456789";
43237         
43238         if(this.allowDecimals){
43239             allowed += this.decimalSeparator;
43240         }
43241         
43242         if(this.allowNegative){
43243             allowed += "-";
43244         }
43245         
43246         if(this.thousandsDelimiter) {
43247             allowed += ",";
43248         }
43249         
43250         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43251         
43252         var keyPress = function(e){
43253             
43254             var k = e.getKey();
43255             
43256             var c = e.getCharCode();
43257             
43258             if(
43259                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43260                     allowed.indexOf(String.fromCharCode(c)) === -1
43261             ){
43262                 e.stopEvent();
43263                 return;
43264             }
43265             
43266             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43267                 return;
43268             }
43269             
43270             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43271                 e.stopEvent();
43272             }
43273         };
43274         
43275         this.inputEl().on("keypress", keyPress, this);
43276         
43277     },
43278     
43279     onTriggerClick : function(e)
43280     {   
43281         if(this.disabled){
43282             return;
43283         }
43284         
43285         this.page = 0;
43286         this.loadNext = false;
43287         
43288         if(this.isExpanded()){
43289             this.collapse();
43290             return;
43291         }
43292         
43293         this.hasFocus = true;
43294         
43295         if(this.triggerAction == 'all') {
43296             this.doQuery(this.allQuery, true);
43297             return;
43298         }
43299         
43300         this.doQuery(this.getRawValue());
43301     },
43302     
43303     getCurrency : function()
43304     {   
43305         var v = this.currencyEl().getValue();
43306         
43307         return v;
43308     },
43309     
43310     restrictHeight : function()
43311     {
43312         this.list.alignTo(this.currencyEl(), this.listAlign);
43313         this.list.alignTo(this.currencyEl(), this.listAlign);
43314     },
43315     
43316     onViewClick : function(view, doFocus, el, e)
43317     {
43318         var index = this.view.getSelectedIndexes()[0];
43319         
43320         var r = this.store.getAt(index);
43321         
43322         if(r){
43323             this.onSelect(r, index);
43324         }
43325     },
43326     
43327     onSelect : function(record, index){
43328         
43329         if(this.fireEvent('beforeselect', this, record, index) !== false){
43330         
43331             this.setFromCurrencyData(index > -1 ? record.data : false);
43332             
43333             this.collapse();
43334             
43335             this.fireEvent('select', this, record, index);
43336         }
43337     },
43338     
43339     setFromCurrencyData : function(o)
43340     {
43341         var currency = '';
43342         
43343         this.lastCurrency = o;
43344         
43345         if (this.currencyField) {
43346             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43347         } else {
43348             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43349         }
43350         
43351         this.lastSelectionText = currency;
43352         
43353         //setting default currency
43354         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43355             this.setCurrency(this.defaultCurrency);
43356             return;
43357         }
43358         
43359         this.setCurrency(currency);
43360     },
43361     
43362     setFromData : function(o)
43363     {
43364         var c = {};
43365         
43366         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43367         
43368         this.setFromCurrencyData(c);
43369         
43370         var value = '';
43371         
43372         if (this.name) {
43373             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43374         } else {
43375             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43376         }
43377         
43378         this.setValue(value);
43379         
43380     },
43381     
43382     setCurrency : function(v)
43383     {   
43384         this.currencyValue = v;
43385         
43386         if(this.rendered){
43387             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43388             this.validate();
43389         }
43390     },
43391     
43392     setValue : function(v)
43393     {
43394         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43395         
43396         this.value = v;
43397         
43398         if(this.rendered){
43399             
43400             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43401             
43402             this.inputEl().dom.value = (v == '') ? '' :
43403                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43404             
43405             if(!this.allowZero && v === '0') {
43406                 this.hiddenEl().dom.value = '';
43407                 this.inputEl().dom.value = '';
43408             }
43409             
43410             this.validate();
43411         }
43412     },
43413     
43414     getRawValue : function()
43415     {
43416         var v = this.inputEl().getValue();
43417         
43418         return v;
43419     },
43420     
43421     getValue : function()
43422     {
43423         return this.fixPrecision(this.parseValue(this.getRawValue()));
43424     },
43425     
43426     parseValue : function(value)
43427     {
43428         if(this.thousandsDelimiter) {
43429             value += "";
43430             r = new RegExp(",", "g");
43431             value = value.replace(r, "");
43432         }
43433         
43434         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43435         return isNaN(value) ? '' : value;
43436         
43437     },
43438     
43439     fixPrecision : function(value)
43440     {
43441         if(this.thousandsDelimiter) {
43442             value += "";
43443             r = new RegExp(",", "g");
43444             value = value.replace(r, "");
43445         }
43446         
43447         var nan = isNaN(value);
43448         
43449         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43450             return nan ? '' : value;
43451         }
43452         return parseFloat(value).toFixed(this.decimalPrecision);
43453     },
43454     
43455     decimalPrecisionFcn : function(v)
43456     {
43457         return Math.floor(v);
43458     },
43459     
43460     validateValue : function(value)
43461     {
43462         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43463             return false;
43464         }
43465         
43466         var num = this.parseValue(value);
43467         
43468         if(isNaN(num)){
43469             this.markInvalid(String.format(this.nanText, value));
43470             return false;
43471         }
43472         
43473         if(num < this.minValue){
43474             this.markInvalid(String.format(this.minText, this.minValue));
43475             return false;
43476         }
43477         
43478         if(num > this.maxValue){
43479             this.markInvalid(String.format(this.maxText, this.maxValue));
43480             return false;
43481         }
43482         
43483         return true;
43484     },
43485     
43486     validate : function()
43487     {
43488         if(this.disabled || this.allowBlank){
43489             this.markValid();
43490             return true;
43491         }
43492         
43493         var currency = this.getCurrency();
43494         
43495         if(this.validateValue(this.getRawValue()) && currency.length){
43496             this.markValid();
43497             return true;
43498         }
43499         
43500         this.markInvalid();
43501         return false;
43502     },
43503     
43504     getName: function()
43505     {
43506         return this.name;
43507     },
43508     
43509     beforeBlur : function()
43510     {
43511         if(!this.castInt){
43512             return;
43513         }
43514         
43515         var v = this.parseValue(this.getRawValue());
43516         
43517         if(v || v == 0){
43518             this.setValue(v);
43519         }
43520     },
43521     
43522     onBlur : function()
43523     {
43524         this.beforeBlur();
43525         
43526         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43527             //this.el.removeClass(this.focusClass);
43528         }
43529         
43530         this.hasFocus = false;
43531         
43532         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43533             this.validate();
43534         }
43535         
43536         var v = this.getValue();
43537         
43538         if(String(v) !== String(this.startValue)){
43539             this.fireEvent('change', this, v, this.startValue);
43540         }
43541         
43542         this.fireEvent("blur", this);
43543     },
43544     
43545     inputEl : function()
43546     {
43547         return this.el.select('.roo-money-amount-input', true).first();
43548     },
43549     
43550     currencyEl : function()
43551     {
43552         return this.el.select('.roo-money-currency-input', true).first();
43553     },
43554     
43555     hiddenEl : function()
43556     {
43557         return this.el.select('input.hidden-number-input',true).first();
43558     }
43559     
43560 });/**
43561  * @class Roo.bootstrap.BezierSignature
43562  * @extends Roo.bootstrap.Component
43563  * Bootstrap BezierSignature class
43564  * This script refer to:
43565  *    Title: Signature Pad
43566  *    Author: szimek
43567  *    Availability: https://github.com/szimek/signature_pad
43568  *
43569  * @constructor
43570  * Create a new BezierSignature
43571  * @param {Object} config The config object
43572  */
43573
43574 Roo.bootstrap.BezierSignature = function(config){
43575     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43576     this.addEvents({
43577         "resize" : true
43578     });
43579 };
43580
43581 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43582 {
43583      
43584     curve_data: [],
43585     
43586     is_empty: true,
43587     
43588     mouse_btn_down: true,
43589     
43590     /**
43591      * @cfg {int} canvas height
43592      */
43593     canvas_height: '200px',
43594     
43595     /**
43596      * @cfg {float|function} Radius of a single dot.
43597      */ 
43598     dot_size: false,
43599     
43600     /**
43601      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43602      */
43603     min_width: 0.5,
43604     
43605     /**
43606      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43607      */
43608     max_width: 2.5,
43609     
43610     /**
43611      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43612      */
43613     throttle: 16,
43614     
43615     /**
43616      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43617      */
43618     min_distance: 5,
43619     
43620     /**
43621      * @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.
43622      */
43623     bg_color: 'rgba(0, 0, 0, 0)',
43624     
43625     /**
43626      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43627      */
43628     dot_color: 'black',
43629     
43630     /**
43631      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43632      */ 
43633     velocity_filter_weight: 0.7,
43634     
43635     /**
43636      * @cfg {function} Callback when stroke begin. 
43637      */
43638     onBegin: false,
43639     
43640     /**
43641      * @cfg {function} Callback when stroke end.
43642      */
43643     onEnd: false,
43644     
43645     getAutoCreate : function()
43646     {
43647         var cls = 'roo-signature column';
43648         
43649         if(this.cls){
43650             cls += ' ' + this.cls;
43651         }
43652         
43653         var col_sizes = [
43654             'lg',
43655             'md',
43656             'sm',
43657             'xs'
43658         ];
43659         
43660         for(var i = 0; i < col_sizes.length; i++) {
43661             if(this[col_sizes[i]]) {
43662                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43663             }
43664         }
43665         
43666         var cfg = {
43667             tag: 'div',
43668             cls: cls,
43669             cn: [
43670                 {
43671                     tag: 'div',
43672                     cls: 'roo-signature-body',
43673                     cn: [
43674                         {
43675                             tag: 'canvas',
43676                             cls: 'roo-signature-body-canvas',
43677                             height: this.canvas_height,
43678                             width: this.canvas_width
43679                         }
43680                     ]
43681                 },
43682                 {
43683                     tag: 'input',
43684                     type: 'file',
43685                     style: 'display: none'
43686                 }
43687             ]
43688         };
43689         
43690         return cfg;
43691     },
43692     
43693     initEvents: function() 
43694     {
43695         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43696         
43697         var canvas = this.canvasEl();
43698         
43699         // mouse && touch event swapping...
43700         canvas.dom.style.touchAction = 'none';
43701         canvas.dom.style.msTouchAction = 'none';
43702         
43703         this.mouse_btn_down = false;
43704         canvas.on('mousedown', this._handleMouseDown, this);
43705         canvas.on('mousemove', this._handleMouseMove, this);
43706         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43707         
43708         if (window.PointerEvent) {
43709             canvas.on('pointerdown', this._handleMouseDown, this);
43710             canvas.on('pointermove', this._handleMouseMove, this);
43711             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43712         }
43713         
43714         if ('ontouchstart' in window) {
43715             canvas.on('touchstart', this._handleTouchStart, this);
43716             canvas.on('touchmove', this._handleTouchMove, this);
43717             canvas.on('touchend', this._handleTouchEnd, this);
43718         }
43719         
43720         Roo.EventManager.onWindowResize(this.resize, this, true);
43721         
43722         // file input event
43723         this.fileEl().on('change', this.uploadImage, this);
43724         
43725         this.clear();
43726         
43727         this.resize();
43728     },
43729     
43730     resize: function(){
43731         
43732         var canvas = this.canvasEl().dom;
43733         var ctx = this.canvasElCtx();
43734         var img_data = false;
43735         
43736         if(canvas.width > 0) {
43737             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43738         }
43739         // setting canvas width will clean img data
43740         canvas.width = 0;
43741         
43742         var style = window.getComputedStyle ? 
43743             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43744             
43745         var padding_left = parseInt(style.paddingLeft) || 0;
43746         var padding_right = parseInt(style.paddingRight) || 0;
43747         
43748         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43749         
43750         if(img_data) {
43751             ctx.putImageData(img_data, 0, 0);
43752         }
43753     },
43754     
43755     _handleMouseDown: function(e)
43756     {
43757         if (e.browserEvent.which === 1) {
43758             this.mouse_btn_down = true;
43759             this.strokeBegin(e);
43760         }
43761     },
43762     
43763     _handleMouseMove: function (e)
43764     {
43765         if (this.mouse_btn_down) {
43766             this.strokeMoveUpdate(e);
43767         }
43768     },
43769     
43770     _handleMouseUp: function (e)
43771     {
43772         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43773             this.mouse_btn_down = false;
43774             this.strokeEnd(e);
43775         }
43776     },
43777     
43778     _handleTouchStart: function (e) {
43779         
43780         e.preventDefault();
43781         if (e.browserEvent.targetTouches.length === 1) {
43782             // var touch = e.browserEvent.changedTouches[0];
43783             // this.strokeBegin(touch);
43784             
43785              this.strokeBegin(e); // assume e catching the correct xy...
43786         }
43787     },
43788     
43789     _handleTouchMove: function (e) {
43790         e.preventDefault();
43791         // var touch = event.targetTouches[0];
43792         // _this._strokeMoveUpdate(touch);
43793         this.strokeMoveUpdate(e);
43794     },
43795     
43796     _handleTouchEnd: function (e) {
43797         var wasCanvasTouched = e.target === this.canvasEl().dom;
43798         if (wasCanvasTouched) {
43799             e.preventDefault();
43800             // var touch = event.changedTouches[0];
43801             // _this._strokeEnd(touch);
43802             this.strokeEnd(e);
43803         }
43804     },
43805     
43806     reset: function () {
43807         this._lastPoints = [];
43808         this._lastVelocity = 0;
43809         this._lastWidth = (this.min_width + this.max_width) / 2;
43810         this.canvasElCtx().fillStyle = this.dot_color;
43811     },
43812     
43813     strokeMoveUpdate: function(e)
43814     {
43815         this.strokeUpdate(e);
43816         
43817         if (this.throttle) {
43818             this.throttleStroke(this.strokeUpdate, this.throttle);
43819         }
43820         else {
43821             this.strokeUpdate(e);
43822         }
43823     },
43824     
43825     strokeBegin: function(e)
43826     {
43827         var newPointGroup = {
43828             color: this.dot_color,
43829             points: []
43830         };
43831         
43832         if (typeof this.onBegin === 'function') {
43833             this.onBegin(e);
43834         }
43835         
43836         this.curve_data.push(newPointGroup);
43837         this.reset();
43838         this.strokeUpdate(e);
43839     },
43840     
43841     strokeUpdate: function(e)
43842     {
43843         var rect = this.canvasEl().dom.getBoundingClientRect();
43844         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43845         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43846         var lastPoints = lastPointGroup.points;
43847         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43848         var isLastPointTooClose = lastPoint
43849             ? point.distanceTo(lastPoint) <= this.min_distance
43850             : false;
43851         var color = lastPointGroup.color;
43852         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43853             var curve = this.addPoint(point);
43854             if (!lastPoint) {
43855                 this.drawDot({color: color, point: point});
43856             }
43857             else if (curve) {
43858                 this.drawCurve({color: color, curve: curve});
43859             }
43860             lastPoints.push({
43861                 time: point.time,
43862                 x: point.x,
43863                 y: point.y
43864             });
43865         }
43866     },
43867     
43868     strokeEnd: function(e)
43869     {
43870         this.strokeUpdate(e);
43871         if (typeof this.onEnd === 'function') {
43872             this.onEnd(e);
43873         }
43874     },
43875     
43876     addPoint:  function (point) {
43877         var _lastPoints = this._lastPoints;
43878         _lastPoints.push(point);
43879         if (_lastPoints.length > 2) {
43880             if (_lastPoints.length === 3) {
43881                 _lastPoints.unshift(_lastPoints[0]);
43882             }
43883             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43884             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43885             _lastPoints.shift();
43886             return curve;
43887         }
43888         return null;
43889     },
43890     
43891     calculateCurveWidths: function (startPoint, endPoint) {
43892         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43893             (1 - this.velocity_filter_weight) * this._lastVelocity;
43894
43895         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43896         var widths = {
43897             end: newWidth,
43898             start: this._lastWidth
43899         };
43900         
43901         this._lastVelocity = velocity;
43902         this._lastWidth = newWidth;
43903         return widths;
43904     },
43905     
43906     drawDot: function (_a) {
43907         var color = _a.color, point = _a.point;
43908         var ctx = this.canvasElCtx();
43909         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43910         ctx.beginPath();
43911         this.drawCurveSegment(point.x, point.y, width);
43912         ctx.closePath();
43913         ctx.fillStyle = color;
43914         ctx.fill();
43915     },
43916     
43917     drawCurve: function (_a) {
43918         var color = _a.color, curve = _a.curve;
43919         var ctx = this.canvasElCtx();
43920         var widthDelta = curve.endWidth - curve.startWidth;
43921         var drawSteps = Math.floor(curve.length()) * 2;
43922         ctx.beginPath();
43923         ctx.fillStyle = color;
43924         for (var i = 0; i < drawSteps; i += 1) {
43925         var t = i / drawSteps;
43926         var tt = t * t;
43927         var ttt = tt * t;
43928         var u = 1 - t;
43929         var uu = u * u;
43930         var uuu = uu * u;
43931         var x = uuu * curve.startPoint.x;
43932         x += 3 * uu * t * curve.control1.x;
43933         x += 3 * u * tt * curve.control2.x;
43934         x += ttt * curve.endPoint.x;
43935         var y = uuu * curve.startPoint.y;
43936         y += 3 * uu * t * curve.control1.y;
43937         y += 3 * u * tt * curve.control2.y;
43938         y += ttt * curve.endPoint.y;
43939         var width = curve.startWidth + ttt * widthDelta;
43940         this.drawCurveSegment(x, y, width);
43941         }
43942         ctx.closePath();
43943         ctx.fill();
43944     },
43945     
43946     drawCurveSegment: function (x, y, width) {
43947         var ctx = this.canvasElCtx();
43948         ctx.moveTo(x, y);
43949         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43950         this.is_empty = false;
43951     },
43952     
43953     clear: function()
43954     {
43955         var ctx = this.canvasElCtx();
43956         var canvas = this.canvasEl().dom;
43957         ctx.fillStyle = this.bg_color;
43958         ctx.clearRect(0, 0, canvas.width, canvas.height);
43959         ctx.fillRect(0, 0, canvas.width, canvas.height);
43960         this.curve_data = [];
43961         this.reset();
43962         this.is_empty = true;
43963     },
43964     
43965     fileEl: function()
43966     {
43967         return  this.el.select('input',true).first();
43968     },
43969     
43970     canvasEl: function()
43971     {
43972         return this.el.select('canvas',true).first();
43973     },
43974     
43975     canvasElCtx: function()
43976     {
43977         return this.el.select('canvas',true).first().dom.getContext('2d');
43978     },
43979     
43980     getImage: function(type)
43981     {
43982         if(this.is_empty) {
43983             return false;
43984         }
43985         
43986         // encryption ?
43987         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43988     },
43989     
43990     drawFromImage: function(img_src)
43991     {
43992         var img = new Image();
43993         
43994         img.onload = function(){
43995             this.canvasElCtx().drawImage(img, 0, 0);
43996         }.bind(this);
43997         
43998         img.src = img_src;
43999         
44000         this.is_empty = false;
44001     },
44002     
44003     selectImage: function()
44004     {
44005         this.fileEl().dom.click();
44006     },
44007     
44008     uploadImage: function(e)
44009     {
44010         var reader = new FileReader();
44011         
44012         reader.onload = function(e){
44013             var img = new Image();
44014             img.onload = function(){
44015                 this.reset();
44016                 this.canvasElCtx().drawImage(img, 0, 0);
44017             }.bind(this);
44018             img.src = e.target.result;
44019         }.bind(this);
44020         
44021         reader.readAsDataURL(e.target.files[0]);
44022     },
44023     
44024     // Bezier Point Constructor
44025     Point: (function () {
44026         function Point(x, y, time) {
44027             this.x = x;
44028             this.y = y;
44029             this.time = time || Date.now();
44030         }
44031         Point.prototype.distanceTo = function (start) {
44032             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44033         };
44034         Point.prototype.equals = function (other) {
44035             return this.x === other.x && this.y === other.y && this.time === other.time;
44036         };
44037         Point.prototype.velocityFrom = function (start) {
44038             return this.time !== start.time
44039             ? this.distanceTo(start) / (this.time - start.time)
44040             : 0;
44041         };
44042         return Point;
44043     }()),
44044     
44045     
44046     // Bezier Constructor
44047     Bezier: (function () {
44048         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44049             this.startPoint = startPoint;
44050             this.control2 = control2;
44051             this.control1 = control1;
44052             this.endPoint = endPoint;
44053             this.startWidth = startWidth;
44054             this.endWidth = endWidth;
44055         }
44056         Bezier.fromPoints = function (points, widths, scope) {
44057             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44058             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44059             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44060         };
44061         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44062             var dx1 = s1.x - s2.x;
44063             var dy1 = s1.y - s2.y;
44064             var dx2 = s2.x - s3.x;
44065             var dy2 = s2.y - s3.y;
44066             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44067             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44068             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44069             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44070             var dxm = m1.x - m2.x;
44071             var dym = m1.y - m2.y;
44072             var k = l2 / (l1 + l2);
44073             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44074             var tx = s2.x - cm.x;
44075             var ty = s2.y - cm.y;
44076             return {
44077                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44078                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44079             };
44080         };
44081         Bezier.prototype.length = function () {
44082             var steps = 10;
44083             var length = 0;
44084             var px;
44085             var py;
44086             for (var i = 0; i <= steps; i += 1) {
44087                 var t = i / steps;
44088                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44089                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44090                 if (i > 0) {
44091                     var xdiff = cx - px;
44092                     var ydiff = cy - py;
44093                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44094                 }
44095                 px = cx;
44096                 py = cy;
44097             }
44098             return length;
44099         };
44100         Bezier.prototype.point = function (t, start, c1, c2, end) {
44101             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44102             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44103             + (3.0 * c2 * (1.0 - t) * t * t)
44104             + (end * t * t * t);
44105         };
44106         return Bezier;
44107     }()),
44108     
44109     throttleStroke: function(fn, wait) {
44110       if (wait === void 0) { wait = 250; }
44111       var previous = 0;
44112       var timeout = null;
44113       var result;
44114       var storedContext;
44115       var storedArgs;
44116       var later = function () {
44117           previous = Date.now();
44118           timeout = null;
44119           result = fn.apply(storedContext, storedArgs);
44120           if (!timeout) {
44121               storedContext = null;
44122               storedArgs = [];
44123           }
44124       };
44125       return function wrapper() {
44126           var args = [];
44127           for (var _i = 0; _i < arguments.length; _i++) {
44128               args[_i] = arguments[_i];
44129           }
44130           var now = Date.now();
44131           var remaining = wait - (now - previous);
44132           storedContext = this;
44133           storedArgs = args;
44134           if (remaining <= 0 || remaining > wait) {
44135               if (timeout) {
44136                   clearTimeout(timeout);
44137                   timeout = null;
44138               }
44139               previous = now;
44140               result = fn.apply(storedContext, storedArgs);
44141               if (!timeout) {
44142                   storedContext = null;
44143                   storedArgs = [];
44144               }
44145           }
44146           else if (!timeout) {
44147               timeout = window.setTimeout(later, remaining);
44148           }
44149           return result;
44150       };
44151   }
44152   
44153 });
44154
44155  
44156
44157