sync
[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.namespace('Roo.bootstrap.breadcrumb');
6625
6626 Roo.bootstrap.breadcrumb.Nav = function(config){
6627     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6628     
6629     
6630 };
6631
6632 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6633     
6634     getAutoCreate : function()
6635     {
6636
6637         var cfg = {
6638             tag: 'nav',
6639             cn : [
6640                 {
6641                     tag : 'ol',
6642                     cls : 'breadcrumb'
6643                 }
6644             ]
6645             
6646         };
6647           
6648         return cfg;
6649     },
6650     
6651     initEvents: function()
6652     {
6653         this.olEl = this.el.select('ol',true).first();    
6654     },
6655     getChildContainer : function()
6656     {
6657         return this.olEl;  
6658     }
6659     
6660 });
6661
6662  /*
6663  * - LGPL
6664  *
6665  *  Breadcrumb Item
6666  * 
6667  */
6668
6669
6670 /**
6671  * @class Roo.bootstrap.breadcrumb.Nav
6672  * @extends Roo.bootstrap.Component
6673  * Bootstrap Breadcrumb Nav Class
6674  *  
6675  * @children Roo.bootstrap.breadcrumb.Component
6676  * @cfg {String} html the content of the link.
6677  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6678  * @cfg {Boolean} active is it active
6679
6680  * 
6681  * @constructor
6682  * Create a new breadcrumb.Nav
6683  * @param {Object} config The config object
6684  */
6685
6686 Roo.bootstrap.breadcrumb.Item = function(config){
6687     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6688     this.addEvents({
6689         // img events
6690         /**
6691          * @event click
6692          * The img click event for the img.
6693          * @param {Roo.EventObject} e
6694          */
6695         "click" : true
6696     });
6697     
6698 };
6699
6700 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6701     
6702     href: false,
6703     html : '',
6704     
6705     getAutoCreate : function()
6706     {
6707
6708         var cfg = {
6709             tag: 'li',
6710             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6711         };
6712         if (this.href !== false) {
6713             cfg.cn = [{
6714                 tag : 'a',
6715                 href : this.href,
6716                 html : this.html
6717             }];
6718         } else {
6719             cfg.html = this.html;
6720         }
6721         
6722         return cfg;
6723     },
6724     
6725     initEvents: function()
6726     {
6727         if (this.href) {
6728             this.el.select('a', true).first().onClick(this.onClick, this)
6729         }
6730         
6731     },
6732     onClick : function(e)
6733     {
6734         e.preventDefault();
6735         this.fireEvent('click',this,  e);
6736     }
6737     
6738 });
6739
6740  /*
6741  * - LGPL
6742  *
6743  * row
6744  * 
6745  */
6746
6747 /**
6748  * @class Roo.bootstrap.Row
6749  * @extends Roo.bootstrap.Component
6750  * Bootstrap Row class (contains columns...)
6751  * 
6752  * @constructor
6753  * Create a new Row
6754  * @param {Object} config The config object
6755  */
6756
6757 Roo.bootstrap.Row = function(config){
6758     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6759 };
6760
6761 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6762     
6763     getAutoCreate : function(){
6764        return {
6765             cls: 'row clearfix'
6766        };
6767     }
6768     
6769     
6770 });
6771
6772  
6773
6774  /*
6775  * - LGPL
6776  *
6777  * pagination
6778  * 
6779  */
6780
6781 /**
6782  * @class Roo.bootstrap.Pagination
6783  * @extends Roo.bootstrap.Component
6784  * Bootstrap Pagination class
6785  * @cfg {String} size xs | sm | md | lg
6786  * @cfg {Boolean} inverse false | true
6787  * 
6788  * @constructor
6789  * Create a new Pagination
6790  * @param {Object} config The config object
6791  */
6792
6793 Roo.bootstrap.Pagination = function(config){
6794     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6795 };
6796
6797 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6798     
6799     cls: false,
6800     size: false,
6801     inverse: false,
6802     
6803     getAutoCreate : function(){
6804         var cfg = {
6805             tag: 'ul',
6806                 cls: 'pagination'
6807         };
6808         if (this.inverse) {
6809             cfg.cls += ' inverse';
6810         }
6811         if (this.html) {
6812             cfg.html=this.html;
6813         }
6814         if (this.cls) {
6815             cfg.cls += " " + this.cls;
6816         }
6817         return cfg;
6818     }
6819    
6820 });
6821
6822  
6823
6824  /*
6825  * - LGPL
6826  *
6827  * Pagination item
6828  * 
6829  */
6830
6831
6832 /**
6833  * @class Roo.bootstrap.PaginationItem
6834  * @extends Roo.bootstrap.Component
6835  * Bootstrap PaginationItem class
6836  * @cfg {String} html text
6837  * @cfg {String} href the link
6838  * @cfg {Boolean} preventDefault (true | false) default true
6839  * @cfg {Boolean} active (true | false) default false
6840  * @cfg {Boolean} disabled default false
6841  * 
6842  * 
6843  * @constructor
6844  * Create a new PaginationItem
6845  * @param {Object} config The config object
6846  */
6847
6848
6849 Roo.bootstrap.PaginationItem = function(config){
6850     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6851     this.addEvents({
6852         // raw events
6853         /**
6854          * @event click
6855          * The raw click event for the entire grid.
6856          * @param {Roo.EventObject} e
6857          */
6858         "click" : true
6859     });
6860 };
6861
6862 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6863     
6864     href : false,
6865     html : false,
6866     preventDefault: true,
6867     active : false,
6868     cls : false,
6869     disabled: false,
6870     
6871     getAutoCreate : function(){
6872         var cfg= {
6873             tag: 'li',
6874             cn: [
6875                 {
6876                     tag : 'a',
6877                     href : this.href ? this.href : '#',
6878                     html : this.html ? this.html : ''
6879                 }
6880             ]
6881         };
6882         
6883         if(this.cls){
6884             cfg.cls = this.cls;
6885         }
6886         
6887         if(this.disabled){
6888             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6889         }
6890         
6891         if(this.active){
6892             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6893         }
6894         
6895         return cfg;
6896     },
6897     
6898     initEvents: function() {
6899         
6900         this.el.on('click', this.onClick, this);
6901         
6902     },
6903     onClick : function(e)
6904     {
6905         Roo.log('PaginationItem on click ');
6906         if(this.preventDefault){
6907             e.preventDefault();
6908         }
6909         
6910         if(this.disabled){
6911             return;
6912         }
6913         
6914         this.fireEvent('click', this, e);
6915     }
6916    
6917 });
6918
6919  
6920
6921  /*
6922  * - LGPL
6923  *
6924  * slider
6925  * 
6926  */
6927
6928
6929 /**
6930  * @class Roo.bootstrap.Slider
6931  * @extends Roo.bootstrap.Component
6932  * Bootstrap Slider class
6933  *    
6934  * @constructor
6935  * Create a new Slider
6936  * @param {Object} config The config object
6937  */
6938
6939 Roo.bootstrap.Slider = function(config){
6940     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6941 };
6942
6943 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6944     
6945     getAutoCreate : function(){
6946         
6947         var cfg = {
6948             tag: 'div',
6949             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6950             cn: [
6951                 {
6952                     tag: 'a',
6953                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6954                 }
6955             ]
6956         };
6957         
6958         return cfg;
6959     }
6960    
6961 });
6962
6963  /*
6964  * Based on:
6965  * Ext JS Library 1.1.1
6966  * Copyright(c) 2006-2007, Ext JS, LLC.
6967  *
6968  * Originally Released Under LGPL - original licence link has changed is not relivant.
6969  *
6970  * Fork - LGPL
6971  * <script type="text/javascript">
6972  */
6973  
6974
6975 /**
6976  * @class Roo.grid.ColumnModel
6977  * @extends Roo.util.Observable
6978  * This is the default implementation of a ColumnModel used by the Grid. It defines
6979  * the columns in the grid.
6980  * <br>Usage:<br>
6981  <pre><code>
6982  var colModel = new Roo.grid.ColumnModel([
6983         {header: "Ticker", width: 60, sortable: true, locked: true},
6984         {header: "Company Name", width: 150, sortable: true},
6985         {header: "Market Cap.", width: 100, sortable: true},
6986         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6987         {header: "Employees", width: 100, sortable: true, resizable: false}
6988  ]);
6989  </code></pre>
6990  * <p>
6991  
6992  * The config options listed for this class are options which may appear in each
6993  * individual column definition.
6994  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6995  * @constructor
6996  * @param {Object} config An Array of column config objects. See this class's
6997  * config objects for details.
6998 */
6999 Roo.grid.ColumnModel = function(config){
7000         /**
7001      * The config passed into the constructor
7002      */
7003     this.config = config;
7004     this.lookup = {};
7005
7006     // if no id, create one
7007     // if the column does not have a dataIndex mapping,
7008     // map it to the order it is in the config
7009     for(var i = 0, len = config.length; i < len; i++){
7010         var c = config[i];
7011         if(typeof c.dataIndex == "undefined"){
7012             c.dataIndex = i;
7013         }
7014         if(typeof c.renderer == "string"){
7015             c.renderer = Roo.util.Format[c.renderer];
7016         }
7017         if(typeof c.id == "undefined"){
7018             c.id = Roo.id();
7019         }
7020         if(c.editor && c.editor.xtype){
7021             c.editor  = Roo.factory(c.editor, Roo.grid);
7022         }
7023         if(c.editor && c.editor.isFormField){
7024             c.editor = new Roo.grid.GridEditor(c.editor);
7025         }
7026         this.lookup[c.id] = c;
7027     }
7028
7029     /**
7030      * The width of columns which have no width specified (defaults to 100)
7031      * @type Number
7032      */
7033     this.defaultWidth = 100;
7034
7035     /**
7036      * Default sortable of columns which have no sortable specified (defaults to false)
7037      * @type Boolean
7038      */
7039     this.defaultSortable = false;
7040
7041     this.addEvents({
7042         /**
7043              * @event widthchange
7044              * Fires when the width of a column changes.
7045              * @param {ColumnModel} this
7046              * @param {Number} columnIndex The column index
7047              * @param {Number} newWidth The new width
7048              */
7049             "widthchange": true,
7050         /**
7051              * @event headerchange
7052              * Fires when the text of a header changes.
7053              * @param {ColumnModel} this
7054              * @param {Number} columnIndex The column index
7055              * @param {Number} newText The new header text
7056              */
7057             "headerchange": true,
7058         /**
7059              * @event hiddenchange
7060              * Fires when a column is hidden or "unhidden".
7061              * @param {ColumnModel} this
7062              * @param {Number} columnIndex The column index
7063              * @param {Boolean} hidden true if hidden, false otherwise
7064              */
7065             "hiddenchange": true,
7066             /**
7067          * @event columnmoved
7068          * Fires when a column is moved.
7069          * @param {ColumnModel} this
7070          * @param {Number} oldIndex
7071          * @param {Number} newIndex
7072          */
7073         "columnmoved" : true,
7074         /**
7075          * @event columlockchange
7076          * Fires when a column's locked state is changed
7077          * @param {ColumnModel} this
7078          * @param {Number} colIndex
7079          * @param {Boolean} locked true if locked
7080          */
7081         "columnlockchange" : true
7082     });
7083     Roo.grid.ColumnModel.superclass.constructor.call(this);
7084 };
7085 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7086     /**
7087      * @cfg {String} header The header text to display in the Grid view.
7088      */
7089     /**
7090      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7091      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7092      * specified, the column's index is used as an index into the Record's data Array.
7093      */
7094     /**
7095      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7096      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7097      */
7098     /**
7099      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7100      * Defaults to the value of the {@link #defaultSortable} property.
7101      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7102      */
7103     /**
7104      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7105      */
7106     /**
7107      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7108      */
7109     /**
7110      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7111      */
7112     /**
7113      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7114      */
7115     /**
7116      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7117      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7118      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7119      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7120      */
7121        /**
7122      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7123      */
7124     /**
7125      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7126      */
7127     /**
7128      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7129      */
7130     /**
7131      * @cfg {String} cursor (Optional)
7132      */
7133     /**
7134      * @cfg {String} tooltip (Optional)
7135      */
7136     /**
7137      * @cfg {Number} xs (Optional)
7138      */
7139     /**
7140      * @cfg {Number} sm (Optional)
7141      */
7142     /**
7143      * @cfg {Number} md (Optional)
7144      */
7145     /**
7146      * @cfg {Number} lg (Optional)
7147      */
7148     /**
7149      * Returns the id of the column at the specified index.
7150      * @param {Number} index The column index
7151      * @return {String} the id
7152      */
7153     getColumnId : function(index){
7154         return this.config[index].id;
7155     },
7156
7157     /**
7158      * Returns the column for a specified id.
7159      * @param {String} id The column id
7160      * @return {Object} the column
7161      */
7162     getColumnById : function(id){
7163         return this.lookup[id];
7164     },
7165
7166     
7167     /**
7168      * Returns the column for a specified dataIndex.
7169      * @param {String} dataIndex The column dataIndex
7170      * @return {Object|Boolean} the column or false if not found
7171      */
7172     getColumnByDataIndex: function(dataIndex){
7173         var index = this.findColumnIndex(dataIndex);
7174         return index > -1 ? this.config[index] : false;
7175     },
7176     
7177     /**
7178      * Returns the index for a specified column id.
7179      * @param {String} id The column id
7180      * @return {Number} the index, or -1 if not found
7181      */
7182     getIndexById : function(id){
7183         for(var i = 0, len = this.config.length; i < len; i++){
7184             if(this.config[i].id == id){
7185                 return i;
7186             }
7187         }
7188         return -1;
7189     },
7190     
7191     /**
7192      * Returns the index for a specified column dataIndex.
7193      * @param {String} dataIndex The column dataIndex
7194      * @return {Number} the index, or -1 if not found
7195      */
7196     
7197     findColumnIndex : function(dataIndex){
7198         for(var i = 0, len = this.config.length; i < len; i++){
7199             if(this.config[i].dataIndex == dataIndex){
7200                 return i;
7201             }
7202         }
7203         return -1;
7204     },
7205     
7206     
7207     moveColumn : function(oldIndex, newIndex){
7208         var c = this.config[oldIndex];
7209         this.config.splice(oldIndex, 1);
7210         this.config.splice(newIndex, 0, c);
7211         this.dataMap = null;
7212         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7213     },
7214
7215     isLocked : function(colIndex){
7216         return this.config[colIndex].locked === true;
7217     },
7218
7219     setLocked : function(colIndex, value, suppressEvent){
7220         if(this.isLocked(colIndex) == value){
7221             return;
7222         }
7223         this.config[colIndex].locked = value;
7224         if(!suppressEvent){
7225             this.fireEvent("columnlockchange", this, colIndex, value);
7226         }
7227     },
7228
7229     getTotalLockedWidth : function(){
7230         var totalWidth = 0;
7231         for(var i = 0; i < this.config.length; i++){
7232             if(this.isLocked(i) && !this.isHidden(i)){
7233                 this.totalWidth += this.getColumnWidth(i);
7234             }
7235         }
7236         return totalWidth;
7237     },
7238
7239     getLockedCount : function(){
7240         for(var i = 0, len = this.config.length; i < len; i++){
7241             if(!this.isLocked(i)){
7242                 return i;
7243             }
7244         }
7245         
7246         return this.config.length;
7247     },
7248
7249     /**
7250      * Returns the number of columns.
7251      * @return {Number}
7252      */
7253     getColumnCount : function(visibleOnly){
7254         if(visibleOnly === true){
7255             var c = 0;
7256             for(var i = 0, len = this.config.length; i < len; i++){
7257                 if(!this.isHidden(i)){
7258                     c++;
7259                 }
7260             }
7261             return c;
7262         }
7263         return this.config.length;
7264     },
7265
7266     /**
7267      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7268      * @param {Function} fn
7269      * @param {Object} scope (optional)
7270      * @return {Array} result
7271      */
7272     getColumnsBy : function(fn, scope){
7273         var r = [];
7274         for(var i = 0, len = this.config.length; i < len; i++){
7275             var c = this.config[i];
7276             if(fn.call(scope||this, c, i) === true){
7277                 r[r.length] = c;
7278             }
7279         }
7280         return r;
7281     },
7282
7283     /**
7284      * Returns true if the specified column is sortable.
7285      * @param {Number} col The column index
7286      * @return {Boolean}
7287      */
7288     isSortable : function(col){
7289         if(typeof this.config[col].sortable == "undefined"){
7290             return this.defaultSortable;
7291         }
7292         return this.config[col].sortable;
7293     },
7294
7295     /**
7296      * Returns the rendering (formatting) function defined for the column.
7297      * @param {Number} col The column index.
7298      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7299      */
7300     getRenderer : function(col){
7301         if(!this.config[col].renderer){
7302             return Roo.grid.ColumnModel.defaultRenderer;
7303         }
7304         return this.config[col].renderer;
7305     },
7306
7307     /**
7308      * Sets the rendering (formatting) function for a column.
7309      * @param {Number} col The column index
7310      * @param {Function} fn The function to use to process the cell's raw data
7311      * to return HTML markup for the grid view. The render function is called with
7312      * the following parameters:<ul>
7313      * <li>Data value.</li>
7314      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7315      * <li>css A CSS style string to apply to the table cell.</li>
7316      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7317      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7318      * <li>Row index</li>
7319      * <li>Column index</li>
7320      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7321      */
7322     setRenderer : function(col, fn){
7323         this.config[col].renderer = fn;
7324     },
7325
7326     /**
7327      * Returns the width for the specified column.
7328      * @param {Number} col The column index
7329      * @return {Number}
7330      */
7331     getColumnWidth : function(col){
7332         return this.config[col].width * 1 || this.defaultWidth;
7333     },
7334
7335     /**
7336      * Sets the width for a column.
7337      * @param {Number} col The column index
7338      * @param {Number} width The new width
7339      */
7340     setColumnWidth : function(col, width, suppressEvent){
7341         this.config[col].width = width;
7342         this.totalWidth = null;
7343         if(!suppressEvent){
7344              this.fireEvent("widthchange", this, col, width);
7345         }
7346     },
7347
7348     /**
7349      * Returns the total width of all columns.
7350      * @param {Boolean} includeHidden True to include hidden column widths
7351      * @return {Number}
7352      */
7353     getTotalWidth : function(includeHidden){
7354         if(!this.totalWidth){
7355             this.totalWidth = 0;
7356             for(var i = 0, len = this.config.length; i < len; i++){
7357                 if(includeHidden || !this.isHidden(i)){
7358                     this.totalWidth += this.getColumnWidth(i);
7359                 }
7360             }
7361         }
7362         return this.totalWidth;
7363     },
7364
7365     /**
7366      * Returns the header for the specified column.
7367      * @param {Number} col The column index
7368      * @return {String}
7369      */
7370     getColumnHeader : function(col){
7371         return this.config[col].header;
7372     },
7373
7374     /**
7375      * Sets the header for a column.
7376      * @param {Number} col The column index
7377      * @param {String} header The new header
7378      */
7379     setColumnHeader : function(col, header){
7380         this.config[col].header = header;
7381         this.fireEvent("headerchange", this, col, header);
7382     },
7383
7384     /**
7385      * Returns the tooltip for the specified column.
7386      * @param {Number} col The column index
7387      * @return {String}
7388      */
7389     getColumnTooltip : function(col){
7390             return this.config[col].tooltip;
7391     },
7392     /**
7393      * Sets the tooltip for a column.
7394      * @param {Number} col The column index
7395      * @param {String} tooltip The new tooltip
7396      */
7397     setColumnTooltip : function(col, tooltip){
7398             this.config[col].tooltip = tooltip;
7399     },
7400
7401     /**
7402      * Returns the dataIndex for the specified column.
7403      * @param {Number} col The column index
7404      * @return {Number}
7405      */
7406     getDataIndex : function(col){
7407         return this.config[col].dataIndex;
7408     },
7409
7410     /**
7411      * Sets the dataIndex for a column.
7412      * @param {Number} col The column index
7413      * @param {Number} dataIndex The new dataIndex
7414      */
7415     setDataIndex : function(col, dataIndex){
7416         this.config[col].dataIndex = dataIndex;
7417     },
7418
7419     
7420     
7421     /**
7422      * Returns true if the cell is editable.
7423      * @param {Number} colIndex The column index
7424      * @param {Number} rowIndex The row index - this is nto actually used..?
7425      * @return {Boolean}
7426      */
7427     isCellEditable : function(colIndex, rowIndex){
7428         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7429     },
7430
7431     /**
7432      * Returns the editor defined for the cell/column.
7433      * return false or null to disable editing.
7434      * @param {Number} colIndex The column index
7435      * @param {Number} rowIndex The row index
7436      * @return {Object}
7437      */
7438     getCellEditor : function(colIndex, rowIndex){
7439         return this.config[colIndex].editor;
7440     },
7441
7442     /**
7443      * Sets if a column is editable.
7444      * @param {Number} col The column index
7445      * @param {Boolean} editable True if the column is editable
7446      */
7447     setEditable : function(col, editable){
7448         this.config[col].editable = editable;
7449     },
7450
7451
7452     /**
7453      * Returns true if the column is hidden.
7454      * @param {Number} colIndex The column index
7455      * @return {Boolean}
7456      */
7457     isHidden : function(colIndex){
7458         return this.config[colIndex].hidden;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column width cannot be changed
7464      */
7465     isFixed : function(colIndex){
7466         return this.config[colIndex].fixed;
7467     },
7468
7469     /**
7470      * Returns true if the column can be resized
7471      * @return {Boolean}
7472      */
7473     isResizable : function(colIndex){
7474         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7475     },
7476     /**
7477      * Sets if a column is hidden.
7478      * @param {Number} colIndex The column index
7479      * @param {Boolean} hidden True if the column is hidden
7480      */
7481     setHidden : function(colIndex, hidden){
7482         this.config[colIndex].hidden = hidden;
7483         this.totalWidth = null;
7484         this.fireEvent("hiddenchange", this, colIndex, hidden);
7485     },
7486
7487     /**
7488      * Sets the editor for a column.
7489      * @param {Number} col The column index
7490      * @param {Object} editor The editor object
7491      */
7492     setEditor : function(col, editor){
7493         this.config[col].editor = editor;
7494     }
7495 });
7496
7497 Roo.grid.ColumnModel.defaultRenderer = function(value)
7498 {
7499     if(typeof value == "object") {
7500         return value;
7501     }
7502         if(typeof value == "string" && value.length < 1){
7503             return "&#160;";
7504         }
7505     
7506         return String.format("{0}", value);
7507 };
7508
7509 // Alias for backwards compatibility
7510 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7511 /*
7512  * Based on:
7513  * Ext JS Library 1.1.1
7514  * Copyright(c) 2006-2007, Ext JS, LLC.
7515  *
7516  * Originally Released Under LGPL - original licence link has changed is not relivant.
7517  *
7518  * Fork - LGPL
7519  * <script type="text/javascript">
7520  */
7521  
7522 /**
7523  * @class Roo.LoadMask
7524  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7525  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7526  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7527  * element's UpdateManager load indicator and will be destroyed after the initial load.
7528  * @constructor
7529  * Create a new LoadMask
7530  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7531  * @param {Object} config The config object
7532  */
7533 Roo.LoadMask = function(el, config){
7534     this.el = Roo.get(el);
7535     Roo.apply(this, config);
7536     if(this.store){
7537         this.store.on('beforeload', this.onBeforeLoad, this);
7538         this.store.on('load', this.onLoad, this);
7539         this.store.on('loadexception', this.onLoadException, this);
7540         this.removeMask = false;
7541     }else{
7542         var um = this.el.getUpdateManager();
7543         um.showLoadIndicator = false; // disable the default indicator
7544         um.on('beforeupdate', this.onBeforeLoad, this);
7545         um.on('update', this.onLoad, this);
7546         um.on('failure', this.onLoad, this);
7547         this.removeMask = true;
7548     }
7549 };
7550
7551 Roo.LoadMask.prototype = {
7552     /**
7553      * @cfg {Boolean} removeMask
7554      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7555      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7556      */
7557     /**
7558      * @cfg {String} msg
7559      * The text to display in a centered loading message box (defaults to 'Loading...')
7560      */
7561     msg : 'Loading...',
7562     /**
7563      * @cfg {String} msgCls
7564      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7565      */
7566     msgCls : 'x-mask-loading',
7567
7568     /**
7569      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7570      * @type Boolean
7571      */
7572     disabled: false,
7573
7574     /**
7575      * Disables the mask to prevent it from being displayed
7576      */
7577     disable : function(){
7578        this.disabled = true;
7579     },
7580
7581     /**
7582      * Enables the mask so that it can be displayed
7583      */
7584     enable : function(){
7585         this.disabled = false;
7586     },
7587     
7588     onLoadException : function()
7589     {
7590         Roo.log(arguments);
7591         
7592         if (typeof(arguments[3]) != 'undefined') {
7593             Roo.MessageBox.alert("Error loading",arguments[3]);
7594         } 
7595         /*
7596         try {
7597             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7598                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7599             }   
7600         } catch(e) {
7601             
7602         }
7603         */
7604     
7605         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7606     },
7607     // private
7608     onLoad : function()
7609     {
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612
7613     // private
7614     onBeforeLoad : function(){
7615         if(!this.disabled){
7616             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7617         }
7618     },
7619
7620     // private
7621     destroy : function(){
7622         if(this.store){
7623             this.store.un('beforeload', this.onBeforeLoad, this);
7624             this.store.un('load', this.onLoad, this);
7625             this.store.un('loadexception', this.onLoadException, this);
7626         }else{
7627             var um = this.el.getUpdateManager();
7628             um.un('beforeupdate', this.onBeforeLoad, this);
7629             um.un('update', this.onLoad, this);
7630             um.un('failure', this.onLoad, this);
7631         }
7632     }
7633 };/*
7634  * - LGPL
7635  *
7636  * table
7637  * 
7638  */
7639
7640 /**
7641  * @class Roo.bootstrap.Table
7642  * @extends Roo.bootstrap.Component
7643  * Bootstrap Table class
7644  * @cfg {String} cls table class
7645  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7646  * @cfg {String} bgcolor Specifies the background color for a table
7647  * @cfg {Number} border Specifies whether the table cells should have borders or not
7648  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7649  * @cfg {Number} cellspacing Specifies the space between cells
7650  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7651  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7652  * @cfg {String} sortable Specifies that the table should be sortable
7653  * @cfg {String} summary Specifies a summary of the content of a table
7654  * @cfg {Number} width Specifies the width of a table
7655  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7656  * 
7657  * @cfg {boolean} striped Should the rows be alternative striped
7658  * @cfg {boolean} bordered Add borders to the table
7659  * @cfg {boolean} hover Add hover highlighting
7660  * @cfg {boolean} condensed Format condensed
7661  * @cfg {boolean} responsive Format condensed
7662  * @cfg {Boolean} loadMask (true|false) default false
7663  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7664  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7665  * @cfg {Boolean} rowSelection (true|false) default false
7666  * @cfg {Boolean} cellSelection (true|false) default false
7667  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7668  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7669  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7670  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7671  
7672  * 
7673  * @constructor
7674  * Create a new Table
7675  * @param {Object} config The config object
7676  */
7677
7678 Roo.bootstrap.Table = function(config){
7679     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7680     
7681   
7682     
7683     // BC...
7684     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7685     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7686     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7687     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7688     
7689     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7690     if (this.sm) {
7691         this.sm.grid = this;
7692         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7693         this.sm = this.selModel;
7694         this.sm.xmodule = this.xmodule || false;
7695     }
7696     
7697     if (this.cm && typeof(this.cm.config) == 'undefined') {
7698         this.colModel = new Roo.grid.ColumnModel(this.cm);
7699         this.cm = this.colModel;
7700         this.cm.xmodule = this.xmodule || false;
7701     }
7702     if (this.store) {
7703         this.store= Roo.factory(this.store, Roo.data);
7704         this.ds = this.store;
7705         this.ds.xmodule = this.xmodule || false;
7706          
7707     }
7708     if (this.footer && this.store) {
7709         this.footer.dataSource = this.ds;
7710         this.footer = Roo.factory(this.footer);
7711     }
7712     
7713     /** @private */
7714     this.addEvents({
7715         /**
7716          * @event cellclick
7717          * Fires when a cell is clicked
7718          * @param {Roo.bootstrap.Table} this
7719          * @param {Roo.Element} el
7720          * @param {Number} rowIndex
7721          * @param {Number} columnIndex
7722          * @param {Roo.EventObject} e
7723          */
7724         "cellclick" : true,
7725         /**
7726          * @event celldblclick
7727          * Fires when a cell is double clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "celldblclick" : true,
7735         /**
7736          * @event rowclick
7737          * Fires when a row is clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Roo.EventObject} e
7742          */
7743         "rowclick" : true,
7744         /**
7745          * @event rowdblclick
7746          * Fires when a row is double clicked
7747          * @param {Roo.bootstrap.Table} this
7748          * @param {Roo.Element} el
7749          * @param {Number} rowIndex
7750          * @param {Roo.EventObject} e
7751          */
7752         "rowdblclick" : true,
7753         /**
7754          * @event mouseover
7755          * Fires when a mouseover occur
7756          * @param {Roo.bootstrap.Table} this
7757          * @param {Roo.Element} el
7758          * @param {Number} rowIndex
7759          * @param {Number} columnIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "mouseover" : true,
7763         /**
7764          * @event mouseout
7765          * Fires when a mouseout occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseout" : true,
7773         /**
7774          * @event rowclass
7775          * Fires when a row is rendered, so you can change add a style to it.
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7778          */
7779         'rowclass' : true,
7780           /**
7781          * @event rowsrendered
7782          * Fires when all the  rows have been rendered
7783          * @param {Roo.bootstrap.Table} this
7784          */
7785         'rowsrendered' : true,
7786         /**
7787          * @event contextmenu
7788          * The raw contextmenu event for the entire grid.
7789          * @param {Roo.EventObject} e
7790          */
7791         "contextmenu" : true,
7792         /**
7793          * @event rowcontextmenu
7794          * Fires when a row is right clicked
7795          * @param {Roo.bootstrap.Table} this
7796          * @param {Number} rowIndex
7797          * @param {Roo.EventObject} e
7798          */
7799         "rowcontextmenu" : true,
7800         /**
7801          * @event cellcontextmenu
7802          * Fires when a cell is right clicked
7803          * @param {Roo.bootstrap.Table} this
7804          * @param {Number} rowIndex
7805          * @param {Number} cellIndex
7806          * @param {Roo.EventObject} e
7807          */
7808          "cellcontextmenu" : true,
7809          /**
7810          * @event headercontextmenu
7811          * Fires when a header is right clicked
7812          * @param {Roo.bootstrap.Table} this
7813          * @param {Number} columnIndex
7814          * @param {Roo.EventObject} e
7815          */
7816         "headercontextmenu" : true
7817     });
7818 };
7819
7820 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7821     
7822     cls: false,
7823     align: false,
7824     bgcolor: false,
7825     border: false,
7826     cellpadding: false,
7827     cellspacing: false,
7828     frame: false,
7829     rules: false,
7830     sortable: false,
7831     summary: false,
7832     width: false,
7833     striped : false,
7834     scrollBody : false,
7835     bordered: false,
7836     hover:  false,
7837     condensed : false,
7838     responsive : false,
7839     sm : false,
7840     cm : false,
7841     store : false,
7842     loadMask : false,
7843     footerShow : true,
7844     headerShow : true,
7845   
7846     rowSelection : false,
7847     cellSelection : false,
7848     layout : false,
7849     
7850     // Roo.Element - the tbody
7851     mainBody: false,
7852     // Roo.Element - thead element
7853     mainHead: false,
7854     
7855     container: false, // used by gridpanel...
7856     
7857     lazyLoad : false,
7858     
7859     CSS : Roo.util.CSS,
7860     
7861     auto_hide_footer : false,
7862     
7863     getAutoCreate : function()
7864     {
7865         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7866         
7867         cfg = {
7868             tag: 'table',
7869             cls : 'table',
7870             cn : []
7871         };
7872         if (this.scrollBody) {
7873             cfg.cls += ' table-body-fixed';
7874         }    
7875         if (this.striped) {
7876             cfg.cls += ' table-striped';
7877         }
7878         
7879         if (this.hover) {
7880             cfg.cls += ' table-hover';
7881         }
7882         if (this.bordered) {
7883             cfg.cls += ' table-bordered';
7884         }
7885         if (this.condensed) {
7886             cfg.cls += ' table-condensed';
7887         }
7888         if (this.responsive) {
7889             cfg.cls += ' table-responsive';
7890         }
7891         
7892         if (this.cls) {
7893             cfg.cls+=  ' ' +this.cls;
7894         }
7895         
7896         // this lot should be simplifed...
7897         var _t = this;
7898         var cp = [
7899             'align',
7900             'bgcolor',
7901             'border',
7902             'cellpadding',
7903             'cellspacing',
7904             'frame',
7905             'rules',
7906             'sortable',
7907             'summary',
7908             'width'
7909         ].forEach(function(k) {
7910             if (_t[k]) {
7911                 cfg[k] = _t[k];
7912             }
7913         });
7914         
7915         
7916         if (this.layout) {
7917             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7918         }
7919         
7920         if(this.store || this.cm){
7921             if(this.headerShow){
7922                 cfg.cn.push(this.renderHeader());
7923             }
7924             
7925             cfg.cn.push(this.renderBody());
7926             
7927             if(this.footerShow){
7928                 cfg.cn.push(this.renderFooter());
7929             }
7930             // where does this come from?
7931             //cfg.cls+=  ' TableGrid';
7932         }
7933         
7934         return { cn : [ cfg ] };
7935     },
7936     
7937     initEvents : function()
7938     {   
7939         if(!this.store || !this.cm){
7940             return;
7941         }
7942         if (this.selModel) {
7943             this.selModel.initEvents();
7944         }
7945         
7946         
7947         //Roo.log('initEvents with ds!!!!');
7948         
7949         this.mainBody = this.el.select('tbody', true).first();
7950         this.mainHead = this.el.select('thead', true).first();
7951         this.mainFoot = this.el.select('tfoot', true).first();
7952         
7953         
7954         
7955         var _this = this;
7956         
7957         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7958             e.on('click', _this.sort, _this);
7959         });
7960         
7961         this.mainBody.on("click", this.onClick, this);
7962         this.mainBody.on("dblclick", this.onDblClick, this);
7963         
7964         // why is this done????? = it breaks dialogs??
7965         //this.parent().el.setStyle('position', 'relative');
7966         
7967         
7968         if (this.footer) {
7969             this.footer.parentId = this.id;
7970             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7971             
7972             if(this.lazyLoad){
7973                 this.el.select('tfoot tr td').first().addClass('hide');
7974             }
7975         } 
7976         
7977         if(this.loadMask) {
7978             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7979         }
7980         
7981         this.store.on('load', this.onLoad, this);
7982         this.store.on('beforeload', this.onBeforeLoad, this);
7983         this.store.on('update', this.onUpdate, this);
7984         this.store.on('add', this.onAdd, this);
7985         this.store.on("clear", this.clear, this);
7986         
7987         this.el.on("contextmenu", this.onContextMenu, this);
7988         
7989         this.mainBody.on('scroll', this.onBodyScroll, this);
7990         
7991         this.cm.on("headerchange", this.onHeaderChange, this);
7992         
7993         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7994         
7995     },
7996     
7997     onContextMenu : function(e, t)
7998     {
7999         this.processEvent("contextmenu", e);
8000     },
8001     
8002     processEvent : function(name, e)
8003     {
8004         if (name != 'touchstart' ) {
8005             this.fireEvent(name, e);    
8006         }
8007         
8008         var t = e.getTarget();
8009         
8010         var cell = Roo.get(t);
8011         
8012         if(!cell){
8013             return;
8014         }
8015         
8016         if(cell.findParent('tfoot', false, true)){
8017             return;
8018         }
8019         
8020         if(cell.findParent('thead', false, true)){
8021             
8022             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8023                 cell = Roo.get(t).findParent('th', false, true);
8024                 if (!cell) {
8025                     Roo.log("failed to find th in thead?");
8026                     Roo.log(e.getTarget());
8027                     return;
8028                 }
8029             }
8030             
8031             var cellIndex = cell.dom.cellIndex;
8032             
8033             var ename = name == 'touchstart' ? 'click' : name;
8034             this.fireEvent("header" + ename, this, cellIndex, e);
8035             
8036             return;
8037         }
8038         
8039         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8040             cell = Roo.get(t).findParent('td', false, true);
8041             if (!cell) {
8042                 Roo.log("failed to find th in tbody?");
8043                 Roo.log(e.getTarget());
8044                 return;
8045             }
8046         }
8047         
8048         var row = cell.findParent('tr', false, true);
8049         var cellIndex = cell.dom.cellIndex;
8050         var rowIndex = row.dom.rowIndex - 1;
8051         
8052         if(row !== false){
8053             
8054             this.fireEvent("row" + name, this, rowIndex, e);
8055             
8056             if(cell !== false){
8057             
8058                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8059             }
8060         }
8061         
8062     },
8063     
8064     onMouseover : function(e, el)
8065     {
8066         var cell = Roo.get(el);
8067         
8068         if(!cell){
8069             return;
8070         }
8071         
8072         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8073             cell = cell.findParent('td', false, true);
8074         }
8075         
8076         var row = cell.findParent('tr', false, true);
8077         var cellIndex = cell.dom.cellIndex;
8078         var rowIndex = row.dom.rowIndex - 1; // start from 0
8079         
8080         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8081         
8082     },
8083     
8084     onMouseout : function(e, el)
8085     {
8086         var cell = Roo.get(el);
8087         
8088         if(!cell){
8089             return;
8090         }
8091         
8092         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8093             cell = cell.findParent('td', false, true);
8094         }
8095         
8096         var row = cell.findParent('tr', false, true);
8097         var cellIndex = cell.dom.cellIndex;
8098         var rowIndex = row.dom.rowIndex - 1; // start from 0
8099         
8100         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8101         
8102     },
8103     
8104     onClick : function(e, el)
8105     {
8106         var cell = Roo.get(el);
8107         
8108         if(!cell || (!this.cellSelection && !this.rowSelection)){
8109             return;
8110         }
8111         
8112         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8113             cell = cell.findParent('td', false, true);
8114         }
8115         
8116         if(!cell || typeof(cell) == 'undefined'){
8117             return;
8118         }
8119         
8120         var row = cell.findParent('tr', false, true);
8121         
8122         if(!row || typeof(row) == 'undefined'){
8123             return;
8124         }
8125         
8126         var cellIndex = cell.dom.cellIndex;
8127         var rowIndex = this.getRowIndex(row);
8128         
8129         // why??? - should these not be based on SelectionModel?
8130         if(this.cellSelection){
8131             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8132         }
8133         
8134         if(this.rowSelection){
8135             this.fireEvent('rowclick', this, row, rowIndex, e);
8136         }
8137         
8138         
8139     },
8140         
8141     onDblClick : function(e,el)
8142     {
8143         var cell = Roo.get(el);
8144         
8145         if(!cell || (!this.cellSelection && !this.rowSelection)){
8146             return;
8147         }
8148         
8149         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8150             cell = cell.findParent('td', false, true);
8151         }
8152         
8153         if(!cell || typeof(cell) == 'undefined'){
8154             return;
8155         }
8156         
8157         var row = cell.findParent('tr', false, true);
8158         
8159         if(!row || typeof(row) == 'undefined'){
8160             return;
8161         }
8162         
8163         var cellIndex = cell.dom.cellIndex;
8164         var rowIndex = this.getRowIndex(row);
8165         
8166         if(this.cellSelection){
8167             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8168         }
8169         
8170         if(this.rowSelection){
8171             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8172         }
8173     },
8174     
8175     sort : function(e,el)
8176     {
8177         var col = Roo.get(el);
8178         
8179         if(!col.hasClass('sortable')){
8180             return;
8181         }
8182         
8183         var sort = col.attr('sort');
8184         var dir = 'ASC';
8185         
8186         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8187             dir = 'DESC';
8188         }
8189         
8190         this.store.sortInfo = {field : sort, direction : dir};
8191         
8192         if (this.footer) {
8193             Roo.log("calling footer first");
8194             this.footer.onClick('first');
8195         } else {
8196         
8197             this.store.load({ params : { start : 0 } });
8198         }
8199     },
8200     
8201     renderHeader : function()
8202     {
8203         var header = {
8204             tag: 'thead',
8205             cn : []
8206         };
8207         
8208         var cm = this.cm;
8209         this.totalWidth = 0;
8210         
8211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8212             
8213             var config = cm.config[i];
8214             
8215             var c = {
8216                 tag: 'th',
8217                 cls : 'x-hcol-' + i,
8218                 style : '',
8219                 html: cm.getColumnHeader(i)
8220             };
8221             
8222             var hh = '';
8223             
8224             if(typeof(config.sortable) != 'undefined' && config.sortable){
8225                 c.cls = 'sortable';
8226                 c.html = '<i class="glyphicon"></i>' + c.html;
8227             }
8228             
8229             // could use BS4 hidden-..-down 
8230             
8231             if(typeof(config.lgHeader) != 'undefined'){
8232                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8233             }
8234             
8235             if(typeof(config.mdHeader) != 'undefined'){
8236                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8237             }
8238             
8239             if(typeof(config.smHeader) != 'undefined'){
8240                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8241             }
8242             
8243             if(typeof(config.xsHeader) != 'undefined'){
8244                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8245             }
8246             
8247             if(hh.length){
8248                 c.html = hh;
8249             }
8250             
8251             if(typeof(config.tooltip) != 'undefined'){
8252                 c.tooltip = config.tooltip;
8253             }
8254             
8255             if(typeof(config.colspan) != 'undefined'){
8256                 c.colspan = config.colspan;
8257             }
8258             
8259             if(typeof(config.hidden) != 'undefined' && config.hidden){
8260                 c.style += ' display:none;';
8261             }
8262             
8263             if(typeof(config.dataIndex) != 'undefined'){
8264                 c.sort = config.dataIndex;
8265             }
8266             
8267            
8268             
8269             if(typeof(config.align) != 'undefined' && config.align.length){
8270                 c.style += ' text-align:' + config.align + ';';
8271             }
8272             
8273             if(typeof(config.width) != 'undefined'){
8274                 c.style += ' width:' + config.width + 'px;';
8275                 this.totalWidth += config.width;
8276             } else {
8277                 this.totalWidth += 100; // assume minimum of 100 per column?
8278             }
8279             
8280             if(typeof(config.cls) != 'undefined'){
8281                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8282             }
8283             
8284             ['xs','sm','md','lg'].map(function(size){
8285                 
8286                 if(typeof(config[size]) == 'undefined'){
8287                     return;
8288                 }
8289                  
8290                 if (!config[size]) { // 0 = hidden
8291                     // BS 4 '0' is treated as hide that column and below.
8292                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8293                     return;
8294                 }
8295                 
8296                 c.cls += ' col-' + size + '-' + config[size] + (
8297                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8298                 );
8299                 
8300                 
8301             });
8302             
8303             header.cn.push(c)
8304         }
8305         
8306         return header;
8307     },
8308     
8309     renderBody : function()
8310     {
8311         var body = {
8312             tag: 'tbody',
8313             cn : [
8314                 {
8315                     tag: 'tr',
8316                     cn : [
8317                         {
8318                             tag : 'td',
8319                             colspan :  this.cm.getColumnCount()
8320                         }
8321                     ]
8322                 }
8323             ]
8324         };
8325         
8326         return body;
8327     },
8328     
8329     renderFooter : function()
8330     {
8331         var footer = {
8332             tag: 'tfoot',
8333             cn : [
8334                 {
8335                     tag: 'tr',
8336                     cn : [
8337                         {
8338                             tag : 'td',
8339                             colspan :  this.cm.getColumnCount()
8340                         }
8341                     ]
8342                 }
8343             ]
8344         };
8345         
8346         return footer;
8347     },
8348     
8349     
8350     
8351     onLoad : function()
8352     {
8353 //        Roo.log('ds onload');
8354         this.clear();
8355         
8356         var _this = this;
8357         var cm = this.cm;
8358         var ds = this.store;
8359         
8360         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8361             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8362             if (_this.store.sortInfo) {
8363                     
8364                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8365                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8366                 }
8367                 
8368                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8369                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8370                 }
8371             }
8372         });
8373         
8374         var tbody =  this.mainBody;
8375               
8376         if(ds.getCount() > 0){
8377             ds.data.each(function(d,rowIndex){
8378                 var row =  this.renderRow(cm, ds, rowIndex);
8379                 
8380                 tbody.createChild(row);
8381                 
8382                 var _this = this;
8383                 
8384                 if(row.cellObjects.length){
8385                     Roo.each(row.cellObjects, function(r){
8386                         _this.renderCellObject(r);
8387                     })
8388                 }
8389                 
8390             }, this);
8391         }
8392         
8393         var tfoot = this.el.select('tfoot', true).first();
8394         
8395         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8396             
8397             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8398             
8399             var total = this.ds.getTotalCount();
8400             
8401             if(this.footer.pageSize < total){
8402                 this.mainFoot.show();
8403             }
8404         }
8405         
8406         Roo.each(this.el.select('tbody td', true).elements, function(e){
8407             e.on('mouseover', _this.onMouseover, _this);
8408         });
8409         
8410         Roo.each(this.el.select('tbody td', true).elements, function(e){
8411             e.on('mouseout', _this.onMouseout, _this);
8412         });
8413         this.fireEvent('rowsrendered', this);
8414         
8415         this.autoSize();
8416     },
8417     
8418     
8419     onUpdate : function(ds,record)
8420     {
8421         this.refreshRow(record);
8422         this.autoSize();
8423     },
8424     
8425     onRemove : function(ds, record, index, isUpdate){
8426         if(isUpdate !== true){
8427             this.fireEvent("beforerowremoved", this, index, record);
8428         }
8429         var bt = this.mainBody.dom;
8430         
8431         var rows = this.el.select('tbody > tr', true).elements;
8432         
8433         if(typeof(rows[index]) != 'undefined'){
8434             bt.removeChild(rows[index].dom);
8435         }
8436         
8437 //        if(bt.rows[index]){
8438 //            bt.removeChild(bt.rows[index]);
8439 //        }
8440         
8441         if(isUpdate !== true){
8442             //this.stripeRows(index);
8443             //this.syncRowHeights(index, index);
8444             //this.layout();
8445             this.fireEvent("rowremoved", this, index, record);
8446         }
8447     },
8448     
8449     onAdd : function(ds, records, rowIndex)
8450     {
8451         //Roo.log('on Add called');
8452         // - note this does not handle multiple adding very well..
8453         var bt = this.mainBody.dom;
8454         for (var i =0 ; i < records.length;i++) {
8455             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8456             //Roo.log(records[i]);
8457             //Roo.log(this.store.getAt(rowIndex+i));
8458             this.insertRow(this.store, rowIndex + i, false);
8459             return;
8460         }
8461         
8462     },
8463     
8464     
8465     refreshRow : function(record){
8466         var ds = this.store, index;
8467         if(typeof record == 'number'){
8468             index = record;
8469             record = ds.getAt(index);
8470         }else{
8471             index = ds.indexOf(record);
8472             if (index < 0) {
8473                 return; // should not happen - but seems to 
8474             }
8475         }
8476         this.insertRow(ds, index, true);
8477         this.autoSize();
8478         this.onRemove(ds, record, index+1, true);
8479         this.autoSize();
8480         //this.syncRowHeights(index, index);
8481         //this.layout();
8482         this.fireEvent("rowupdated", this, index, record);
8483     },
8484     
8485     insertRow : function(dm, rowIndex, isUpdate){
8486         
8487         if(!isUpdate){
8488             this.fireEvent("beforerowsinserted", this, rowIndex);
8489         }
8490             //var s = this.getScrollState();
8491         var row = this.renderRow(this.cm, this.store, rowIndex);
8492         // insert before rowIndex..
8493         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8494         
8495         var _this = this;
8496                 
8497         if(row.cellObjects.length){
8498             Roo.each(row.cellObjects, function(r){
8499                 _this.renderCellObject(r);
8500             })
8501         }
8502             
8503         if(!isUpdate){
8504             this.fireEvent("rowsinserted", this, rowIndex);
8505             //this.syncRowHeights(firstRow, lastRow);
8506             //this.stripeRows(firstRow);
8507             //this.layout();
8508         }
8509         
8510     },
8511     
8512     
8513     getRowDom : function(rowIndex)
8514     {
8515         var rows = this.el.select('tbody > tr', true).elements;
8516         
8517         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8518         
8519     },
8520     // returns the object tree for a tr..
8521   
8522     
8523     renderRow : function(cm, ds, rowIndex) 
8524     {
8525         var d = ds.getAt(rowIndex);
8526         
8527         var row = {
8528             tag : 'tr',
8529             cls : 'x-row-' + rowIndex,
8530             cn : []
8531         };
8532             
8533         var cellObjects = [];
8534         
8535         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8536             var config = cm.config[i];
8537             
8538             var renderer = cm.getRenderer(i);
8539             var value = '';
8540             var id = false;
8541             
8542             if(typeof(renderer) !== 'undefined'){
8543                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8544             }
8545             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8546             // and are rendered into the cells after the row is rendered - using the id for the element.
8547             
8548             if(typeof(value) === 'object'){
8549                 id = Roo.id();
8550                 cellObjects.push({
8551                     container : id,
8552                     cfg : value 
8553                 })
8554             }
8555             
8556             var rowcfg = {
8557                 record: d,
8558                 rowIndex : rowIndex,
8559                 colIndex : i,
8560                 rowClass : ''
8561             };
8562
8563             this.fireEvent('rowclass', this, rowcfg);
8564             
8565             var td = {
8566                 tag: 'td',
8567                 cls : rowcfg.rowClass + ' x-col-' + i,
8568                 style: '',
8569                 html: (typeof(value) === 'object') ? '' : value
8570             };
8571             
8572             if (id) {
8573                 td.id = id;
8574             }
8575             
8576             if(typeof(config.colspan) != 'undefined'){
8577                 td.colspan = config.colspan;
8578             }
8579             
8580             if(typeof(config.hidden) != 'undefined' && config.hidden){
8581                 td.style += ' display:none;';
8582             }
8583             
8584             if(typeof(config.align) != 'undefined' && config.align.length){
8585                 td.style += ' text-align:' + config.align + ';';
8586             }
8587             if(typeof(config.valign) != 'undefined' && config.valign.length){
8588                 td.style += ' vertical-align:' + config.valign + ';';
8589             }
8590             
8591             if(typeof(config.width) != 'undefined'){
8592                 td.style += ' width:' +  config.width + 'px;';
8593             }
8594             
8595             if(typeof(config.cursor) != 'undefined'){
8596                 td.style += ' cursor:' +  config.cursor + ';';
8597             }
8598             
8599             if(typeof(config.cls) != 'undefined'){
8600                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8601             }
8602             
8603             ['xs','sm','md','lg'].map(function(size){
8604                 
8605                 if(typeof(config[size]) == 'undefined'){
8606                     return;
8607                 }
8608                 
8609                 
8610                   
8611                 if (!config[size]) { // 0 = hidden
8612                     // BS 4 '0' is treated as hide that column and below.
8613                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8614                     return;
8615                 }
8616                 
8617                 td.cls += ' col-' + size + '-' + config[size] + (
8618                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8619                 );
8620                  
8621
8622             });
8623             
8624             row.cn.push(td);
8625            
8626         }
8627         
8628         row.cellObjects = cellObjects;
8629         
8630         return row;
8631           
8632     },
8633     
8634     
8635     
8636     onBeforeLoad : function()
8637     {
8638         
8639     },
8640      /**
8641      * Remove all rows
8642      */
8643     clear : function()
8644     {
8645         this.el.select('tbody', true).first().dom.innerHTML = '';
8646     },
8647     /**
8648      * Show or hide a row.
8649      * @param {Number} rowIndex to show or hide
8650      * @param {Boolean} state hide
8651      */
8652     setRowVisibility : function(rowIndex, state)
8653     {
8654         var bt = this.mainBody.dom;
8655         
8656         var rows = this.el.select('tbody > tr', true).elements;
8657         
8658         if(typeof(rows[rowIndex]) == 'undefined'){
8659             return;
8660         }
8661         rows[rowIndex].dom.style.display = state ? '' : 'none';
8662     },
8663     
8664     
8665     getSelectionModel : function(){
8666         if(!this.selModel){
8667             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8668         }
8669         return this.selModel;
8670     },
8671     /*
8672      * Render the Roo.bootstrap object from renderder
8673      */
8674     renderCellObject : function(r)
8675     {
8676         var _this = this;
8677         
8678         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8679         
8680         var t = r.cfg.render(r.container);
8681         
8682         if(r.cfg.cn){
8683             Roo.each(r.cfg.cn, function(c){
8684                 var child = {
8685                     container: t.getChildContainer(),
8686                     cfg: c
8687                 };
8688                 _this.renderCellObject(child);
8689             })
8690         }
8691     },
8692     
8693     getRowIndex : function(row)
8694     {
8695         var rowIndex = -1;
8696         
8697         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8698             if(el != row){
8699                 return;
8700             }
8701             
8702             rowIndex = index;
8703         });
8704         
8705         return rowIndex;
8706     },
8707      /**
8708      * Returns the grid's underlying element = used by panel.Grid
8709      * @return {Element} The element
8710      */
8711     getGridEl : function(){
8712         return this.el;
8713     },
8714      /**
8715      * Forces a resize - used by panel.Grid
8716      * @return {Element} The element
8717      */
8718     autoSize : function()
8719     {
8720         //var ctr = Roo.get(this.container.dom.parentElement);
8721         var ctr = Roo.get(this.el.dom);
8722         
8723         var thd = this.getGridEl().select('thead',true).first();
8724         var tbd = this.getGridEl().select('tbody', true).first();
8725         var tfd = this.getGridEl().select('tfoot', true).first();
8726         
8727         var cw = ctr.getWidth();
8728         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8729         
8730         if (tbd) {
8731             
8732             tbd.setWidth(ctr.getWidth());
8733             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8734             // this needs fixing for various usage - currently only hydra job advers I think..
8735             //tdb.setHeight(
8736             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8737             //); 
8738             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8739             cw -= barsize;
8740         }
8741         cw = Math.max(cw, this.totalWidth);
8742         this.getGridEl().select('tbody tr',true).setWidth(cw);
8743         
8744         // resize 'expandable coloumn?
8745         
8746         return; // we doe not have a view in this design..
8747         
8748     },
8749     onBodyScroll: function()
8750     {
8751         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8752         if(this.mainHead){
8753             this.mainHead.setStyle({
8754                 'position' : 'relative',
8755                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8756             });
8757         }
8758         
8759         if(this.lazyLoad){
8760             
8761             var scrollHeight = this.mainBody.dom.scrollHeight;
8762             
8763             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8764             
8765             var height = this.mainBody.getHeight();
8766             
8767             if(scrollHeight - height == scrollTop) {
8768                 
8769                 var total = this.ds.getTotalCount();
8770                 
8771                 if(this.footer.cursor + this.footer.pageSize < total){
8772                     
8773                     this.footer.ds.load({
8774                         params : {
8775                             start : this.footer.cursor + this.footer.pageSize,
8776                             limit : this.footer.pageSize
8777                         },
8778                         add : true
8779                     });
8780                 }
8781             }
8782             
8783         }
8784     },
8785     
8786     onHeaderChange : function()
8787     {
8788         var header = this.renderHeader();
8789         var table = this.el.select('table', true).first();
8790         
8791         this.mainHead.remove();
8792         this.mainHead = table.createChild(header, this.mainBody, false);
8793     },
8794     
8795     onHiddenChange : function(colModel, colIndex, hidden)
8796     {
8797         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8798         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8799         
8800         this.CSS.updateRule(thSelector, "display", "");
8801         this.CSS.updateRule(tdSelector, "display", "");
8802         
8803         if(hidden){
8804             this.CSS.updateRule(thSelector, "display", "none");
8805             this.CSS.updateRule(tdSelector, "display", "none");
8806         }
8807         
8808         this.onHeaderChange();
8809         this.onLoad();
8810     },
8811     
8812     setColumnWidth: function(col_index, width)
8813     {
8814         // width = "md-2 xs-2..."
8815         if(!this.colModel.config[col_index]) {
8816             return;
8817         }
8818         
8819         var w = width.split(" ");
8820         
8821         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8822         
8823         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8824         
8825         
8826         for(var j = 0; j < w.length; j++) {
8827             
8828             if(!w[j]) {
8829                 continue;
8830             }
8831             
8832             var size_cls = w[j].split("-");
8833             
8834             if(!Number.isInteger(size_cls[1] * 1)) {
8835                 continue;
8836             }
8837             
8838             if(!this.colModel.config[col_index][size_cls[0]]) {
8839                 continue;
8840             }
8841             
8842             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8843                 continue;
8844             }
8845             
8846             h_row[0].classList.replace(
8847                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8848                 "col-"+size_cls[0]+"-"+size_cls[1]
8849             );
8850             
8851             for(var i = 0; i < rows.length; i++) {
8852                 
8853                 var size_cls = w[j].split("-");
8854                 
8855                 if(!Number.isInteger(size_cls[1] * 1)) {
8856                     continue;
8857                 }
8858                 
8859                 if(!this.colModel.config[col_index][size_cls[0]]) {
8860                     continue;
8861                 }
8862                 
8863                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8864                     continue;
8865                 }
8866                 
8867                 rows[i].classList.replace(
8868                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8869                     "col-"+size_cls[0]+"-"+size_cls[1]
8870                 );
8871             }
8872             
8873             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8874         }
8875     }
8876 });
8877
8878  
8879
8880  /*
8881  * - LGPL
8882  *
8883  * table cell
8884  * 
8885  */
8886
8887 /**
8888  * @class Roo.bootstrap.TableCell
8889  * @extends Roo.bootstrap.Component
8890  * Bootstrap TableCell class
8891  * @cfg {String} html cell contain text
8892  * @cfg {String} cls cell class
8893  * @cfg {String} tag cell tag (td|th) default td
8894  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8895  * @cfg {String} align Aligns the content in a cell
8896  * @cfg {String} axis Categorizes cells
8897  * @cfg {String} bgcolor Specifies the background color of a cell
8898  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8899  * @cfg {Number} colspan Specifies the number of columns a cell should span
8900  * @cfg {String} headers Specifies one or more header cells a cell is related to
8901  * @cfg {Number} height Sets the height of a cell
8902  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8903  * @cfg {Number} rowspan Sets the number of rows a cell should span
8904  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8905  * @cfg {String} valign Vertical aligns the content in a cell
8906  * @cfg {Number} width Specifies the width of a cell
8907  * 
8908  * @constructor
8909  * Create a new TableCell
8910  * @param {Object} config The config object
8911  */
8912
8913 Roo.bootstrap.TableCell = function(config){
8914     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8915 };
8916
8917 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8918     
8919     html: false,
8920     cls: false,
8921     tag: false,
8922     abbr: false,
8923     align: false,
8924     axis: false,
8925     bgcolor: false,
8926     charoff: false,
8927     colspan: false,
8928     headers: false,
8929     height: false,
8930     nowrap: false,
8931     rowspan: false,
8932     scope: false,
8933     valign: false,
8934     width: false,
8935     
8936     
8937     getAutoCreate : function(){
8938         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8939         
8940         cfg = {
8941             tag: 'td'
8942         };
8943         
8944         if(this.tag){
8945             cfg.tag = this.tag;
8946         }
8947         
8948         if (this.html) {
8949             cfg.html=this.html
8950         }
8951         if (this.cls) {
8952             cfg.cls=this.cls
8953         }
8954         if (this.abbr) {
8955             cfg.abbr=this.abbr
8956         }
8957         if (this.align) {
8958             cfg.align=this.align
8959         }
8960         if (this.axis) {
8961             cfg.axis=this.axis
8962         }
8963         if (this.bgcolor) {
8964             cfg.bgcolor=this.bgcolor
8965         }
8966         if (this.charoff) {
8967             cfg.charoff=this.charoff
8968         }
8969         if (this.colspan) {
8970             cfg.colspan=this.colspan
8971         }
8972         if (this.headers) {
8973             cfg.headers=this.headers
8974         }
8975         if (this.height) {
8976             cfg.height=this.height
8977         }
8978         if (this.nowrap) {
8979             cfg.nowrap=this.nowrap
8980         }
8981         if (this.rowspan) {
8982             cfg.rowspan=this.rowspan
8983         }
8984         if (this.scope) {
8985             cfg.scope=this.scope
8986         }
8987         if (this.valign) {
8988             cfg.valign=this.valign
8989         }
8990         if (this.width) {
8991             cfg.width=this.width
8992         }
8993         
8994         
8995         return cfg;
8996     }
8997    
8998 });
8999
9000  
9001
9002  /*
9003  * - LGPL
9004  *
9005  * table row
9006  * 
9007  */
9008
9009 /**
9010  * @class Roo.bootstrap.TableRow
9011  * @extends Roo.bootstrap.Component
9012  * Bootstrap TableRow class
9013  * @cfg {String} cls row class
9014  * @cfg {String} align Aligns the content in a table row
9015  * @cfg {String} bgcolor Specifies a background color for a table row
9016  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9017  * @cfg {String} valign Vertical aligns the content in a table row
9018  * 
9019  * @constructor
9020  * Create a new TableRow
9021  * @param {Object} config The config object
9022  */
9023
9024 Roo.bootstrap.TableRow = function(config){
9025     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9026 };
9027
9028 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9029     
9030     cls: false,
9031     align: false,
9032     bgcolor: false,
9033     charoff: false,
9034     valign: false,
9035     
9036     getAutoCreate : function(){
9037         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9038         
9039         cfg = {
9040             tag: 'tr'
9041         };
9042             
9043         if(this.cls){
9044             cfg.cls = this.cls;
9045         }
9046         if(this.align){
9047             cfg.align = this.align;
9048         }
9049         if(this.bgcolor){
9050             cfg.bgcolor = this.bgcolor;
9051         }
9052         if(this.charoff){
9053             cfg.charoff = this.charoff;
9054         }
9055         if(this.valign){
9056             cfg.valign = this.valign;
9057         }
9058         
9059         return cfg;
9060     }
9061    
9062 });
9063
9064  
9065
9066  /*
9067  * - LGPL
9068  *
9069  * table body
9070  * 
9071  */
9072
9073 /**
9074  * @class Roo.bootstrap.TableBody
9075  * @extends Roo.bootstrap.Component
9076  * Bootstrap TableBody class
9077  * @cfg {String} cls element class
9078  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9079  * @cfg {String} align Aligns the content inside the element
9080  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9081  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9082  * 
9083  * @constructor
9084  * Create a new TableBody
9085  * @param {Object} config The config object
9086  */
9087
9088 Roo.bootstrap.TableBody = function(config){
9089     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9090 };
9091
9092 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9093     
9094     cls: false,
9095     tag: false,
9096     align: false,
9097     charoff: false,
9098     valign: false,
9099     
9100     getAutoCreate : function(){
9101         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9102         
9103         cfg = {
9104             tag: 'tbody'
9105         };
9106             
9107         if (this.cls) {
9108             cfg.cls=this.cls
9109         }
9110         if(this.tag){
9111             cfg.tag = this.tag;
9112         }
9113         
9114         if(this.align){
9115             cfg.align = this.align;
9116         }
9117         if(this.charoff){
9118             cfg.charoff = this.charoff;
9119         }
9120         if(this.valign){
9121             cfg.valign = this.valign;
9122         }
9123         
9124         return cfg;
9125     }
9126     
9127     
9128 //    initEvents : function()
9129 //    {
9130 //        
9131 //        if(!this.store){
9132 //            return;
9133 //        }
9134 //        
9135 //        this.store = Roo.factory(this.store, Roo.data);
9136 //        this.store.on('load', this.onLoad, this);
9137 //        
9138 //        this.store.load();
9139 //        
9140 //    },
9141 //    
9142 //    onLoad: function () 
9143 //    {   
9144 //        this.fireEvent('load', this);
9145 //    }
9146 //    
9147 //   
9148 });
9149
9150  
9151
9152  /*
9153  * Based on:
9154  * Ext JS Library 1.1.1
9155  * Copyright(c) 2006-2007, Ext JS, LLC.
9156  *
9157  * Originally Released Under LGPL - original licence link has changed is not relivant.
9158  *
9159  * Fork - LGPL
9160  * <script type="text/javascript">
9161  */
9162
9163 // as we use this in bootstrap.
9164 Roo.namespace('Roo.form');
9165  /**
9166  * @class Roo.form.Action
9167  * Internal Class used to handle form actions
9168  * @constructor
9169  * @param {Roo.form.BasicForm} el The form element or its id
9170  * @param {Object} config Configuration options
9171  */
9172
9173  
9174  
9175 // define the action interface
9176 Roo.form.Action = function(form, options){
9177     this.form = form;
9178     this.options = options || {};
9179 };
9180 /**
9181  * Client Validation Failed
9182  * @const 
9183  */
9184 Roo.form.Action.CLIENT_INVALID = 'client';
9185 /**
9186  * Server Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.SERVER_INVALID = 'server';
9190  /**
9191  * Connect to Server Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CONNECT_FAILURE = 'connect';
9195 /**
9196  * Reading Data from Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.LOAD_FAILURE = 'load';
9200
9201 Roo.form.Action.prototype = {
9202     type : 'default',
9203     failureType : undefined,
9204     response : undefined,
9205     result : undefined,
9206
9207     // interface method
9208     run : function(options){
9209
9210     },
9211
9212     // interface method
9213     success : function(response){
9214
9215     },
9216
9217     // interface method
9218     handleResponse : function(response){
9219
9220     },
9221
9222     // default connection failure
9223     failure : function(response){
9224         
9225         this.response = response;
9226         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9227         this.form.afterAction(this, false);
9228     },
9229
9230     processResponse : function(response){
9231         this.response = response;
9232         if(!response.responseText){
9233             return true;
9234         }
9235         this.result = this.handleResponse(response);
9236         return this.result;
9237     },
9238
9239     // utility functions used internally
9240     getUrl : function(appendParams){
9241         var url = this.options.url || this.form.url || this.form.el.dom.action;
9242         if(appendParams){
9243             var p = this.getParams();
9244             if(p){
9245                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9246             }
9247         }
9248         return url;
9249     },
9250
9251     getMethod : function(){
9252         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9253     },
9254
9255     getParams : function(){
9256         var bp = this.form.baseParams;
9257         var p = this.options.params;
9258         if(p){
9259             if(typeof p == "object"){
9260                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9261             }else if(typeof p == 'string' && bp){
9262                 p += '&' + Roo.urlEncode(bp);
9263             }
9264         }else if(bp){
9265             p = Roo.urlEncode(bp);
9266         }
9267         return p;
9268     },
9269
9270     createCallback : function(){
9271         return {
9272             success: this.success,
9273             failure: this.failure,
9274             scope: this,
9275             timeout: (this.form.timeout*1000),
9276             upload: this.form.fileUpload ? this.success : undefined
9277         };
9278     }
9279 };
9280
9281 Roo.form.Action.Submit = function(form, options){
9282     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9283 };
9284
9285 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9286     type : 'submit',
9287
9288     haveProgress : false,
9289     uploadComplete : false,
9290     
9291     // uploadProgress indicator.
9292     uploadProgress : function()
9293     {
9294         if (!this.form.progressUrl) {
9295             return;
9296         }
9297         
9298         if (!this.haveProgress) {
9299             Roo.MessageBox.progress("Uploading", "Uploading");
9300         }
9301         if (this.uploadComplete) {
9302            Roo.MessageBox.hide();
9303            return;
9304         }
9305         
9306         this.haveProgress = true;
9307    
9308         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9309         
9310         var c = new Roo.data.Connection();
9311         c.request({
9312             url : this.form.progressUrl,
9313             params: {
9314                 id : uid
9315             },
9316             method: 'GET',
9317             success : function(req){
9318                //console.log(data);
9319                 var rdata = false;
9320                 var edata;
9321                 try  {
9322                    rdata = Roo.decode(req.responseText)
9323                 } catch (e) {
9324                     Roo.log("Invalid data from server..");
9325                     Roo.log(edata);
9326                     return;
9327                 }
9328                 if (!rdata || !rdata.success) {
9329                     Roo.log(rdata);
9330                     Roo.MessageBox.alert(Roo.encode(rdata));
9331                     return;
9332                 }
9333                 var data = rdata.data;
9334                 
9335                 if (this.uploadComplete) {
9336                    Roo.MessageBox.hide();
9337                    return;
9338                 }
9339                    
9340                 if (data){
9341                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9342                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9343                     );
9344                 }
9345                 this.uploadProgress.defer(2000,this);
9346             },
9347        
9348             failure: function(data) {
9349                 Roo.log('progress url failed ');
9350                 Roo.log(data);
9351             },
9352             scope : this
9353         });
9354            
9355     },
9356     
9357     
9358     run : function()
9359     {
9360         // run get Values on the form, so it syncs any secondary forms.
9361         this.form.getValues();
9362         
9363         var o = this.options;
9364         var method = this.getMethod();
9365         var isPost = method == 'POST';
9366         if(o.clientValidation === false || this.form.isValid()){
9367             
9368             if (this.form.progressUrl) {
9369                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9370                     (new Date() * 1) + '' + Math.random());
9371                     
9372             } 
9373             
9374             
9375             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9376                 form:this.form.el.dom,
9377                 url:this.getUrl(!isPost),
9378                 method: method,
9379                 params:isPost ? this.getParams() : null,
9380                 isUpload: this.form.fileUpload,
9381                 formData : this.form.formData
9382             }));
9383             
9384             this.uploadProgress();
9385
9386         }else if (o.clientValidation !== false){ // client validation failed
9387             this.failureType = Roo.form.Action.CLIENT_INVALID;
9388             this.form.afterAction(this, false);
9389         }
9390     },
9391
9392     success : function(response)
9393     {
9394         this.uploadComplete= true;
9395         if (this.haveProgress) {
9396             Roo.MessageBox.hide();
9397         }
9398         
9399         
9400         var result = this.processResponse(response);
9401         if(result === true || result.success){
9402             this.form.afterAction(this, true);
9403             return;
9404         }
9405         if(result.errors){
9406             this.form.markInvalid(result.errors);
9407             this.failureType = Roo.form.Action.SERVER_INVALID;
9408         }
9409         this.form.afterAction(this, false);
9410     },
9411     failure : function(response)
9412     {
9413         this.uploadComplete= true;
9414         if (this.haveProgress) {
9415             Roo.MessageBox.hide();
9416         }
9417         
9418         this.response = response;
9419         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9420         this.form.afterAction(this, false);
9421     },
9422     
9423     handleResponse : function(response){
9424         if(this.form.errorReader){
9425             var rs = this.form.errorReader.read(response);
9426             var errors = [];
9427             if(rs.records){
9428                 for(var i = 0, len = rs.records.length; i < len; i++) {
9429                     var r = rs.records[i];
9430                     errors[i] = r.data;
9431                 }
9432             }
9433             if(errors.length < 1){
9434                 errors = null;
9435             }
9436             return {
9437                 success : rs.success,
9438                 errors : errors
9439             };
9440         }
9441         var ret = false;
9442         try {
9443             ret = Roo.decode(response.responseText);
9444         } catch (e) {
9445             ret = {
9446                 success: false,
9447                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9448                 errors : []
9449             };
9450         }
9451         return ret;
9452         
9453     }
9454 });
9455
9456
9457 Roo.form.Action.Load = function(form, options){
9458     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9459     this.reader = this.form.reader;
9460 };
9461
9462 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9463     type : 'load',
9464
9465     run : function(){
9466         
9467         Roo.Ajax.request(Roo.apply(
9468                 this.createCallback(), {
9469                     method:this.getMethod(),
9470                     url:this.getUrl(false),
9471                     params:this.getParams()
9472         }));
9473     },
9474
9475     success : function(response){
9476         
9477         var result = this.processResponse(response);
9478         if(result === true || !result.success || !result.data){
9479             this.failureType = Roo.form.Action.LOAD_FAILURE;
9480             this.form.afterAction(this, false);
9481             return;
9482         }
9483         this.form.clearInvalid();
9484         this.form.setValues(result.data);
9485         this.form.afterAction(this, true);
9486     },
9487
9488     handleResponse : function(response){
9489         if(this.form.reader){
9490             var rs = this.form.reader.read(response);
9491             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9492             return {
9493                 success : rs.success,
9494                 data : data
9495             };
9496         }
9497         return Roo.decode(response.responseText);
9498     }
9499 });
9500
9501 Roo.form.Action.ACTION_TYPES = {
9502     'load' : Roo.form.Action.Load,
9503     'submit' : Roo.form.Action.Submit
9504 };/*
9505  * - LGPL
9506  *
9507  * form
9508  *
9509  */
9510
9511 /**
9512  * @class Roo.bootstrap.Form
9513  * @extends Roo.bootstrap.Component
9514  * Bootstrap Form class
9515  * @cfg {String} method  GET | POST (default POST)
9516  * @cfg {String} labelAlign top | left (default top)
9517  * @cfg {String} align left  | right - for navbars
9518  * @cfg {Boolean} loadMask load mask when submit (default true)
9519
9520  *
9521  * @constructor
9522  * Create a new Form
9523  * @param {Object} config The config object
9524  */
9525
9526
9527 Roo.bootstrap.Form = function(config){
9528     
9529     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9530     
9531     Roo.bootstrap.Form.popover.apply();
9532     
9533     this.addEvents({
9534         /**
9535          * @event clientvalidation
9536          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9537          * @param {Form} this
9538          * @param {Boolean} valid true if the form has passed client-side validation
9539          */
9540         clientvalidation: true,
9541         /**
9542          * @event beforeaction
9543          * Fires before any action is performed. Return false to cancel the action.
9544          * @param {Form} this
9545          * @param {Action} action The action to be performed
9546          */
9547         beforeaction: true,
9548         /**
9549          * @event actionfailed
9550          * Fires when an action fails.
9551          * @param {Form} this
9552          * @param {Action} action The action that failed
9553          */
9554         actionfailed : true,
9555         /**
9556          * @event actioncomplete
9557          * Fires when an action is completed.
9558          * @param {Form} this
9559          * @param {Action} action The action that completed
9560          */
9561         actioncomplete : true
9562     });
9563 };
9564
9565 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9566
9567      /**
9568      * @cfg {String} method
9569      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9570      */
9571     method : 'POST',
9572     /**
9573      * @cfg {String} url
9574      * The URL to use for form actions if one isn't supplied in the action options.
9575      */
9576     /**
9577      * @cfg {Boolean} fileUpload
9578      * Set to true if this form is a file upload.
9579      */
9580
9581     /**
9582      * @cfg {Object} baseParams
9583      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9584      */
9585
9586     /**
9587      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9588      */
9589     timeout: 30,
9590     /**
9591      * @cfg {Sting} align (left|right) for navbar forms
9592      */
9593     align : 'left',
9594
9595     // private
9596     activeAction : null,
9597
9598     /**
9599      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9600      * element by passing it or its id or mask the form itself by passing in true.
9601      * @type Mixed
9602      */
9603     waitMsgTarget : false,
9604
9605     loadMask : true,
9606     
9607     /**
9608      * @cfg {Boolean} errorMask (true|false) default false
9609      */
9610     errorMask : false,
9611     
9612     /**
9613      * @cfg {Number} maskOffset Default 100
9614      */
9615     maskOffset : 100,
9616     
9617     /**
9618      * @cfg {Boolean} maskBody
9619      */
9620     maskBody : false,
9621
9622     getAutoCreate : function(){
9623
9624         var cfg = {
9625             tag: 'form',
9626             method : this.method || 'POST',
9627             id : this.id || Roo.id(),
9628             cls : ''
9629         };
9630         if (this.parent().xtype.match(/^Nav/)) {
9631             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9632
9633         }
9634
9635         if (this.labelAlign == 'left' ) {
9636             cfg.cls += ' form-horizontal';
9637         }
9638
9639
9640         return cfg;
9641     },
9642     initEvents : function()
9643     {
9644         this.el.on('submit', this.onSubmit, this);
9645         // this was added as random key presses on the form where triggering form submit.
9646         this.el.on('keypress', function(e) {
9647             if (e.getCharCode() != 13) {
9648                 return true;
9649             }
9650             // we might need to allow it for textareas.. and some other items.
9651             // check e.getTarget().
9652
9653             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9654                 return true;
9655             }
9656
9657             Roo.log("keypress blocked");
9658
9659             e.preventDefault();
9660             return false;
9661         });
9662         
9663     },
9664     // private
9665     onSubmit : function(e){
9666         e.stopEvent();
9667     },
9668
9669      /**
9670      * Returns true if client-side validation on the form is successful.
9671      * @return Boolean
9672      */
9673     isValid : function(){
9674         var items = this.getItems();
9675         var valid = true;
9676         var target = false;
9677         
9678         items.each(function(f){
9679             
9680             if(f.validate()){
9681                 return;
9682             }
9683             
9684             Roo.log('invalid field: ' + f.name);
9685             
9686             valid = false;
9687
9688             if(!target && f.el.isVisible(true)){
9689                 target = f;
9690             }
9691            
9692         });
9693         
9694         if(this.errorMask && !valid){
9695             Roo.bootstrap.Form.popover.mask(this, target);
9696         }
9697         
9698         return valid;
9699     },
9700     
9701     /**
9702      * Returns true if any fields in this form have changed since their original load.
9703      * @return Boolean
9704      */
9705     isDirty : function(){
9706         var dirty = false;
9707         var items = this.getItems();
9708         items.each(function(f){
9709            if(f.isDirty()){
9710                dirty = true;
9711                return false;
9712            }
9713            return true;
9714         });
9715         return dirty;
9716     },
9717      /**
9718      * Performs a predefined action (submit or load) or custom actions you define on this form.
9719      * @param {String} actionName The name of the action type
9720      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9721      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9722      * accept other config options):
9723      * <pre>
9724 Property          Type             Description
9725 ----------------  ---------------  ----------------------------------------------------------------------------------
9726 url               String           The url for the action (defaults to the form's url)
9727 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9728 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9729 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9730                                    validate the form on the client (defaults to false)
9731      * </pre>
9732      * @return {BasicForm} this
9733      */
9734     doAction : function(action, options){
9735         if(typeof action == 'string'){
9736             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9737         }
9738         if(this.fireEvent('beforeaction', this, action) !== false){
9739             this.beforeAction(action);
9740             action.run.defer(100, action);
9741         }
9742         return this;
9743     },
9744
9745     // private
9746     beforeAction : function(action){
9747         var o = action.options;
9748         
9749         if(this.loadMask){
9750             
9751             if(this.maskBody){
9752                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9753             } else {
9754                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9755             }
9756         }
9757         // not really supported yet.. ??
9758
9759         //if(this.waitMsgTarget === true){
9760         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9761         //}else if(this.waitMsgTarget){
9762         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9763         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9764         //}else {
9765         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9766        // }
9767
9768     },
9769
9770     // private
9771     afterAction : function(action, success){
9772         this.activeAction = null;
9773         var o = action.options;
9774
9775         if(this.loadMask){
9776             
9777             if(this.maskBody){
9778                 Roo.get(document.body).unmask();
9779             } else {
9780                 this.el.unmask();
9781             }
9782         }
9783         
9784         //if(this.waitMsgTarget === true){
9785 //            this.el.unmask();
9786         //}else if(this.waitMsgTarget){
9787         //    this.waitMsgTarget.unmask();
9788         //}else{
9789         //    Roo.MessageBox.updateProgress(1);
9790         //    Roo.MessageBox.hide();
9791        // }
9792         //
9793         if(success){
9794             if(o.reset){
9795                 this.reset();
9796             }
9797             Roo.callback(o.success, o.scope, [this, action]);
9798             this.fireEvent('actioncomplete', this, action);
9799
9800         }else{
9801
9802             // failure condition..
9803             // we have a scenario where updates need confirming.
9804             // eg. if a locking scenario exists..
9805             // we look for { errors : { needs_confirm : true }} in the response.
9806             if (
9807                 (typeof(action.result) != 'undefined')  &&
9808                 (typeof(action.result.errors) != 'undefined')  &&
9809                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9810            ){
9811                 var _t = this;
9812                 Roo.log("not supported yet");
9813                  /*
9814
9815                 Roo.MessageBox.confirm(
9816                     "Change requires confirmation",
9817                     action.result.errorMsg,
9818                     function(r) {
9819                         if (r != 'yes') {
9820                             return;
9821                         }
9822                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9823                     }
9824
9825                 );
9826                 */
9827
9828
9829                 return;
9830             }
9831
9832             Roo.callback(o.failure, o.scope, [this, action]);
9833             // show an error message if no failed handler is set..
9834             if (!this.hasListener('actionfailed')) {
9835                 Roo.log("need to add dialog support");
9836                 /*
9837                 Roo.MessageBox.alert("Error",
9838                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9839                         action.result.errorMsg :
9840                         "Saving Failed, please check your entries or try again"
9841                 );
9842                 */
9843             }
9844
9845             this.fireEvent('actionfailed', this, action);
9846         }
9847
9848     },
9849     /**
9850      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9851      * @param {String} id The value to search for
9852      * @return Field
9853      */
9854     findField : function(id){
9855         var items = this.getItems();
9856         var field = items.get(id);
9857         if(!field){
9858              items.each(function(f){
9859                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9860                     field = f;
9861                     return false;
9862                 }
9863                 return true;
9864             });
9865         }
9866         return field || null;
9867     },
9868      /**
9869      * Mark fields in this form invalid in bulk.
9870      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9871      * @return {BasicForm} this
9872      */
9873     markInvalid : function(errors){
9874         if(errors instanceof Array){
9875             for(var i = 0, len = errors.length; i < len; i++){
9876                 var fieldError = errors[i];
9877                 var f = this.findField(fieldError.id);
9878                 if(f){
9879                     f.markInvalid(fieldError.msg);
9880                 }
9881             }
9882         }else{
9883             var field, id;
9884             for(id in errors){
9885                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9886                     field.markInvalid(errors[id]);
9887                 }
9888             }
9889         }
9890         //Roo.each(this.childForms || [], function (f) {
9891         //    f.markInvalid(errors);
9892         //});
9893
9894         return this;
9895     },
9896
9897     /**
9898      * Set values for fields in this form in bulk.
9899      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9900      * @return {BasicForm} this
9901      */
9902     setValues : function(values){
9903         if(values instanceof Array){ // array of objects
9904             for(var i = 0, len = values.length; i < len; i++){
9905                 var v = values[i];
9906                 var f = this.findField(v.id);
9907                 if(f){
9908                     f.setValue(v.value);
9909                     if(this.trackResetOnLoad){
9910                         f.originalValue = f.getValue();
9911                     }
9912                 }
9913             }
9914         }else{ // object hash
9915             var field, id;
9916             for(id in values){
9917                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9918
9919                     if (field.setFromData &&
9920                         field.valueField &&
9921                         field.displayField &&
9922                         // combos' with local stores can
9923                         // be queried via setValue()
9924                         // to set their value..
9925                         (field.store && !field.store.isLocal)
9926                         ) {
9927                         // it's a combo
9928                         var sd = { };
9929                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9930                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9931                         field.setFromData(sd);
9932
9933                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9934                         
9935                         field.setFromData(values);
9936                         
9937                     } else {
9938                         field.setValue(values[id]);
9939                     }
9940
9941
9942                     if(this.trackResetOnLoad){
9943                         field.originalValue = field.getValue();
9944                     }
9945                 }
9946             }
9947         }
9948
9949         //Roo.each(this.childForms || [], function (f) {
9950         //    f.setValues(values);
9951         //});
9952
9953         return this;
9954     },
9955
9956     /**
9957      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9958      * they are returned as an array.
9959      * @param {Boolean} asString
9960      * @return {Object}
9961      */
9962     getValues : function(asString){
9963         //if (this.childForms) {
9964             // copy values from the child forms
9965         //    Roo.each(this.childForms, function (f) {
9966         //        this.setValues(f.getValues());
9967         //    }, this);
9968         //}
9969
9970
9971
9972         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9973         if(asString === true){
9974             return fs;
9975         }
9976         return Roo.urlDecode(fs);
9977     },
9978
9979     /**
9980      * Returns the fields in this form as an object with key/value pairs.
9981      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9982      * @return {Object}
9983      */
9984     getFieldValues : function(with_hidden)
9985     {
9986         var items = this.getItems();
9987         var ret = {};
9988         items.each(function(f){
9989             
9990             if (!f.getName()) {
9991                 return;
9992             }
9993             
9994             var v = f.getValue();
9995             
9996             if (f.inputType =='radio') {
9997                 if (typeof(ret[f.getName()]) == 'undefined') {
9998                     ret[f.getName()] = ''; // empty..
9999                 }
10000
10001                 if (!f.el.dom.checked) {
10002                     return;
10003
10004                 }
10005                 v = f.el.dom.value;
10006
10007             }
10008             
10009             if(f.xtype == 'MoneyField'){
10010                 ret[f.currencyName] = f.getCurrency();
10011             }
10012
10013             // not sure if this supported any more..
10014             if ((typeof(v) == 'object') && f.getRawValue) {
10015                 v = f.getRawValue() ; // dates..
10016             }
10017             // combo boxes where name != hiddenName...
10018             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10019                 ret[f.name] = f.getRawValue();
10020             }
10021             ret[f.getName()] = v;
10022         });
10023
10024         return ret;
10025     },
10026
10027     /**
10028      * Clears all invalid messages in this form.
10029      * @return {BasicForm} this
10030      */
10031     clearInvalid : function(){
10032         var items = this.getItems();
10033
10034         items.each(function(f){
10035            f.clearInvalid();
10036         });
10037
10038         return this;
10039     },
10040
10041     /**
10042      * Resets this form.
10043      * @return {BasicForm} this
10044      */
10045     reset : function(){
10046         var items = this.getItems();
10047         items.each(function(f){
10048             f.reset();
10049         });
10050
10051         Roo.each(this.childForms || [], function (f) {
10052             f.reset();
10053         });
10054
10055
10056         return this;
10057     },
10058     
10059     getItems : function()
10060     {
10061         var r=new Roo.util.MixedCollection(false, function(o){
10062             return o.id || (o.id = Roo.id());
10063         });
10064         var iter = function(el) {
10065             if (el.inputEl) {
10066                 r.add(el);
10067             }
10068             if (!el.items) {
10069                 return;
10070             }
10071             Roo.each(el.items,function(e) {
10072                 iter(e);
10073             });
10074         };
10075
10076         iter(this);
10077         return r;
10078     },
10079     
10080     hideFields : function(items)
10081     {
10082         Roo.each(items, function(i){
10083             
10084             var f = this.findField(i);
10085             
10086             if(!f){
10087                 return;
10088             }
10089             
10090             f.hide();
10091             
10092         }, this);
10093     },
10094     
10095     showFields : function(items)
10096     {
10097         Roo.each(items, function(i){
10098             
10099             var f = this.findField(i);
10100             
10101             if(!f){
10102                 return;
10103             }
10104             
10105             f.show();
10106             
10107         }, this);
10108     }
10109
10110 });
10111
10112 Roo.apply(Roo.bootstrap.Form, {
10113     
10114     popover : {
10115         
10116         padding : 5,
10117         
10118         isApplied : false,
10119         
10120         isMasked : false,
10121         
10122         form : false,
10123         
10124         target : false,
10125         
10126         toolTip : false,
10127         
10128         intervalID : false,
10129         
10130         maskEl : false,
10131         
10132         apply : function()
10133         {
10134             if(this.isApplied){
10135                 return;
10136             }
10137             
10138             this.maskEl = {
10139                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10140                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10141                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10142                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10143             };
10144             
10145             this.maskEl.top.enableDisplayMode("block");
10146             this.maskEl.left.enableDisplayMode("block");
10147             this.maskEl.bottom.enableDisplayMode("block");
10148             this.maskEl.right.enableDisplayMode("block");
10149             
10150             this.toolTip = new Roo.bootstrap.Tooltip({
10151                 cls : 'roo-form-error-popover',
10152                 alignment : {
10153                     'left' : ['r-l', [-2,0], 'right'],
10154                     'right' : ['l-r', [2,0], 'left'],
10155                     'bottom' : ['tl-bl', [0,2], 'top'],
10156                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10157                 }
10158             });
10159             
10160             this.toolTip.render(Roo.get(document.body));
10161
10162             this.toolTip.el.enableDisplayMode("block");
10163             
10164             Roo.get(document.body).on('click', function(){
10165                 this.unmask();
10166             }, this);
10167             
10168             Roo.get(document.body).on('touchstart', function(){
10169                 this.unmask();
10170             }, this);
10171             
10172             this.isApplied = true
10173         },
10174         
10175         mask : function(form, target)
10176         {
10177             this.form = form;
10178             
10179             this.target = target;
10180             
10181             if(!this.form.errorMask || !target.el){
10182                 return;
10183             }
10184             
10185             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10186             
10187             Roo.log(scrollable);
10188             
10189             var ot = this.target.el.calcOffsetsTo(scrollable);
10190             
10191             var scrollTo = ot[1] - this.form.maskOffset;
10192             
10193             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10194             
10195             scrollable.scrollTo('top', scrollTo);
10196             
10197             var box = this.target.el.getBox();
10198             Roo.log(box);
10199             var zIndex = Roo.bootstrap.Modal.zIndex++;
10200
10201             
10202             this.maskEl.top.setStyle('position', 'absolute');
10203             this.maskEl.top.setStyle('z-index', zIndex);
10204             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10205             this.maskEl.top.setLeft(0);
10206             this.maskEl.top.setTop(0);
10207             this.maskEl.top.show();
10208             
10209             this.maskEl.left.setStyle('position', 'absolute');
10210             this.maskEl.left.setStyle('z-index', zIndex);
10211             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10212             this.maskEl.left.setLeft(0);
10213             this.maskEl.left.setTop(box.y - this.padding);
10214             this.maskEl.left.show();
10215
10216             this.maskEl.bottom.setStyle('position', 'absolute');
10217             this.maskEl.bottom.setStyle('z-index', zIndex);
10218             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10219             this.maskEl.bottom.setLeft(0);
10220             this.maskEl.bottom.setTop(box.bottom + this.padding);
10221             this.maskEl.bottom.show();
10222
10223             this.maskEl.right.setStyle('position', 'absolute');
10224             this.maskEl.right.setStyle('z-index', zIndex);
10225             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10226             this.maskEl.right.setLeft(box.right + this.padding);
10227             this.maskEl.right.setTop(box.y - this.padding);
10228             this.maskEl.right.show();
10229
10230             this.toolTip.bindEl = this.target.el;
10231
10232             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10233
10234             var tip = this.target.blankText;
10235
10236             if(this.target.getValue() !== '' ) {
10237                 
10238                 if (this.target.invalidText.length) {
10239                     tip = this.target.invalidText;
10240                 } else if (this.target.regexText.length){
10241                     tip = this.target.regexText;
10242                 }
10243             }
10244
10245             this.toolTip.show(tip);
10246
10247             this.intervalID = window.setInterval(function() {
10248                 Roo.bootstrap.Form.popover.unmask();
10249             }, 10000);
10250
10251             window.onwheel = function(){ return false;};
10252             
10253             (function(){ this.isMasked = true; }).defer(500, this);
10254             
10255         },
10256         
10257         unmask : function()
10258         {
10259             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10260                 return;
10261             }
10262             
10263             this.maskEl.top.setStyle('position', 'absolute');
10264             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10265             this.maskEl.top.hide();
10266
10267             this.maskEl.left.setStyle('position', 'absolute');
10268             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10269             this.maskEl.left.hide();
10270
10271             this.maskEl.bottom.setStyle('position', 'absolute');
10272             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10273             this.maskEl.bottom.hide();
10274
10275             this.maskEl.right.setStyle('position', 'absolute');
10276             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10277             this.maskEl.right.hide();
10278             
10279             this.toolTip.hide();
10280             
10281             this.toolTip.el.hide();
10282             
10283             window.onwheel = function(){ return true;};
10284             
10285             if(this.intervalID){
10286                 window.clearInterval(this.intervalID);
10287                 this.intervalID = false;
10288             }
10289             
10290             this.isMasked = false;
10291             
10292         }
10293         
10294     }
10295     
10296 });
10297
10298 /*
10299  * Based on:
10300  * Ext JS Library 1.1.1
10301  * Copyright(c) 2006-2007, Ext JS, LLC.
10302  *
10303  * Originally Released Under LGPL - original licence link has changed is not relivant.
10304  *
10305  * Fork - LGPL
10306  * <script type="text/javascript">
10307  */
10308 /**
10309  * @class Roo.form.VTypes
10310  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10311  * @singleton
10312  */
10313 Roo.form.VTypes = function(){
10314     // closure these in so they are only created once.
10315     var alpha = /^[a-zA-Z_]+$/;
10316     var alphanum = /^[a-zA-Z0-9_]+$/;
10317     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10318     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10319
10320     // All these messages and functions are configurable
10321     return {
10322         /**
10323          * The function used to validate email addresses
10324          * @param {String} value The email address
10325          */
10326         'email' : function(v){
10327             return email.test(v);
10328         },
10329         /**
10330          * The error text to display when the email validation function returns false
10331          * @type String
10332          */
10333         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10334         /**
10335          * The keystroke filter mask to be applied on email input
10336          * @type RegExp
10337          */
10338         'emailMask' : /[a-z0-9_\.\-@]/i,
10339
10340         /**
10341          * The function used to validate URLs
10342          * @param {String} value The URL
10343          */
10344         'url' : function(v){
10345             return url.test(v);
10346         },
10347         /**
10348          * The error text to display when the url validation function returns false
10349          * @type String
10350          */
10351         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10352         
10353         /**
10354          * The function used to validate alpha values
10355          * @param {String} value The value
10356          */
10357         'alpha' : function(v){
10358             return alpha.test(v);
10359         },
10360         /**
10361          * The error text to display when the alpha validation function returns false
10362          * @type String
10363          */
10364         'alphaText' : 'This field should only contain letters and _',
10365         /**
10366          * The keystroke filter mask to be applied on alpha input
10367          * @type RegExp
10368          */
10369         'alphaMask' : /[a-z_]/i,
10370
10371         /**
10372          * The function used to validate alphanumeric values
10373          * @param {String} value The value
10374          */
10375         'alphanum' : function(v){
10376             return alphanum.test(v);
10377         },
10378         /**
10379          * The error text to display when the alphanumeric validation function returns false
10380          * @type String
10381          */
10382         'alphanumText' : 'This field should only contain letters, numbers and _',
10383         /**
10384          * The keystroke filter mask to be applied on alphanumeric input
10385          * @type RegExp
10386          */
10387         'alphanumMask' : /[a-z0-9_]/i
10388     };
10389 }();/*
10390  * - LGPL
10391  *
10392  * Input
10393  * 
10394  */
10395
10396 /**
10397  * @class Roo.bootstrap.Input
10398  * @extends Roo.bootstrap.Component
10399  * Bootstrap Input class
10400  * @cfg {Boolean} disabled is it disabled
10401  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10402  * @cfg {String} name name of the input
10403  * @cfg {string} fieldLabel - the label associated
10404  * @cfg {string} placeholder - placeholder to put in text.
10405  * @cfg {string}  before - input group add on before
10406  * @cfg {string} after - input group add on after
10407  * @cfg {string} size - (lg|sm) or leave empty..
10408  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10409  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10410  * @cfg {Number} md colspan out of 12 for computer-sized screens
10411  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10412  * @cfg {string} value default value of the input
10413  * @cfg {Number} labelWidth set the width of label 
10414  * @cfg {Number} labellg set the width of label (1-12)
10415  * @cfg {Number} labelmd set the width of label (1-12)
10416  * @cfg {Number} labelsm set the width of label (1-12)
10417  * @cfg {Number} labelxs set the width of label (1-12)
10418  * @cfg {String} labelAlign (top|left)
10419  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10420  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10421  * @cfg {String} indicatorpos (left|right) default left
10422  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10423  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10424  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10425
10426  * @cfg {String} align (left|center|right) Default left
10427  * @cfg {Boolean} forceFeedback (true|false) Default false
10428  * 
10429  * @constructor
10430  * Create a new Input
10431  * @param {Object} config The config object
10432  */
10433
10434 Roo.bootstrap.Input = function(config){
10435     
10436     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10437     
10438     this.addEvents({
10439         /**
10440          * @event focus
10441          * Fires when this field receives input focus.
10442          * @param {Roo.form.Field} this
10443          */
10444         focus : true,
10445         /**
10446          * @event blur
10447          * Fires when this field loses input focus.
10448          * @param {Roo.form.Field} this
10449          */
10450         blur : true,
10451         /**
10452          * @event specialkey
10453          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10454          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10455          * @param {Roo.form.Field} this
10456          * @param {Roo.EventObject} e The event object
10457          */
10458         specialkey : true,
10459         /**
10460          * @event change
10461          * Fires just before the field blurs if the field value has changed.
10462          * @param {Roo.form.Field} this
10463          * @param {Mixed} newValue The new value
10464          * @param {Mixed} oldValue The original value
10465          */
10466         change : true,
10467         /**
10468          * @event invalid
10469          * Fires after the field has been marked as invalid.
10470          * @param {Roo.form.Field} this
10471          * @param {String} msg The validation message
10472          */
10473         invalid : true,
10474         /**
10475          * @event valid
10476          * Fires after the field has been validated with no errors.
10477          * @param {Roo.form.Field} this
10478          */
10479         valid : true,
10480          /**
10481          * @event keyup
10482          * Fires after the key up
10483          * @param {Roo.form.Field} this
10484          * @param {Roo.EventObject}  e The event Object
10485          */
10486         keyup : true
10487     });
10488 };
10489
10490 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10491      /**
10492      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10493       automatic validation (defaults to "keyup").
10494      */
10495     validationEvent : "keyup",
10496      /**
10497      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10498      */
10499     validateOnBlur : true,
10500     /**
10501      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10502      */
10503     validationDelay : 250,
10504      /**
10505      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10506      */
10507     focusClass : "x-form-focus",  // not needed???
10508     
10509        
10510     /**
10511      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10512      */
10513     invalidClass : "has-warning",
10514     
10515     /**
10516      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     validClass : "has-success",
10519     
10520     /**
10521      * @cfg {Boolean} hasFeedback (true|false) default true
10522      */
10523     hasFeedback : true,
10524     
10525     /**
10526      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10527      */
10528     invalidFeedbackClass : "glyphicon-warning-sign",
10529     
10530     /**
10531      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     validFeedbackClass : "glyphicon-ok",
10534     
10535     /**
10536      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10537      */
10538     selectOnFocus : false,
10539     
10540      /**
10541      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10542      */
10543     maskRe : null,
10544        /**
10545      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10546      */
10547     vtype : null,
10548     
10549       /**
10550      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10551      */
10552     disableKeyFilter : false,
10553     
10554        /**
10555      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10556      */
10557     disabled : false,
10558      /**
10559      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10560      */
10561     allowBlank : true,
10562     /**
10563      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10564      */
10565     blankText : "Please complete this mandatory field",
10566     
10567      /**
10568      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10569      */
10570     minLength : 0,
10571     /**
10572      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10573      */
10574     maxLength : Number.MAX_VALUE,
10575     /**
10576      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10577      */
10578     minLengthText : "The minimum length for this field is {0}",
10579     /**
10580      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10581      */
10582     maxLengthText : "The maximum length for this field is {0}",
10583   
10584     
10585     /**
10586      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10587      * If available, this function will be called only after the basic validators all return true, and will be passed the
10588      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10589      */
10590     validator : null,
10591     /**
10592      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10593      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10594      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10595      */
10596     regex : null,
10597     /**
10598      * @cfg {String} regexText -- Depricated - use Invalid Text
10599      */
10600     regexText : "",
10601     
10602     /**
10603      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10604      */
10605     invalidText : "",
10606     
10607     
10608     
10609     autocomplete: false,
10610     
10611     
10612     fieldLabel : '',
10613     inputType : 'text',
10614     
10615     name : false,
10616     placeholder: false,
10617     before : false,
10618     after : false,
10619     size : false,
10620     hasFocus : false,
10621     preventMark: false,
10622     isFormField : true,
10623     value : '',
10624     labelWidth : 2,
10625     labelAlign : false,
10626     readOnly : false,
10627     align : false,
10628     formatedValue : false,
10629     forceFeedback : false,
10630     
10631     indicatorpos : 'left',
10632     
10633     labellg : 0,
10634     labelmd : 0,
10635     labelsm : 0,
10636     labelxs : 0,
10637     
10638     capture : '',
10639     accept : '',
10640     
10641     parentLabelAlign : function()
10642     {
10643         var parent = this;
10644         while (parent.parent()) {
10645             parent = parent.parent();
10646             if (typeof(parent.labelAlign) !='undefined') {
10647                 return parent.labelAlign;
10648             }
10649         }
10650         return 'left';
10651         
10652     },
10653     
10654     getAutoCreate : function()
10655     {
10656         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10657         
10658         var id = Roo.id();
10659         
10660         var cfg = {};
10661         
10662         if(this.inputType != 'hidden'){
10663             cfg.cls = 'form-group' //input-group
10664         }
10665         
10666         var input =  {
10667             tag: 'input',
10668             id : id,
10669             type : this.inputType,
10670             value : this.value,
10671             cls : 'form-control',
10672             placeholder : this.placeholder || '',
10673             autocomplete : this.autocomplete || 'new-password'
10674         };
10675         if (this.inputType == 'file') {
10676             input.style = 'overflow:hidden'; // why not in CSS?
10677         }
10678         
10679         if(this.capture.length){
10680             input.capture = this.capture;
10681         }
10682         
10683         if(this.accept.length){
10684             input.accept = this.accept + "/*";
10685         }
10686         
10687         if(this.align){
10688             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10689         }
10690         
10691         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10692             input.maxLength = this.maxLength;
10693         }
10694         
10695         if (this.disabled) {
10696             input.disabled=true;
10697         }
10698         
10699         if (this.readOnly) {
10700             input.readonly=true;
10701         }
10702         
10703         if (this.name) {
10704             input.name = this.name;
10705         }
10706         
10707         if (this.size) {
10708             input.cls += ' input-' + this.size;
10709         }
10710         
10711         var settings=this;
10712         ['xs','sm','md','lg'].map(function(size){
10713             if (settings[size]) {
10714                 cfg.cls += ' col-' + size + '-' + settings[size];
10715             }
10716         });
10717         
10718         var inputblock = input;
10719         
10720         var feedback = {
10721             tag: 'span',
10722             cls: 'glyphicon form-control-feedback'
10723         };
10724             
10725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10726             
10727             inputblock = {
10728                 cls : 'has-feedback',
10729                 cn :  [
10730                     input,
10731                     feedback
10732                 ] 
10733             };  
10734         }
10735         
10736         if (this.before || this.after) {
10737             
10738             inputblock = {
10739                 cls : 'input-group',
10740                 cn :  [] 
10741             };
10742             
10743             if (this.before && typeof(this.before) == 'string') {
10744                 
10745                 inputblock.cn.push({
10746                     tag :'span',
10747                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10748                     html : this.before
10749                 });
10750             }
10751             if (this.before && typeof(this.before) == 'object') {
10752                 this.before = Roo.factory(this.before);
10753                 
10754                 inputblock.cn.push({
10755                     tag :'span',
10756                     cls : 'roo-input-before input-group-prepend   input-group-' +
10757                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10758                 });
10759             }
10760             
10761             inputblock.cn.push(input);
10762             
10763             if (this.after && typeof(this.after) == 'string') {
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10767                     html : this.after
10768                 });
10769             }
10770             if (this.after && typeof(this.after) == 'object') {
10771                 this.after = Roo.factory(this.after);
10772                 
10773                 inputblock.cn.push({
10774                     tag :'span',
10775                     cls : 'roo-input-after input-group-append  input-group-' +
10776                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10777                 });
10778             }
10779             
10780             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10781                 inputblock.cls += ' has-feedback';
10782                 inputblock.cn.push(feedback);
10783             }
10784         };
10785         var indicator = {
10786             tag : 'i',
10787             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10788             tooltip : 'This field is required'
10789         };
10790         if (this.allowBlank ) {
10791             indicator.style = this.allowBlank ? ' display:none' : '';
10792         }
10793         if (align ==='left' && this.fieldLabel.length) {
10794             
10795             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10796             
10797             cfg.cn = [
10798                 indicator,
10799                 {
10800                     tag: 'label',
10801                     'for' :  id,
10802                     cls : 'control-label col-form-label',
10803                     html : this.fieldLabel
10804
10805                 },
10806                 {
10807                     cls : "", 
10808                     cn: [
10809                         inputblock
10810                     ]
10811                 }
10812             ];
10813             
10814             var labelCfg = cfg.cn[1];
10815             var contentCfg = cfg.cn[2];
10816             
10817             if(this.indicatorpos == 'right'){
10818                 cfg.cn = [
10819                     {
10820                         tag: 'label',
10821                         'for' :  id,
10822                         cls : 'control-label col-form-label',
10823                         cn : [
10824                             {
10825                                 tag : 'span',
10826                                 html : this.fieldLabel
10827                             },
10828                             indicator
10829                         ]
10830                     },
10831                     {
10832                         cls : "",
10833                         cn: [
10834                             inputblock
10835                         ]
10836                     }
10837
10838                 ];
10839                 
10840                 labelCfg = cfg.cn[0];
10841                 contentCfg = cfg.cn[1];
10842             
10843             }
10844             
10845             if(this.labelWidth > 12){
10846                 labelCfg.style = "width: " + this.labelWidth + 'px';
10847             }
10848             
10849             if(this.labelWidth < 13 && this.labelmd == 0){
10850                 this.labelmd = this.labelWidth;
10851             }
10852             
10853             if(this.labellg > 0){
10854                 labelCfg.cls += ' col-lg-' + this.labellg;
10855                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10856             }
10857             
10858             if(this.labelmd > 0){
10859                 labelCfg.cls += ' col-md-' + this.labelmd;
10860                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10861             }
10862             
10863             if(this.labelsm > 0){
10864                 labelCfg.cls += ' col-sm-' + this.labelsm;
10865                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10866             }
10867             
10868             if(this.labelxs > 0){
10869                 labelCfg.cls += ' col-xs-' + this.labelxs;
10870                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10871             }
10872             
10873             
10874         } else if ( this.fieldLabel.length) {
10875                 
10876             
10877             
10878             cfg.cn = [
10879                 {
10880                     tag : 'i',
10881                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10882                     tooltip : 'This field is required',
10883                     style : this.allowBlank ? ' display:none' : '' 
10884                 },
10885                 {
10886                     tag: 'label',
10887                    //cls : 'input-group-addon',
10888                     html : this.fieldLabel
10889
10890                 },
10891
10892                inputblock
10893
10894            ];
10895            
10896            if(this.indicatorpos == 'right'){
10897        
10898                 cfg.cn = [
10899                     {
10900                         tag: 'label',
10901                        //cls : 'input-group-addon',
10902                         html : this.fieldLabel
10903
10904                     },
10905                     {
10906                         tag : 'i',
10907                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10908                         tooltip : 'This field is required',
10909                         style : this.allowBlank ? ' display:none' : '' 
10910                     },
10911
10912                    inputblock
10913
10914                ];
10915
10916             }
10917
10918         } else {
10919             
10920             cfg.cn = [
10921
10922                     inputblock
10923
10924             ];
10925                 
10926                 
10927         };
10928         
10929         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10930            cfg.cls += ' navbar-form';
10931         }
10932         
10933         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10934             // on BS4 we do this only if not form 
10935             cfg.cls += ' navbar-form';
10936             cfg.tag = 'li';
10937         }
10938         
10939         return cfg;
10940         
10941     },
10942     /**
10943      * return the real input element.
10944      */
10945     inputEl: function ()
10946     {
10947         return this.el.select('input.form-control',true).first();
10948     },
10949     
10950     tooltipEl : function()
10951     {
10952         return this.inputEl();
10953     },
10954     
10955     indicatorEl : function()
10956     {
10957         if (Roo.bootstrap.version == 4) {
10958             return false; // not enabled in v4 yet.
10959         }
10960         
10961         var indicator = this.el.select('i.roo-required-indicator',true).first();
10962         
10963         if(!indicator){
10964             return false;
10965         }
10966         
10967         return indicator;
10968         
10969     },
10970     
10971     setDisabled : function(v)
10972     {
10973         var i  = this.inputEl().dom;
10974         if (!v) {
10975             i.removeAttribute('disabled');
10976             return;
10977             
10978         }
10979         i.setAttribute('disabled','true');
10980     },
10981     initEvents : function()
10982     {
10983           
10984         this.inputEl().on("keydown" , this.fireKey,  this);
10985         this.inputEl().on("focus", this.onFocus,  this);
10986         this.inputEl().on("blur", this.onBlur,  this);
10987         
10988         this.inputEl().relayEvent('keyup', this);
10989         
10990         this.indicator = this.indicatorEl();
10991         
10992         if(this.indicator){
10993             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10994         }
10995  
10996         // reference to original value for reset
10997         this.originalValue = this.getValue();
10998         //Roo.form.TextField.superclass.initEvents.call(this);
10999         if(this.validationEvent == 'keyup'){
11000             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11001             this.inputEl().on('keyup', this.filterValidation, this);
11002         }
11003         else if(this.validationEvent !== false){
11004             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11005         }
11006         
11007         if(this.selectOnFocus){
11008             this.on("focus", this.preFocus, this);
11009             
11010         }
11011         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11012             this.inputEl().on("keypress", this.filterKeys, this);
11013         } else {
11014             this.inputEl().relayEvent('keypress', this);
11015         }
11016        /* if(this.grow){
11017             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11018             this.el.on("click", this.autoSize,  this);
11019         }
11020         */
11021         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11022             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11023         }
11024         
11025         if (typeof(this.before) == 'object') {
11026             this.before.render(this.el.select('.roo-input-before',true).first());
11027         }
11028         if (typeof(this.after) == 'object') {
11029             this.after.render(this.el.select('.roo-input-after',true).first());
11030         }
11031         
11032         this.inputEl().on('change', this.onChange, this);
11033         
11034     },
11035     filterValidation : function(e){
11036         if(!e.isNavKeyPress()){
11037             this.validationTask.delay(this.validationDelay);
11038         }
11039     },
11040      /**
11041      * Validates the field value
11042      * @return {Boolean} True if the value is valid, else false
11043      */
11044     validate : function(){
11045         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11046         if(this.disabled || this.validateValue(this.getRawValue())){
11047             this.markValid();
11048             return true;
11049         }
11050         
11051         this.markInvalid();
11052         return false;
11053     },
11054     
11055     
11056     /**
11057      * Validates a value according to the field's validation rules and marks the field as invalid
11058      * if the validation fails
11059      * @param {Mixed} value The value to validate
11060      * @return {Boolean} True if the value is valid, else false
11061      */
11062     validateValue : function(value)
11063     {
11064         if(this.getVisibilityEl().hasClass('hidden')){
11065             return true;
11066         }
11067         
11068         if(value.length < 1)  { // if it's blank
11069             if(this.allowBlank){
11070                 return true;
11071             }
11072             return false;
11073         }
11074         
11075         if(value.length < this.minLength){
11076             return false;
11077         }
11078         if(value.length > this.maxLength){
11079             return false;
11080         }
11081         if(this.vtype){
11082             var vt = Roo.form.VTypes;
11083             if(!vt[this.vtype](value, this)){
11084                 return false;
11085             }
11086         }
11087         if(typeof this.validator == "function"){
11088             var msg = this.validator(value);
11089             if(msg !== true){
11090                 return false;
11091             }
11092             if (typeof(msg) == 'string') {
11093                 this.invalidText = msg;
11094             }
11095         }
11096         
11097         if(this.regex && !this.regex.test(value)){
11098             return false;
11099         }
11100         
11101         return true;
11102     },
11103     
11104      // private
11105     fireKey : function(e){
11106         //Roo.log('field ' + e.getKey());
11107         if(e.isNavKeyPress()){
11108             this.fireEvent("specialkey", this, e);
11109         }
11110     },
11111     focus : function (selectText){
11112         if(this.rendered){
11113             this.inputEl().focus();
11114             if(selectText === true){
11115                 this.inputEl().dom.select();
11116             }
11117         }
11118         return this;
11119     } ,
11120     
11121     onFocus : function(){
11122         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11123            // this.el.addClass(this.focusClass);
11124         }
11125         if(!this.hasFocus){
11126             this.hasFocus = true;
11127             this.startValue = this.getValue();
11128             this.fireEvent("focus", this);
11129         }
11130     },
11131     
11132     beforeBlur : Roo.emptyFn,
11133
11134     
11135     // private
11136     onBlur : function(){
11137         this.beforeBlur();
11138         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11139             //this.el.removeClass(this.focusClass);
11140         }
11141         this.hasFocus = false;
11142         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11143             this.validate();
11144         }
11145         var v = this.getValue();
11146         if(String(v) !== String(this.startValue)){
11147             this.fireEvent('change', this, v, this.startValue);
11148         }
11149         this.fireEvent("blur", this);
11150     },
11151     
11152     onChange : function(e)
11153     {
11154         var v = this.getValue();
11155         if(String(v) !== String(this.startValue)){
11156             this.fireEvent('change', this, v, this.startValue);
11157         }
11158         
11159     },
11160     
11161     /**
11162      * Resets the current field value to the originally loaded value and clears any validation messages
11163      */
11164     reset : function(){
11165         this.setValue(this.originalValue);
11166         this.validate();
11167     },
11168      /**
11169      * Returns the name of the field
11170      * @return {Mixed} name The name field
11171      */
11172     getName: function(){
11173         return this.name;
11174     },
11175      /**
11176      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11177      * @return {Mixed} value The field value
11178      */
11179     getValue : function(){
11180         
11181         var v = this.inputEl().getValue();
11182         
11183         return v;
11184     },
11185     /**
11186      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getRawValue : function(){
11190         var v = this.inputEl().getValue();
11191         
11192         return v;
11193     },
11194     
11195     /**
11196      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11197      * @param {Mixed} value The value to set
11198      */
11199     setRawValue : function(v){
11200         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11201     },
11202     
11203     selectText : function(start, end){
11204         var v = this.getRawValue();
11205         if(v.length > 0){
11206             start = start === undefined ? 0 : start;
11207             end = end === undefined ? v.length : end;
11208             var d = this.inputEl().dom;
11209             if(d.setSelectionRange){
11210                 d.setSelectionRange(start, end);
11211             }else if(d.createTextRange){
11212                 var range = d.createTextRange();
11213                 range.moveStart("character", start);
11214                 range.moveEnd("character", v.length-end);
11215                 range.select();
11216             }
11217         }
11218     },
11219     
11220     /**
11221      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11222      * @param {Mixed} value The value to set
11223      */
11224     setValue : function(v){
11225         this.value = v;
11226         if(this.rendered){
11227             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11228             this.validate();
11229         }
11230     },
11231     
11232     /*
11233     processValue : function(value){
11234         if(this.stripCharsRe){
11235             var newValue = value.replace(this.stripCharsRe, '');
11236             if(newValue !== value){
11237                 this.setRawValue(newValue);
11238                 return newValue;
11239             }
11240         }
11241         return value;
11242     },
11243   */
11244     preFocus : function(){
11245         
11246         if(this.selectOnFocus){
11247             this.inputEl().dom.select();
11248         }
11249     },
11250     filterKeys : function(e){
11251         var k = e.getKey();
11252         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11253             return;
11254         }
11255         var c = e.getCharCode(), cc = String.fromCharCode(c);
11256         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11257             return;
11258         }
11259         if(!this.maskRe.test(cc)){
11260             e.stopEvent();
11261         }
11262     },
11263      /**
11264      * Clear any invalid styles/messages for this field
11265      */
11266     clearInvalid : function(){
11267         
11268         if(!this.el || this.preventMark){ // not rendered
11269             return;
11270         }
11271         
11272         
11273         this.el.removeClass([this.invalidClass, 'is-invalid']);
11274         
11275         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11276             
11277             var feedback = this.el.select('.form-control-feedback', true).first();
11278             
11279             if(feedback){
11280                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11281             }
11282             
11283         }
11284         
11285         if(this.indicator){
11286             this.indicator.removeClass('visible');
11287             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11288         }
11289         
11290         this.fireEvent('valid', this);
11291     },
11292     
11293      /**
11294      * Mark this field as valid
11295      */
11296     markValid : function()
11297     {
11298         if(!this.el  || this.preventMark){ // not rendered...
11299             return;
11300         }
11301         
11302         this.el.removeClass([this.invalidClass, this.validClass]);
11303         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11304
11305         var feedback = this.el.select('.form-control-feedback', true).first();
11306             
11307         if(feedback){
11308             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11309         }
11310         
11311         if(this.indicator){
11312             this.indicator.removeClass('visible');
11313             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11314         }
11315         
11316         if(this.disabled){
11317             return;
11318         }
11319         
11320            
11321         if(this.allowBlank && !this.getRawValue().length){
11322             return;
11323         }
11324         if (Roo.bootstrap.version == 3) {
11325             this.el.addClass(this.validClass);
11326         } else {
11327             this.inputEl().addClass('is-valid');
11328         }
11329
11330         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11331             
11332             var feedback = this.el.select('.form-control-feedback', true).first();
11333             
11334             if(feedback){
11335                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11336                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11337             }
11338             
11339         }
11340         
11341         this.fireEvent('valid', this);
11342     },
11343     
11344      /**
11345      * Mark this field as invalid
11346      * @param {String} msg The validation message
11347      */
11348     markInvalid : function(msg)
11349     {
11350         if(!this.el  || this.preventMark){ // not rendered
11351             return;
11352         }
11353         
11354         this.el.removeClass([this.invalidClass, this.validClass]);
11355         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11356         
11357         var feedback = this.el.select('.form-control-feedback', true).first();
11358             
11359         if(feedback){
11360             this.el.select('.form-control-feedback', true).first().removeClass(
11361                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11362         }
11363
11364         if(this.disabled){
11365             return;
11366         }
11367         
11368         if(this.allowBlank && !this.getRawValue().length){
11369             return;
11370         }
11371         
11372         if(this.indicator){
11373             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11374             this.indicator.addClass('visible');
11375         }
11376         if (Roo.bootstrap.version == 3) {
11377             this.el.addClass(this.invalidClass);
11378         } else {
11379             this.inputEl().addClass('is-invalid');
11380         }
11381         
11382         
11383         
11384         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11385             
11386             var feedback = this.el.select('.form-control-feedback', true).first();
11387             
11388             if(feedback){
11389                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11390                 
11391                 if(this.getValue().length || this.forceFeedback){
11392                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11393                 }
11394                 
11395             }
11396             
11397         }
11398         
11399         this.fireEvent('invalid', this, msg);
11400     },
11401     // private
11402     SafariOnKeyDown : function(event)
11403     {
11404         // this is a workaround for a password hang bug on chrome/ webkit.
11405         if (this.inputEl().dom.type != 'password') {
11406             return;
11407         }
11408         
11409         var isSelectAll = false;
11410         
11411         if(this.inputEl().dom.selectionEnd > 0){
11412             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11413         }
11414         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11415             event.preventDefault();
11416             this.setValue('');
11417             return;
11418         }
11419         
11420         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11421             
11422             event.preventDefault();
11423             // this is very hacky as keydown always get's upper case.
11424             //
11425             var cc = String.fromCharCode(event.getCharCode());
11426             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11427             
11428         }
11429     },
11430     adjustWidth : function(tag, w){
11431         tag = tag.toLowerCase();
11432         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11433             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11434                 if(tag == 'input'){
11435                     return w + 2;
11436                 }
11437                 if(tag == 'textarea'){
11438                     return w-2;
11439                 }
11440             }else if(Roo.isOpera){
11441                 if(tag == 'input'){
11442                     return w + 2;
11443                 }
11444                 if(tag == 'textarea'){
11445                     return w-2;
11446                 }
11447             }
11448         }
11449         return w;
11450     },
11451     
11452     setFieldLabel : function(v)
11453     {
11454         if(!this.rendered){
11455             return;
11456         }
11457         
11458         if(this.indicatorEl()){
11459             var ar = this.el.select('label > span',true);
11460             
11461             if (ar.elements.length) {
11462                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11463                 this.fieldLabel = v;
11464                 return;
11465             }
11466             
11467             var br = this.el.select('label',true);
11468             
11469             if(br.elements.length) {
11470                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11471                 this.fieldLabel = v;
11472                 return;
11473             }
11474             
11475             Roo.log('Cannot Found any of label > span || label in input');
11476             return;
11477         }
11478         
11479         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11480         this.fieldLabel = v;
11481         
11482         
11483     }
11484 });
11485
11486  
11487 /*
11488  * - LGPL
11489  *
11490  * Input
11491  * 
11492  */
11493
11494 /**
11495  * @class Roo.bootstrap.TextArea
11496  * @extends Roo.bootstrap.Input
11497  * Bootstrap TextArea class
11498  * @cfg {Number} cols Specifies the visible width of a text area
11499  * @cfg {Number} rows Specifies the visible number of lines in a text area
11500  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11501  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11502  * @cfg {string} html text
11503  * 
11504  * @constructor
11505  * Create a new TextArea
11506  * @param {Object} config The config object
11507  */
11508
11509 Roo.bootstrap.TextArea = function(config){
11510     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11511    
11512 };
11513
11514 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11515      
11516     cols : false,
11517     rows : 5,
11518     readOnly : false,
11519     warp : 'soft',
11520     resize : false,
11521     value: false,
11522     html: false,
11523     
11524     getAutoCreate : function(){
11525         
11526         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11527         
11528         var id = Roo.id();
11529         
11530         var cfg = {};
11531         
11532         if(this.inputType != 'hidden'){
11533             cfg.cls = 'form-group' //input-group
11534         }
11535         
11536         var input =  {
11537             tag: 'textarea',
11538             id : id,
11539             warp : this.warp,
11540             rows : this.rows,
11541             value : this.value || '',
11542             html: this.html || '',
11543             cls : 'form-control',
11544             placeholder : this.placeholder || '' 
11545             
11546         };
11547         
11548         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11549             input.maxLength = this.maxLength;
11550         }
11551         
11552         if(this.resize){
11553             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11554         }
11555         
11556         if(this.cols){
11557             input.cols = this.cols;
11558         }
11559         
11560         if (this.readOnly) {
11561             input.readonly = true;
11562         }
11563         
11564         if (this.name) {
11565             input.name = this.name;
11566         }
11567         
11568         if (this.size) {
11569             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11570         }
11571         
11572         var settings=this;
11573         ['xs','sm','md','lg'].map(function(size){
11574             if (settings[size]) {
11575                 cfg.cls += ' col-' + size + '-' + settings[size];
11576             }
11577         });
11578         
11579         var inputblock = input;
11580         
11581         if(this.hasFeedback && !this.allowBlank){
11582             
11583             var feedback = {
11584                 tag: 'span',
11585                 cls: 'glyphicon form-control-feedback'
11586             };
11587
11588             inputblock = {
11589                 cls : 'has-feedback',
11590                 cn :  [
11591                     input,
11592                     feedback
11593                 ] 
11594             };  
11595         }
11596         
11597         
11598         if (this.before || this.after) {
11599             
11600             inputblock = {
11601                 cls : 'input-group',
11602                 cn :  [] 
11603             };
11604             if (this.before) {
11605                 inputblock.cn.push({
11606                     tag :'span',
11607                     cls : 'input-group-addon',
11608                     html : this.before
11609                 });
11610             }
11611             
11612             inputblock.cn.push(input);
11613             
11614             if(this.hasFeedback && !this.allowBlank){
11615                 inputblock.cls += ' has-feedback';
11616                 inputblock.cn.push(feedback);
11617             }
11618             
11619             if (this.after) {
11620                 inputblock.cn.push({
11621                     tag :'span',
11622                     cls : 'input-group-addon',
11623                     html : this.after
11624                 });
11625             }
11626             
11627         }
11628         
11629         if (align ==='left' && this.fieldLabel.length) {
11630             cfg.cn = [
11631                 {
11632                     tag: 'label',
11633                     'for' :  id,
11634                     cls : 'control-label',
11635                     html : this.fieldLabel
11636                 },
11637                 {
11638                     cls : "",
11639                     cn: [
11640                         inputblock
11641                     ]
11642                 }
11643
11644             ];
11645             
11646             if(this.labelWidth > 12){
11647                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11648             }
11649
11650             if(this.labelWidth < 13 && this.labelmd == 0){
11651                 this.labelmd = this.labelWidth;
11652             }
11653
11654             if(this.labellg > 0){
11655                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11656                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11657             }
11658
11659             if(this.labelmd > 0){
11660                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11661                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11662             }
11663
11664             if(this.labelsm > 0){
11665                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11666                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11667             }
11668
11669             if(this.labelxs > 0){
11670                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11671                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11672             }
11673             
11674         } else if ( this.fieldLabel.length) {
11675             cfg.cn = [
11676
11677                {
11678                    tag: 'label',
11679                    //cls : 'input-group-addon',
11680                    html : this.fieldLabel
11681
11682                },
11683
11684                inputblock
11685
11686            ];
11687
11688         } else {
11689
11690             cfg.cn = [
11691
11692                 inputblock
11693
11694             ];
11695                 
11696         }
11697         
11698         if (this.disabled) {
11699             input.disabled=true;
11700         }
11701         
11702         return cfg;
11703         
11704     },
11705     /**
11706      * return the real textarea element.
11707      */
11708     inputEl: function ()
11709     {
11710         return this.el.select('textarea.form-control',true).first();
11711     },
11712     
11713     /**
11714      * Clear any invalid styles/messages for this field
11715      */
11716     clearInvalid : function()
11717     {
11718         
11719         if(!this.el || this.preventMark){ // not rendered
11720             return;
11721         }
11722         
11723         var label = this.el.select('label', true).first();
11724         var icon = this.el.select('i.fa-star', true).first();
11725         
11726         if(label && icon){
11727             icon.remove();
11728         }
11729         this.el.removeClass( this.validClass);
11730         this.inputEl().removeClass('is-invalid');
11731          
11732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11733             
11734             var feedback = this.el.select('.form-control-feedback', true).first();
11735             
11736             if(feedback){
11737                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11738             }
11739             
11740         }
11741         
11742         this.fireEvent('valid', this);
11743     },
11744     
11745      /**
11746      * Mark this field as valid
11747      */
11748     markValid : function()
11749     {
11750         if(!this.el  || this.preventMark){ // not rendered
11751             return;
11752         }
11753         
11754         this.el.removeClass([this.invalidClass, this.validClass]);
11755         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11756         
11757         var feedback = this.el.select('.form-control-feedback', true).first();
11758             
11759         if(feedback){
11760             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11761         }
11762
11763         if(this.disabled || this.allowBlank){
11764             return;
11765         }
11766         
11767         var label = this.el.select('label', true).first();
11768         var icon = this.el.select('i.fa-star', true).first();
11769         
11770         if(label && icon){
11771             icon.remove();
11772         }
11773         if (Roo.bootstrap.version == 3) {
11774             this.el.addClass(this.validClass);
11775         } else {
11776             this.inputEl().addClass('is-valid');
11777         }
11778         
11779         
11780         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11781             
11782             var feedback = this.el.select('.form-control-feedback', true).first();
11783             
11784             if(feedback){
11785                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11786                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11787             }
11788             
11789         }
11790         
11791         this.fireEvent('valid', this);
11792     },
11793     
11794      /**
11795      * Mark this field as invalid
11796      * @param {String} msg The validation message
11797      */
11798     markInvalid : function(msg)
11799     {
11800         if(!this.el  || this.preventMark){ // not rendered
11801             return;
11802         }
11803         
11804         this.el.removeClass([this.invalidClass, this.validClass]);
11805         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11806         
11807         var feedback = this.el.select('.form-control-feedback', true).first();
11808             
11809         if(feedback){
11810             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11811         }
11812
11813         if(this.disabled || this.allowBlank){
11814             return;
11815         }
11816         
11817         var label = this.el.select('label', true).first();
11818         var icon = this.el.select('i.fa-star', true).first();
11819         
11820         if(!this.getValue().length && label && !icon){
11821             this.el.createChild({
11822                 tag : 'i',
11823                 cls : 'text-danger fa fa-lg fa-star',
11824                 tooltip : 'This field is required',
11825                 style : 'margin-right:5px;'
11826             }, label, true);
11827         }
11828         
11829         if (Roo.bootstrap.version == 3) {
11830             this.el.addClass(this.invalidClass);
11831         } else {
11832             this.inputEl().addClass('is-invalid');
11833         }
11834         
11835         // fixme ... this may be depricated need to test..
11836         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11837             
11838             var feedback = this.el.select('.form-control-feedback', true).first();
11839             
11840             if(feedback){
11841                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11842                 
11843                 if(this.getValue().length || this.forceFeedback){
11844                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11845                 }
11846                 
11847             }
11848             
11849         }
11850         
11851         this.fireEvent('invalid', this, msg);
11852     }
11853 });
11854
11855  
11856 /*
11857  * - LGPL
11858  *
11859  * trigger field - base class for combo..
11860  * 
11861  */
11862  
11863 /**
11864  * @class Roo.bootstrap.TriggerField
11865  * @extends Roo.bootstrap.Input
11866  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11867  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11868  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11869  * for which you can provide a custom implementation.  For example:
11870  * <pre><code>
11871 var trigger = new Roo.bootstrap.TriggerField();
11872 trigger.onTriggerClick = myTriggerFn;
11873 trigger.applyTo('my-field');
11874 </code></pre>
11875  *
11876  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11877  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11878  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11879  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11880  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11881
11882  * @constructor
11883  * Create a new TriggerField.
11884  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11885  * to the base TextField)
11886  */
11887 Roo.bootstrap.TriggerField = function(config){
11888     this.mimicing = false;
11889     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11890 };
11891
11892 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11893     /**
11894      * @cfg {String} triggerClass A CSS class to apply to the trigger
11895      */
11896      /**
11897      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11898      */
11899     hideTrigger:false,
11900
11901     /**
11902      * @cfg {Boolean} removable (true|false) special filter default false
11903      */
11904     removable : false,
11905     
11906     /** @cfg {Boolean} grow @hide */
11907     /** @cfg {Number} growMin @hide */
11908     /** @cfg {Number} growMax @hide */
11909
11910     /**
11911      * @hide 
11912      * @method
11913      */
11914     autoSize: Roo.emptyFn,
11915     // private
11916     monitorTab : true,
11917     // private
11918     deferHeight : true,
11919
11920     
11921     actionMode : 'wrap',
11922     
11923     caret : false,
11924     
11925     
11926     getAutoCreate : function(){
11927        
11928         var align = this.labelAlign || this.parentLabelAlign();
11929         
11930         var id = Roo.id();
11931         
11932         var cfg = {
11933             cls: 'form-group' //input-group
11934         };
11935         
11936         
11937         var input =  {
11938             tag: 'input',
11939             id : id,
11940             type : this.inputType,
11941             cls : 'form-control',
11942             autocomplete: 'new-password',
11943             placeholder : this.placeholder || '' 
11944             
11945         };
11946         if (this.name) {
11947             input.name = this.name;
11948         }
11949         if (this.size) {
11950             input.cls += ' input-' + this.size;
11951         }
11952         
11953         if (this.disabled) {
11954             input.disabled=true;
11955         }
11956         
11957         var inputblock = input;
11958         
11959         if(this.hasFeedback && !this.allowBlank){
11960             
11961             var feedback = {
11962                 tag: 'span',
11963                 cls: 'glyphicon form-control-feedback'
11964             };
11965             
11966             if(this.removable && !this.editable  ){
11967                 inputblock = {
11968                     cls : 'has-feedback',
11969                     cn :  [
11970                         inputblock,
11971                         {
11972                             tag: 'button',
11973                             html : 'x',
11974                             cls : 'roo-combo-removable-btn close'
11975                         },
11976                         feedback
11977                     ] 
11978                 };
11979             } else {
11980                 inputblock = {
11981                     cls : 'has-feedback',
11982                     cn :  [
11983                         inputblock,
11984                         feedback
11985                     ] 
11986                 };
11987             }
11988
11989         } else {
11990             if(this.removable && !this.editable ){
11991                 inputblock = {
11992                     cls : 'roo-removable',
11993                     cn :  [
11994                         inputblock,
11995                         {
11996                             tag: 'button',
11997                             html : 'x',
11998                             cls : 'roo-combo-removable-btn close'
11999                         }
12000                     ] 
12001                 };
12002             }
12003         }
12004         
12005         if (this.before || this.after) {
12006             
12007             inputblock = {
12008                 cls : 'input-group',
12009                 cn :  [] 
12010             };
12011             if (this.before) {
12012                 inputblock.cn.push({
12013                     tag :'span',
12014                     cls : 'input-group-addon input-group-prepend input-group-text',
12015                     html : this.before
12016                 });
12017             }
12018             
12019             inputblock.cn.push(input);
12020             
12021             if(this.hasFeedback && !this.allowBlank){
12022                 inputblock.cls += ' has-feedback';
12023                 inputblock.cn.push(feedback);
12024             }
12025             
12026             if (this.after) {
12027                 inputblock.cn.push({
12028                     tag :'span',
12029                     cls : 'input-group-addon input-group-append input-group-text',
12030                     html : this.after
12031                 });
12032             }
12033             
12034         };
12035         
12036       
12037         
12038         var ibwrap = inputblock;
12039         
12040         if(this.multiple){
12041             ibwrap = {
12042                 tag: 'ul',
12043                 cls: 'roo-select2-choices',
12044                 cn:[
12045                     {
12046                         tag: 'li',
12047                         cls: 'roo-select2-search-field',
12048                         cn: [
12049
12050                             inputblock
12051                         ]
12052                     }
12053                 ]
12054             };
12055                 
12056         }
12057         
12058         var combobox = {
12059             cls: 'roo-select2-container input-group',
12060             cn: [
12061                  {
12062                     tag: 'input',
12063                     type : 'hidden',
12064                     cls: 'form-hidden-field'
12065                 },
12066                 ibwrap
12067             ]
12068         };
12069         
12070         if(!this.multiple && this.showToggleBtn){
12071             
12072             var caret = {
12073                         tag: 'span',
12074                         cls: 'caret'
12075              };
12076             if (this.caret != false) {
12077                 caret = {
12078                      tag: 'i',
12079                      cls: 'fa fa-' + this.caret
12080                 };
12081                 
12082             }
12083             
12084             combobox.cn.push({
12085                 tag :'span',
12086                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12087                 cn : [
12088                     Roo.bootstrap.version == 3 ? caret : '',
12089                     {
12090                         tag: 'span',
12091                         cls: 'combobox-clear',
12092                         cn  : [
12093                             {
12094                                 tag : 'i',
12095                                 cls: 'icon-remove'
12096                             }
12097                         ]
12098                     }
12099                 ]
12100
12101             })
12102         }
12103         
12104         if(this.multiple){
12105             combobox.cls += ' roo-select2-container-multi';
12106         }
12107          var indicator = {
12108             tag : 'i',
12109             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12110             tooltip : 'This field is required'
12111         };
12112         if (Roo.bootstrap.version == 4) {
12113             indicator = {
12114                 tag : 'i',
12115                 style : 'display:none'
12116             };
12117         }
12118         
12119         
12120         if (align ==='left' && this.fieldLabel.length) {
12121             
12122             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12123
12124             cfg.cn = [
12125                 indicator,
12126                 {
12127                     tag: 'label',
12128                     'for' :  id,
12129                     cls : 'control-label',
12130                     html : this.fieldLabel
12131
12132                 },
12133                 {
12134                     cls : "", 
12135                     cn: [
12136                         combobox
12137                     ]
12138                 }
12139
12140             ];
12141             
12142             var labelCfg = cfg.cn[1];
12143             var contentCfg = cfg.cn[2];
12144             
12145             if(this.indicatorpos == 'right'){
12146                 cfg.cn = [
12147                     {
12148                         tag: 'label',
12149                         'for' :  id,
12150                         cls : 'control-label',
12151                         cn : [
12152                             {
12153                                 tag : 'span',
12154                                 html : this.fieldLabel
12155                             },
12156                             indicator
12157                         ]
12158                     },
12159                     {
12160                         cls : "", 
12161                         cn: [
12162                             combobox
12163                         ]
12164                     }
12165
12166                 ];
12167                 
12168                 labelCfg = cfg.cn[0];
12169                 contentCfg = cfg.cn[1];
12170             }
12171             
12172             if(this.labelWidth > 12){
12173                 labelCfg.style = "width: " + this.labelWidth + 'px';
12174             }
12175             
12176             if(this.labelWidth < 13 && this.labelmd == 0){
12177                 this.labelmd = this.labelWidth;
12178             }
12179             
12180             if(this.labellg > 0){
12181                 labelCfg.cls += ' col-lg-' + this.labellg;
12182                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12183             }
12184             
12185             if(this.labelmd > 0){
12186                 labelCfg.cls += ' col-md-' + this.labelmd;
12187                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12188             }
12189             
12190             if(this.labelsm > 0){
12191                 labelCfg.cls += ' col-sm-' + this.labelsm;
12192                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12193             }
12194             
12195             if(this.labelxs > 0){
12196                 labelCfg.cls += ' col-xs-' + this.labelxs;
12197                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12198             }
12199             
12200         } else if ( this.fieldLabel.length) {
12201 //                Roo.log(" label");
12202             cfg.cn = [
12203                 indicator,
12204                {
12205                    tag: 'label',
12206                    //cls : 'input-group-addon',
12207                    html : this.fieldLabel
12208
12209                },
12210
12211                combobox
12212
12213             ];
12214             
12215             if(this.indicatorpos == 'right'){
12216                 
12217                 cfg.cn = [
12218                     {
12219                        tag: 'label',
12220                        cn : [
12221                            {
12222                                tag : 'span',
12223                                html : this.fieldLabel
12224                            },
12225                            indicator
12226                        ]
12227
12228                     },
12229                     combobox
12230
12231                 ];
12232
12233             }
12234
12235         } else {
12236             
12237 //                Roo.log(" no label && no align");
12238                 cfg = combobox
12239                      
12240                 
12241         }
12242         
12243         var settings=this;
12244         ['xs','sm','md','lg'].map(function(size){
12245             if (settings[size]) {
12246                 cfg.cls += ' col-' + size + '-' + settings[size];
12247             }
12248         });
12249         
12250         return cfg;
12251         
12252     },
12253     
12254     
12255     
12256     // private
12257     onResize : function(w, h){
12258 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12259 //        if(typeof w == 'number'){
12260 //            var x = w - this.trigger.getWidth();
12261 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12262 //            this.trigger.setStyle('left', x+'px');
12263 //        }
12264     },
12265
12266     // private
12267     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12268
12269     // private
12270     getResizeEl : function(){
12271         return this.inputEl();
12272     },
12273
12274     // private
12275     getPositionEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     alignErrorIcon : function(){
12281         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12282     },
12283
12284     // private
12285     initEvents : function(){
12286         
12287         this.createList();
12288         
12289         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12290         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12291         if(!this.multiple && this.showToggleBtn){
12292             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12293             if(this.hideTrigger){
12294                 this.trigger.setDisplayed(false);
12295             }
12296             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12297         }
12298         
12299         if(this.multiple){
12300             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12301         }
12302         
12303         if(this.removable && !this.editable && !this.tickable){
12304             var close = this.closeTriggerEl();
12305             
12306             if(close){
12307                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12308                 close.on('click', this.removeBtnClick, this, close);
12309             }
12310         }
12311         
12312         //this.trigger.addClassOnOver('x-form-trigger-over');
12313         //this.trigger.addClassOnClick('x-form-trigger-click');
12314         
12315         //if(!this.width){
12316         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12317         //}
12318     },
12319     
12320     closeTriggerEl : function()
12321     {
12322         var close = this.el.select('.roo-combo-removable-btn', true).first();
12323         return close ? close : false;
12324     },
12325     
12326     removeBtnClick : function(e, h, el)
12327     {
12328         e.preventDefault();
12329         
12330         if(this.fireEvent("remove", this) !== false){
12331             this.reset();
12332             this.fireEvent("afterremove", this)
12333         }
12334     },
12335     
12336     createList : function()
12337     {
12338         this.list = Roo.get(document.body).createChild({
12339             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12340             cls: 'typeahead typeahead-long dropdown-menu',
12341             style: 'display:none'
12342         });
12343         
12344         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12345         
12346     },
12347
12348     // private
12349     initTrigger : function(){
12350        
12351     },
12352
12353     // private
12354     onDestroy : function(){
12355         if(this.trigger){
12356             this.trigger.removeAllListeners();
12357           //  this.trigger.remove();
12358         }
12359         //if(this.wrap){
12360         //    this.wrap.remove();
12361         //}
12362         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12363     },
12364
12365     // private
12366     onFocus : function(){
12367         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12368         /*
12369         if(!this.mimicing){
12370             this.wrap.addClass('x-trigger-wrap-focus');
12371             this.mimicing = true;
12372             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12373             if(this.monitorTab){
12374                 this.el.on("keydown", this.checkTab, this);
12375             }
12376         }
12377         */
12378     },
12379
12380     // private
12381     checkTab : function(e){
12382         if(e.getKey() == e.TAB){
12383             this.triggerBlur();
12384         }
12385     },
12386
12387     // private
12388     onBlur : function(){
12389         // do nothing
12390     },
12391
12392     // private
12393     mimicBlur : function(e, t){
12394         /*
12395         if(!this.wrap.contains(t) && this.validateBlur()){
12396             this.triggerBlur();
12397         }
12398         */
12399     },
12400
12401     // private
12402     triggerBlur : function(){
12403         this.mimicing = false;
12404         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12405         if(this.monitorTab){
12406             this.el.un("keydown", this.checkTab, this);
12407         }
12408         //this.wrap.removeClass('x-trigger-wrap-focus');
12409         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12410     },
12411
12412     // private
12413     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12414     validateBlur : function(e, t){
12415         return true;
12416     },
12417
12418     // private
12419     onDisable : function(){
12420         this.inputEl().dom.disabled = true;
12421         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12422         //if(this.wrap){
12423         //    this.wrap.addClass('x-item-disabled');
12424         //}
12425     },
12426
12427     // private
12428     onEnable : function(){
12429         this.inputEl().dom.disabled = false;
12430         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12431         //if(this.wrap){
12432         //    this.el.removeClass('x-item-disabled');
12433         //}
12434     },
12435
12436     // private
12437     onShow : function(){
12438         var ae = this.getActionEl();
12439         
12440         if(ae){
12441             ae.dom.style.display = '';
12442             ae.dom.style.visibility = 'visible';
12443         }
12444     },
12445
12446     // private
12447     
12448     onHide : function(){
12449         var ae = this.getActionEl();
12450         ae.dom.style.display = 'none';
12451     },
12452
12453     /**
12454      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12455      * by an implementing function.
12456      * @method
12457      * @param {EventObject} e
12458      */
12459     onTriggerClick : Roo.emptyFn
12460 });
12461  
12462 /*
12463 * Licence: LGPL
12464 */
12465
12466 /**
12467  * @class Roo.bootstrap.CardUploader
12468  * @extends Roo.bootstrap.Button
12469  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12470  * @cfg {Number} errorTimeout default 3000
12471  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12472  * @cfg {Array}  html The button text.
12473
12474  *
12475  * @constructor
12476  * Create a new CardUploader
12477  * @param {Object} config The config object
12478  */
12479
12480 Roo.bootstrap.CardUploader = function(config){
12481     
12482  
12483     
12484     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12485     
12486     
12487     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12488         return r.data.id
12489         });
12490     
12491     
12492 };
12493
12494 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12495     
12496      
12497     errorTimeout : 3000,
12498      
12499     images : false,
12500    
12501     fileCollection : false,
12502     allowBlank : true,
12503     
12504     getAutoCreate : function()
12505     {
12506         
12507         var cfg =  {
12508             cls :'form-group' ,
12509             cn : [
12510                
12511                 {
12512                     tag: 'label',
12513                    //cls : 'input-group-addon',
12514                     html : this.fieldLabel
12515
12516                 },
12517
12518                 {
12519                     tag: 'input',
12520                     type : 'hidden',
12521                     value : this.value,
12522                     cls : 'd-none  form-control'
12523                 },
12524                 
12525                 {
12526                     tag: 'input',
12527                     multiple : 'multiple',
12528                     type : 'file',
12529                     cls : 'd-none  roo-card-upload-selector'
12530                 },
12531                 
12532                 {
12533                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12534                 },
12535                 {
12536                     cls : 'card-columns roo-card-uploader-container'
12537                 }
12538
12539             ]
12540         };
12541            
12542          
12543         return cfg;
12544     },
12545     
12546     getChildContainer : function() /// what children are added to.
12547     {
12548         return this.containerEl;
12549     },
12550    
12551     getButtonContainer : function() /// what children are added to.
12552     {
12553         return this.el.select(".roo-card-uploader-button-container").first();
12554     },
12555    
12556     initEvents : function()
12557     {
12558         
12559         Roo.bootstrap.Input.prototype.initEvents.call(this);
12560         
12561         var t = this;
12562         this.addxtype({
12563             xns: Roo.bootstrap,
12564
12565             xtype : 'Button',
12566             container_method : 'getButtonContainer' ,            
12567             html :  this.html, // fix changable?
12568             cls : 'w-100 ',
12569             listeners : {
12570                 'click' : function(btn, e) {
12571                     t.onClick(e);
12572                 }
12573             }
12574         });
12575         
12576         
12577         
12578         
12579         this.urlAPI = (window.createObjectURL && window) || 
12580                                 (window.URL && URL.revokeObjectURL && URL) || 
12581                                 (window.webkitURL && webkitURL);
12582                         
12583          
12584          
12585          
12586         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12587         
12588         this.selectorEl.on('change', this.onFileSelected, this);
12589         if (this.images) {
12590             var t = this;
12591             this.images.forEach(function(img) {
12592                 t.addCard(img)
12593             });
12594             this.images = false;
12595         }
12596         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12597          
12598        
12599     },
12600     
12601    
12602     onClick : function(e)
12603     {
12604         e.preventDefault();
12605          
12606         this.selectorEl.dom.click();
12607          
12608     },
12609     
12610     onFileSelected : function(e)
12611     {
12612         e.preventDefault();
12613         
12614         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12615             return;
12616         }
12617         
12618         Roo.each(this.selectorEl.dom.files, function(file){    
12619             this.addFile(file);
12620         }, this);
12621          
12622     },
12623     
12624       
12625     
12626       
12627     
12628     addFile : function(file)
12629     {
12630            
12631         if(typeof(file) === 'string'){
12632             throw "Add file by name?"; // should not happen
12633             return;
12634         }
12635         
12636         if(!file || !this.urlAPI){
12637             return;
12638         }
12639         
12640         // file;
12641         // file.type;
12642         
12643         var _this = this;
12644         
12645         
12646         var url = _this.urlAPI.createObjectURL( file);
12647            
12648         this.addCard({
12649             id : Roo.bootstrap.CardUploader.ID--,
12650             is_uploaded : false,
12651             src : url,
12652             title : file.name,
12653             mimetype : file.type,
12654             preview : false,
12655             is_deleted : 0
12656         })
12657         
12658     },
12659     
12660     addCard : function (data)
12661     {
12662         // hidden input element?
12663         // if the file is not an image...
12664         //then we need to use something other that and header_image
12665         var t = this;
12666         //   remove.....
12667         var footer = [
12668             {
12669                 xns : Roo.bootstrap,
12670                 xtype : 'CardFooter',
12671                 items: [
12672                     {
12673                         xns : Roo.bootstrap,
12674                         xtype : 'Element',
12675                         cls : 'd-flex',
12676                         items : [
12677                             
12678                             {
12679                                 xns : Roo.bootstrap,
12680                                 xtype : 'Button',
12681                                 html : String.format("<small>{0}</small>", data.title),
12682                                 cls : 'col-11 text-left',
12683                                 size: 'sm',
12684                                 weight: 'link',
12685                                 fa : 'download',
12686                                 listeners : {
12687                                     click : function() {
12688                                         this.downloadCard(data.id)
12689                                     }
12690                                 }
12691                             },
12692                           
12693                             {
12694                                 xns : Roo.bootstrap,
12695                                 xtype : 'Button',
12696                                 
12697                                 size : 'sm',
12698                                 weight: 'danger',
12699                                 cls : 'col-1',
12700                                 fa : 'times',
12701                                 listeners : {
12702                                     click : function() {
12703                                         t.removeCard(data.id)
12704                                     }
12705                                 }
12706                             }
12707                         ]
12708                     }
12709                     
12710                 ] 
12711             }
12712             
12713         ];
12714
12715         var cn = this.addxtype(
12716             {
12717                  
12718                 xns : Roo.bootstrap,
12719                 xtype : 'Card',
12720                 closeable : true,
12721                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12722                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12723                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12724                 data : data,
12725                 html : false,
12726                  
12727                 items : footer,
12728                 initEvents : function() {
12729                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12730                     this.imgEl = this.el.select('.card-img-top').first();
12731                     if (this.imgEl) {
12732                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12733                         this.imgEl.set({ 'pointer' : 'cursor' });
12734                                   
12735                     }
12736                     
12737                   
12738                 }
12739                 
12740             }
12741         );
12742         // dont' really need ot update items.
12743         // this.items.push(cn);
12744         this.fileCollection.add(cn);
12745         this.updateInput();
12746         
12747     },
12748     removeCard : function(id)
12749     {
12750         
12751         var card  = this.fileCollection.get(id);
12752         card.data.is_deleted = 1;
12753         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12754         this.fileCollection.remove(card);
12755         //this.items = this.items.filter(function(e) { return e != card });
12756         // dont' really need ot update items.
12757         card.el.dom.parentNode.removeChild(card.el.dom);
12758         
12759     },
12760     reset: function()
12761     {
12762         this.fileCollection.each(function(card) {
12763             card.el.dom.parentNode.removeChild(card.el.dom);    
12764         });
12765         this.fileCollection.clear();
12766         this.updateInput();
12767     },
12768     
12769     updateInput : function()
12770     {
12771         var data = [];
12772         this.fileCollection.each(function(e) {
12773             data.push(e.data);
12774         });
12775         
12776         this.inputEl().dom.value = JSON.stringify(data);
12777     }
12778     
12779     
12780 });
12781
12782
12783 Roo.bootstrap.CardUploader.ID = -1;/*
12784  * Based on:
12785  * Ext JS Library 1.1.1
12786  * Copyright(c) 2006-2007, Ext JS, LLC.
12787  *
12788  * Originally Released Under LGPL - original licence link has changed is not relivant.
12789  *
12790  * Fork - LGPL
12791  * <script type="text/javascript">
12792  */
12793
12794
12795 /**
12796  * @class Roo.data.SortTypes
12797  * @singleton
12798  * Defines the default sorting (casting?) comparison functions used when sorting data.
12799  */
12800 Roo.data.SortTypes = {
12801     /**
12802      * Default sort that does nothing
12803      * @param {Mixed} s The value being converted
12804      * @return {Mixed} The comparison value
12805      */
12806     none : function(s){
12807         return s;
12808     },
12809     
12810     /**
12811      * The regular expression used to strip tags
12812      * @type {RegExp}
12813      * @property
12814      */
12815     stripTagsRE : /<\/?[^>]+>/gi,
12816     
12817     /**
12818      * Strips all HTML tags to sort on text only
12819      * @param {Mixed} s The value being converted
12820      * @return {String} The comparison value
12821      */
12822     asText : function(s){
12823         return String(s).replace(this.stripTagsRE, "");
12824     },
12825     
12826     /**
12827      * Strips all HTML tags to sort on text only - Case insensitive
12828      * @param {Mixed} s The value being converted
12829      * @return {String} The comparison value
12830      */
12831     asUCText : function(s){
12832         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12833     },
12834     
12835     /**
12836      * Case insensitive string
12837      * @param {Mixed} s The value being converted
12838      * @return {String} The comparison value
12839      */
12840     asUCString : function(s) {
12841         return String(s).toUpperCase();
12842     },
12843     
12844     /**
12845      * Date sorting
12846      * @param {Mixed} s The value being converted
12847      * @return {Number} The comparison value
12848      */
12849     asDate : function(s) {
12850         if(!s){
12851             return 0;
12852         }
12853         if(s instanceof Date){
12854             return s.getTime();
12855         }
12856         return Date.parse(String(s));
12857     },
12858     
12859     /**
12860      * Float sorting
12861      * @param {Mixed} s The value being converted
12862      * @return {Float} The comparison value
12863      */
12864     asFloat : function(s) {
12865         var val = parseFloat(String(s).replace(/,/g, ""));
12866         if(isNaN(val)) {
12867             val = 0;
12868         }
12869         return val;
12870     },
12871     
12872     /**
12873      * Integer sorting
12874      * @param {Mixed} s The value being converted
12875      * @return {Number} The comparison value
12876      */
12877     asInt : function(s) {
12878         var val = parseInt(String(s).replace(/,/g, ""));
12879         if(isNaN(val)) {
12880             val = 0;
12881         }
12882         return val;
12883     }
12884 };/*
12885  * Based on:
12886  * Ext JS Library 1.1.1
12887  * Copyright(c) 2006-2007, Ext JS, LLC.
12888  *
12889  * Originally Released Under LGPL - original licence link has changed is not relivant.
12890  *
12891  * Fork - LGPL
12892  * <script type="text/javascript">
12893  */
12894
12895 /**
12896 * @class Roo.data.Record
12897  * Instances of this class encapsulate both record <em>definition</em> information, and record
12898  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12899  * to access Records cached in an {@link Roo.data.Store} object.<br>
12900  * <p>
12901  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12902  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12903  * objects.<br>
12904  * <p>
12905  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12906  * @constructor
12907  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12908  * {@link #create}. The parameters are the same.
12909  * @param {Array} data An associative Array of data values keyed by the field name.
12910  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12911  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12912  * not specified an integer id is generated.
12913  */
12914 Roo.data.Record = function(data, id){
12915     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12916     this.data = data;
12917 };
12918
12919 /**
12920  * Generate a constructor for a specific record layout.
12921  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12922  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12923  * Each field definition object may contain the following properties: <ul>
12924  * <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,
12925  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12926  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12927  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12928  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12929  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12930  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12931  * this may be omitted.</p></li>
12932  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12933  * <ul><li>auto (Default, implies no conversion)</li>
12934  * <li>string</li>
12935  * <li>int</li>
12936  * <li>float</li>
12937  * <li>boolean</li>
12938  * <li>date</li></ul></p></li>
12939  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12940  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12941  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12942  * by the Reader into an object that will be stored in the Record. It is passed the
12943  * following parameters:<ul>
12944  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12945  * </ul></p></li>
12946  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12947  * </ul>
12948  * <br>usage:<br><pre><code>
12949 var TopicRecord = Roo.data.Record.create(
12950     {name: 'title', mapping: 'topic_title'},
12951     {name: 'author', mapping: 'username'},
12952     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12953     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12954     {name: 'lastPoster', mapping: 'user2'},
12955     {name: 'excerpt', mapping: 'post_text'}
12956 );
12957
12958 var myNewRecord = new TopicRecord({
12959     title: 'Do my job please',
12960     author: 'noobie',
12961     totalPosts: 1,
12962     lastPost: new Date(),
12963     lastPoster: 'Animal',
12964     excerpt: 'No way dude!'
12965 });
12966 myStore.add(myNewRecord);
12967 </code></pre>
12968  * @method create
12969  * @static
12970  */
12971 Roo.data.Record.create = function(o){
12972     var f = function(){
12973         f.superclass.constructor.apply(this, arguments);
12974     };
12975     Roo.extend(f, Roo.data.Record);
12976     var p = f.prototype;
12977     p.fields = new Roo.util.MixedCollection(false, function(field){
12978         return field.name;
12979     });
12980     for(var i = 0, len = o.length; i < len; i++){
12981         p.fields.add(new Roo.data.Field(o[i]));
12982     }
12983     f.getField = function(name){
12984         return p.fields.get(name);  
12985     };
12986     return f;
12987 };
12988
12989 Roo.data.Record.AUTO_ID = 1000;
12990 Roo.data.Record.EDIT = 'edit';
12991 Roo.data.Record.REJECT = 'reject';
12992 Roo.data.Record.COMMIT = 'commit';
12993
12994 Roo.data.Record.prototype = {
12995     /**
12996      * Readonly flag - true if this record has been modified.
12997      * @type Boolean
12998      */
12999     dirty : false,
13000     editing : false,
13001     error: null,
13002     modified: null,
13003
13004     // private
13005     join : function(store){
13006         this.store = store;
13007     },
13008
13009     /**
13010      * Set the named field to the specified value.
13011      * @param {String} name The name of the field to set.
13012      * @param {Object} value The value to set the field to.
13013      */
13014     set : function(name, value){
13015         if(this.data[name] == value){
13016             return;
13017         }
13018         this.dirty = true;
13019         if(!this.modified){
13020             this.modified = {};
13021         }
13022         if(typeof this.modified[name] == 'undefined'){
13023             this.modified[name] = this.data[name];
13024         }
13025         this.data[name] = value;
13026         if(!this.editing && this.store){
13027             this.store.afterEdit(this);
13028         }       
13029     },
13030
13031     /**
13032      * Get the value of the named field.
13033      * @param {String} name The name of the field to get the value of.
13034      * @return {Object} The value of the field.
13035      */
13036     get : function(name){
13037         return this.data[name]; 
13038     },
13039
13040     // private
13041     beginEdit : function(){
13042         this.editing = true;
13043         this.modified = {}; 
13044     },
13045
13046     // private
13047     cancelEdit : function(){
13048         this.editing = false;
13049         delete this.modified;
13050     },
13051
13052     // private
13053     endEdit : function(){
13054         this.editing = false;
13055         if(this.dirty && this.store){
13056             this.store.afterEdit(this);
13057         }
13058     },
13059
13060     /**
13061      * Usually called by the {@link Roo.data.Store} which owns the Record.
13062      * Rejects all changes made to the Record since either creation, or the last commit operation.
13063      * Modified fields are reverted to their original values.
13064      * <p>
13065      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13066      * of reject operations.
13067      */
13068     reject : function(){
13069         var m = this.modified;
13070         for(var n in m){
13071             if(typeof m[n] != "function"){
13072                 this.data[n] = m[n];
13073             }
13074         }
13075         this.dirty = false;
13076         delete this.modified;
13077         this.editing = false;
13078         if(this.store){
13079             this.store.afterReject(this);
13080         }
13081     },
13082
13083     /**
13084      * Usually called by the {@link Roo.data.Store} which owns the Record.
13085      * Commits all changes made to the Record since either creation, or the last commit operation.
13086      * <p>
13087      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13088      * of commit operations.
13089      */
13090     commit : function(){
13091         this.dirty = false;
13092         delete this.modified;
13093         this.editing = false;
13094         if(this.store){
13095             this.store.afterCommit(this);
13096         }
13097     },
13098
13099     // private
13100     hasError : function(){
13101         return this.error != null;
13102     },
13103
13104     // private
13105     clearError : function(){
13106         this.error = null;
13107     },
13108
13109     /**
13110      * Creates a copy of this record.
13111      * @param {String} id (optional) A new record id if you don't want to use this record's id
13112      * @return {Record}
13113      */
13114     copy : function(newId) {
13115         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13116     }
13117 };/*
13118  * Based on:
13119  * Ext JS Library 1.1.1
13120  * Copyright(c) 2006-2007, Ext JS, LLC.
13121  *
13122  * Originally Released Under LGPL - original licence link has changed is not relivant.
13123  *
13124  * Fork - LGPL
13125  * <script type="text/javascript">
13126  */
13127
13128
13129
13130 /**
13131  * @class Roo.data.Store
13132  * @extends Roo.util.Observable
13133  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13134  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13135  * <p>
13136  * 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
13137  * has no knowledge of the format of the data returned by the Proxy.<br>
13138  * <p>
13139  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13140  * instances from the data object. These records are cached and made available through accessor functions.
13141  * @constructor
13142  * Creates a new Store.
13143  * @param {Object} config A config object containing the objects needed for the Store to access data,
13144  * and read the data into Records.
13145  */
13146 Roo.data.Store = function(config){
13147     this.data = new Roo.util.MixedCollection(false);
13148     this.data.getKey = function(o){
13149         return o.id;
13150     };
13151     this.baseParams = {};
13152     // private
13153     this.paramNames = {
13154         "start" : "start",
13155         "limit" : "limit",
13156         "sort" : "sort",
13157         "dir" : "dir",
13158         "multisort" : "_multisort"
13159     };
13160
13161     if(config && config.data){
13162         this.inlineData = config.data;
13163         delete config.data;
13164     }
13165
13166     Roo.apply(this, config);
13167     
13168     if(this.reader){ // reader passed
13169         this.reader = Roo.factory(this.reader, Roo.data);
13170         this.reader.xmodule = this.xmodule || false;
13171         if(!this.recordType){
13172             this.recordType = this.reader.recordType;
13173         }
13174         if(this.reader.onMetaChange){
13175             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13176         }
13177     }
13178
13179     if(this.recordType){
13180         this.fields = this.recordType.prototype.fields;
13181     }
13182     this.modified = [];
13183
13184     this.addEvents({
13185         /**
13186          * @event datachanged
13187          * Fires when the data cache has changed, and a widget which is using this Store
13188          * as a Record cache should refresh its view.
13189          * @param {Store} this
13190          */
13191         datachanged : true,
13192         /**
13193          * @event metachange
13194          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13195          * @param {Store} this
13196          * @param {Object} meta The JSON metadata
13197          */
13198         metachange : true,
13199         /**
13200          * @event add
13201          * Fires when Records have been added to the Store
13202          * @param {Store} this
13203          * @param {Roo.data.Record[]} records The array of Records added
13204          * @param {Number} index The index at which the record(s) were added
13205          */
13206         add : true,
13207         /**
13208          * @event remove
13209          * Fires when a Record has been removed from the Store
13210          * @param {Store} this
13211          * @param {Roo.data.Record} record The Record that was removed
13212          * @param {Number} index The index at which the record was removed
13213          */
13214         remove : true,
13215         /**
13216          * @event update
13217          * Fires when a Record has been updated
13218          * @param {Store} this
13219          * @param {Roo.data.Record} record The Record that was updated
13220          * @param {String} operation The update operation being performed.  Value may be one of:
13221          * <pre><code>
13222  Roo.data.Record.EDIT
13223  Roo.data.Record.REJECT
13224  Roo.data.Record.COMMIT
13225          * </code></pre>
13226          */
13227         update : true,
13228         /**
13229          * @event clear
13230          * Fires when the data cache has been cleared.
13231          * @param {Store} this
13232          */
13233         clear : true,
13234         /**
13235          * @event beforeload
13236          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13237          * the load action will be canceled.
13238          * @param {Store} this
13239          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13240          */
13241         beforeload : true,
13242         /**
13243          * @event beforeloadadd
13244          * Fires after a new set of Records has been loaded.
13245          * @param {Store} this
13246          * @param {Roo.data.Record[]} records The Records that were loaded
13247          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13248          */
13249         beforeloadadd : true,
13250         /**
13251          * @event load
13252          * Fires after a new set of Records has been loaded, before they are added to the store.
13253          * @param {Store} this
13254          * @param {Roo.data.Record[]} records The Records that were loaded
13255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13256          * @params {Object} return from reader
13257          */
13258         load : true,
13259         /**
13260          * @event loadexception
13261          * Fires if an exception occurs in the Proxy during loading.
13262          * Called with the signature of the Proxy's "loadexception" event.
13263          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13264          * 
13265          * @param {Proxy} 
13266          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13267          * @param {Object} load options 
13268          * @param {Object} jsonData from your request (normally this contains the Exception)
13269          */
13270         loadexception : true
13271     });
13272     
13273     if(this.proxy){
13274         this.proxy = Roo.factory(this.proxy, Roo.data);
13275         this.proxy.xmodule = this.xmodule || false;
13276         this.relayEvents(this.proxy,  ["loadexception"]);
13277     }
13278     this.sortToggle = {};
13279     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13280
13281     Roo.data.Store.superclass.constructor.call(this);
13282
13283     if(this.inlineData){
13284         this.loadData(this.inlineData);
13285         delete this.inlineData;
13286     }
13287 };
13288
13289 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13290      /**
13291     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13292     * without a remote query - used by combo/forms at present.
13293     */
13294     
13295     /**
13296     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13297     */
13298     /**
13299     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13300     */
13301     /**
13302     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13303     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13304     */
13305     /**
13306     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13307     * on any HTTP request
13308     */
13309     /**
13310     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13311     */
13312     /**
13313     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13314     */
13315     multiSort: false,
13316     /**
13317     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13318     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13319     */
13320     remoteSort : false,
13321
13322     /**
13323     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13324      * loaded or when a record is removed. (defaults to false).
13325     */
13326     pruneModifiedRecords : false,
13327
13328     // private
13329     lastOptions : null,
13330
13331     /**
13332      * Add Records to the Store and fires the add event.
13333      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13334      */
13335     add : function(records){
13336         records = [].concat(records);
13337         for(var i = 0, len = records.length; i < len; i++){
13338             records[i].join(this);
13339         }
13340         var index = this.data.length;
13341         this.data.addAll(records);
13342         this.fireEvent("add", this, records, index);
13343     },
13344
13345     /**
13346      * Remove a Record from the Store and fires the remove event.
13347      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13348      */
13349     remove : function(record){
13350         var index = this.data.indexOf(record);
13351         this.data.removeAt(index);
13352  
13353         if(this.pruneModifiedRecords){
13354             this.modified.remove(record);
13355         }
13356         this.fireEvent("remove", this, record, index);
13357     },
13358
13359     /**
13360      * Remove all Records from the Store and fires the clear event.
13361      */
13362     removeAll : function(){
13363         this.data.clear();
13364         if(this.pruneModifiedRecords){
13365             this.modified = [];
13366         }
13367         this.fireEvent("clear", this);
13368     },
13369
13370     /**
13371      * Inserts Records to the Store at the given index and fires the add event.
13372      * @param {Number} index The start index at which to insert the passed Records.
13373      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13374      */
13375     insert : function(index, records){
13376         records = [].concat(records);
13377         for(var i = 0, len = records.length; i < len; i++){
13378             this.data.insert(index, records[i]);
13379             records[i].join(this);
13380         }
13381         this.fireEvent("add", this, records, index);
13382     },
13383
13384     /**
13385      * Get the index within the cache of the passed Record.
13386      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13387      * @return {Number} The index of the passed Record. Returns -1 if not found.
13388      */
13389     indexOf : function(record){
13390         return this.data.indexOf(record);
13391     },
13392
13393     /**
13394      * Get the index within the cache of the Record with the passed id.
13395      * @param {String} id The id of the Record to find.
13396      * @return {Number} The index of the Record. Returns -1 if not found.
13397      */
13398     indexOfId : function(id){
13399         return this.data.indexOfKey(id);
13400     },
13401
13402     /**
13403      * Get the Record with the specified id.
13404      * @param {String} id The id of the Record to find.
13405      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13406      */
13407     getById : function(id){
13408         return this.data.key(id);
13409     },
13410
13411     /**
13412      * Get the Record at the specified index.
13413      * @param {Number} index The index of the Record to find.
13414      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13415      */
13416     getAt : function(index){
13417         return this.data.itemAt(index);
13418     },
13419
13420     /**
13421      * Returns a range of Records between specified indices.
13422      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13423      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13424      * @return {Roo.data.Record[]} An array of Records
13425      */
13426     getRange : function(start, end){
13427         return this.data.getRange(start, end);
13428     },
13429
13430     // private
13431     storeOptions : function(o){
13432         o = Roo.apply({}, o);
13433         delete o.callback;
13434         delete o.scope;
13435         this.lastOptions = o;
13436     },
13437
13438     /**
13439      * Loads the Record cache from the configured Proxy using the configured Reader.
13440      * <p>
13441      * If using remote paging, then the first load call must specify the <em>start</em>
13442      * and <em>limit</em> properties in the options.params property to establish the initial
13443      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13444      * <p>
13445      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13446      * and this call will return before the new data has been loaded. Perform any post-processing
13447      * in a callback function, or in a "load" event handler.</strong>
13448      * <p>
13449      * @param {Object} options An object containing properties which control loading options:<ul>
13450      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13451      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13452      * passed the following arguments:<ul>
13453      * <li>r : Roo.data.Record[]</li>
13454      * <li>options: Options object from the load call</li>
13455      * <li>success: Boolean success indicator</li></ul></li>
13456      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13457      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13458      * </ul>
13459      */
13460     load : function(options){
13461         options = options || {};
13462         if(this.fireEvent("beforeload", this, options) !== false){
13463             this.storeOptions(options);
13464             var p = Roo.apply(options.params || {}, this.baseParams);
13465             // if meta was not loaded from remote source.. try requesting it.
13466             if (!this.reader.metaFromRemote) {
13467                 p._requestMeta = 1;
13468             }
13469             if(this.sortInfo && this.remoteSort){
13470                 var pn = this.paramNames;
13471                 p[pn["sort"]] = this.sortInfo.field;
13472                 p[pn["dir"]] = this.sortInfo.direction;
13473             }
13474             if (this.multiSort) {
13475                 var pn = this.paramNames;
13476                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13477             }
13478             
13479             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13480         }
13481     },
13482
13483     /**
13484      * Reloads the Record cache from the configured Proxy using the configured Reader and
13485      * the options from the last load operation performed.
13486      * @param {Object} options (optional) An object containing properties which may override the options
13487      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13488      * the most recently used options are reused).
13489      */
13490     reload : function(options){
13491         this.load(Roo.applyIf(options||{}, this.lastOptions));
13492     },
13493
13494     // private
13495     // Called as a callback by the Reader during a load operation.
13496     loadRecords : function(o, options, success){
13497         if(!o || success === false){
13498             if(success !== false){
13499                 this.fireEvent("load", this, [], options, o);
13500             }
13501             if(options.callback){
13502                 options.callback.call(options.scope || this, [], options, false);
13503             }
13504             return;
13505         }
13506         // if data returned failure - throw an exception.
13507         if (o.success === false) {
13508             // show a message if no listener is registered.
13509             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13510                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13511             }
13512             // loadmask wil be hooked into this..
13513             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13514             return;
13515         }
13516         var r = o.records, t = o.totalRecords || r.length;
13517         
13518         this.fireEvent("beforeloadadd", this, r, options, o);
13519         
13520         if(!options || options.add !== true){
13521             if(this.pruneModifiedRecords){
13522                 this.modified = [];
13523             }
13524             for(var i = 0, len = r.length; i < len; i++){
13525                 r[i].join(this);
13526             }
13527             if(this.snapshot){
13528                 this.data = this.snapshot;
13529                 delete this.snapshot;
13530             }
13531             this.data.clear();
13532             this.data.addAll(r);
13533             this.totalLength = t;
13534             this.applySort();
13535             this.fireEvent("datachanged", this);
13536         }else{
13537             this.totalLength = Math.max(t, this.data.length+r.length);
13538             this.add(r);
13539         }
13540         
13541         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13542                 
13543             var e = new Roo.data.Record({});
13544
13545             e.set(this.parent.displayField, this.parent.emptyTitle);
13546             e.set(this.parent.valueField, '');
13547
13548             this.insert(0, e);
13549         }
13550             
13551         this.fireEvent("load", this, r, options, o);
13552         if(options.callback){
13553             options.callback.call(options.scope || this, r, options, true);
13554         }
13555     },
13556
13557
13558     /**
13559      * Loads data from a passed data block. A Reader which understands the format of the data
13560      * must have been configured in the constructor.
13561      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13562      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13563      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13564      */
13565     loadData : function(o, append){
13566         var r = this.reader.readRecords(o);
13567         this.loadRecords(r, {add: append}, true);
13568     },
13569     
13570      /**
13571      * using 'cn' the nested child reader read the child array into it's child stores.
13572      * @param {Object} rec The record with a 'children array
13573      */
13574     loadDataFromChildren : function(rec)
13575     {
13576         this.loadData(this.reader.toLoadData(rec));
13577     },
13578     
13579
13580     /**
13581      * Gets the number of cached records.
13582      * <p>
13583      * <em>If using paging, this may not be the total size of the dataset. If the data object
13584      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13585      * the data set size</em>
13586      */
13587     getCount : function(){
13588         return this.data.length || 0;
13589     },
13590
13591     /**
13592      * Gets the total number of records in the dataset as returned by the server.
13593      * <p>
13594      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13595      * the dataset size</em>
13596      */
13597     getTotalCount : function(){
13598         return this.totalLength || 0;
13599     },
13600
13601     /**
13602      * Returns the sort state of the Store as an object with two properties:
13603      * <pre><code>
13604  field {String} The name of the field by which the Records are sorted
13605  direction {String} The sort order, "ASC" or "DESC"
13606      * </code></pre>
13607      */
13608     getSortState : function(){
13609         return this.sortInfo;
13610     },
13611
13612     // private
13613     applySort : function(){
13614         if(this.sortInfo && !this.remoteSort){
13615             var s = this.sortInfo, f = s.field;
13616             var st = this.fields.get(f).sortType;
13617             var fn = function(r1, r2){
13618                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13619                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13620             };
13621             this.data.sort(s.direction, fn);
13622             if(this.snapshot && this.snapshot != this.data){
13623                 this.snapshot.sort(s.direction, fn);
13624             }
13625         }
13626     },
13627
13628     /**
13629      * Sets the default sort column and order to be used by the next load operation.
13630      * @param {String} fieldName The name of the field to sort by.
13631      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13632      */
13633     setDefaultSort : function(field, dir){
13634         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13635     },
13636
13637     /**
13638      * Sort the Records.
13639      * If remote sorting is used, the sort is performed on the server, and the cache is
13640      * reloaded. If local sorting is used, the cache is sorted internally.
13641      * @param {String} fieldName The name of the field to sort by.
13642      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13643      */
13644     sort : function(fieldName, dir){
13645         var f = this.fields.get(fieldName);
13646         if(!dir){
13647             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13648             
13649             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13650                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13651             }else{
13652                 dir = f.sortDir;
13653             }
13654         }
13655         this.sortToggle[f.name] = dir;
13656         this.sortInfo = {field: f.name, direction: dir};
13657         if(!this.remoteSort){
13658             this.applySort();
13659             this.fireEvent("datachanged", this);
13660         }else{
13661             this.load(this.lastOptions);
13662         }
13663     },
13664
13665     /**
13666      * Calls the specified function for each of the Records in the cache.
13667      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13668      * Returning <em>false</em> aborts and exits the iteration.
13669      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13670      */
13671     each : function(fn, scope){
13672         this.data.each(fn, scope);
13673     },
13674
13675     /**
13676      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13677      * (e.g., during paging).
13678      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13679      */
13680     getModifiedRecords : function(){
13681         return this.modified;
13682     },
13683
13684     // private
13685     createFilterFn : function(property, value, anyMatch){
13686         if(!value.exec){ // not a regex
13687             value = String(value);
13688             if(value.length == 0){
13689                 return false;
13690             }
13691             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13692         }
13693         return function(r){
13694             return value.test(r.data[property]);
13695         };
13696     },
13697
13698     /**
13699      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13700      * @param {String} property A field on your records
13701      * @param {Number} start The record index to start at (defaults to 0)
13702      * @param {Number} end The last record index to include (defaults to length - 1)
13703      * @return {Number} The sum
13704      */
13705     sum : function(property, start, end){
13706         var rs = this.data.items, v = 0;
13707         start = start || 0;
13708         end = (end || end === 0) ? end : rs.length-1;
13709
13710         for(var i = start; i <= end; i++){
13711             v += (rs[i].data[property] || 0);
13712         }
13713         return v;
13714     },
13715
13716     /**
13717      * Filter the records by a specified property.
13718      * @param {String} field A field on your records
13719      * @param {String/RegExp} value Either a string that the field
13720      * should start with or a RegExp to test against the field
13721      * @param {Boolean} anyMatch True to match any part not just the beginning
13722      */
13723     filter : function(property, value, anyMatch){
13724         var fn = this.createFilterFn(property, value, anyMatch);
13725         return fn ? this.filterBy(fn) : this.clearFilter();
13726     },
13727
13728     /**
13729      * Filter by a function. The specified function will be called with each
13730      * record in this data source. If the function returns true the record is included,
13731      * otherwise it is filtered.
13732      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13733      * @param {Object} scope (optional) The scope of the function (defaults to this)
13734      */
13735     filterBy : function(fn, scope){
13736         this.snapshot = this.snapshot || this.data;
13737         this.data = this.queryBy(fn, scope||this);
13738         this.fireEvent("datachanged", this);
13739     },
13740
13741     /**
13742      * Query the records by a specified property.
13743      * @param {String} field A field on your records
13744      * @param {String/RegExp} value Either a string that the field
13745      * should start with or a RegExp to test against the field
13746      * @param {Boolean} anyMatch True to match any part not just the beginning
13747      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13748      */
13749     query : function(property, value, anyMatch){
13750         var fn = this.createFilterFn(property, value, anyMatch);
13751         return fn ? this.queryBy(fn) : this.data.clone();
13752     },
13753
13754     /**
13755      * Query by a function. The specified function will be called with each
13756      * record in this data source. If the function returns true the record is included
13757      * in the results.
13758      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13759      * @param {Object} scope (optional) The scope of the function (defaults to this)
13760       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13761      **/
13762     queryBy : function(fn, scope){
13763         var data = this.snapshot || this.data;
13764         return data.filterBy(fn, scope||this);
13765     },
13766
13767     /**
13768      * Collects unique values for a particular dataIndex from this store.
13769      * @param {String} dataIndex The property to collect
13770      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13771      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13772      * @return {Array} An array of the unique values
13773      **/
13774     collect : function(dataIndex, allowNull, bypassFilter){
13775         var d = (bypassFilter === true && this.snapshot) ?
13776                 this.snapshot.items : this.data.items;
13777         var v, sv, r = [], l = {};
13778         for(var i = 0, len = d.length; i < len; i++){
13779             v = d[i].data[dataIndex];
13780             sv = String(v);
13781             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13782                 l[sv] = true;
13783                 r[r.length] = v;
13784             }
13785         }
13786         return r;
13787     },
13788
13789     /**
13790      * Revert to a view of the Record cache with no filtering applied.
13791      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13792      */
13793     clearFilter : function(suppressEvent){
13794         if(this.snapshot && this.snapshot != this.data){
13795             this.data = this.snapshot;
13796             delete this.snapshot;
13797             if(suppressEvent !== true){
13798                 this.fireEvent("datachanged", this);
13799             }
13800         }
13801     },
13802
13803     // private
13804     afterEdit : function(record){
13805         if(this.modified.indexOf(record) == -1){
13806             this.modified.push(record);
13807         }
13808         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13809     },
13810     
13811     // private
13812     afterReject : function(record){
13813         this.modified.remove(record);
13814         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13815     },
13816
13817     // private
13818     afterCommit : function(record){
13819         this.modified.remove(record);
13820         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13821     },
13822
13823     /**
13824      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13825      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13826      */
13827     commitChanges : function(){
13828         var m = this.modified.slice(0);
13829         this.modified = [];
13830         for(var i = 0, len = m.length; i < len; i++){
13831             m[i].commit();
13832         }
13833     },
13834
13835     /**
13836      * Cancel outstanding changes on all changed records.
13837      */
13838     rejectChanges : function(){
13839         var m = this.modified.slice(0);
13840         this.modified = [];
13841         for(var i = 0, len = m.length; i < len; i++){
13842             m[i].reject();
13843         }
13844     },
13845
13846     onMetaChange : function(meta, rtype, o){
13847         this.recordType = rtype;
13848         this.fields = rtype.prototype.fields;
13849         delete this.snapshot;
13850         this.sortInfo = meta.sortInfo || this.sortInfo;
13851         this.modified = [];
13852         this.fireEvent('metachange', this, this.reader.meta);
13853     },
13854     
13855     moveIndex : function(data, type)
13856     {
13857         var index = this.indexOf(data);
13858         
13859         var newIndex = index + type;
13860         
13861         this.remove(data);
13862         
13863         this.insert(newIndex, data);
13864         
13865     }
13866 });/*
13867  * Based on:
13868  * Ext JS Library 1.1.1
13869  * Copyright(c) 2006-2007, Ext JS, LLC.
13870  *
13871  * Originally Released Under LGPL - original licence link has changed is not relivant.
13872  *
13873  * Fork - LGPL
13874  * <script type="text/javascript">
13875  */
13876
13877 /**
13878  * @class Roo.data.SimpleStore
13879  * @extends Roo.data.Store
13880  * Small helper class to make creating Stores from Array data easier.
13881  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13882  * @cfg {Array} fields An array of field definition objects, or field name strings.
13883  * @cfg {Object} an existing reader (eg. copied from another store)
13884  * @cfg {Array} data The multi-dimensional array of data
13885  * @constructor
13886  * @param {Object} config
13887  */
13888 Roo.data.SimpleStore = function(config)
13889 {
13890     Roo.data.SimpleStore.superclass.constructor.call(this, {
13891         isLocal : true,
13892         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13893                 id: config.id
13894             },
13895             Roo.data.Record.create(config.fields)
13896         ),
13897         proxy : new Roo.data.MemoryProxy(config.data)
13898     });
13899     this.load();
13900 };
13901 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13902  * Based on:
13903  * Ext JS Library 1.1.1
13904  * Copyright(c) 2006-2007, Ext JS, LLC.
13905  *
13906  * Originally Released Under LGPL - original licence link has changed is not relivant.
13907  *
13908  * Fork - LGPL
13909  * <script type="text/javascript">
13910  */
13911
13912 /**
13913 /**
13914  * @extends Roo.data.Store
13915  * @class Roo.data.JsonStore
13916  * Small helper class to make creating Stores for JSON data easier. <br/>
13917 <pre><code>
13918 var store = new Roo.data.JsonStore({
13919     url: 'get-images.php',
13920     root: 'images',
13921     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13922 });
13923 </code></pre>
13924  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13925  * JsonReader and HttpProxy (unless inline data is provided).</b>
13926  * @cfg {Array} fields An array of field definition objects, or field name strings.
13927  * @constructor
13928  * @param {Object} config
13929  */
13930 Roo.data.JsonStore = function(c){
13931     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13932         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13933         reader: new Roo.data.JsonReader(c, c.fields)
13934     }));
13935 };
13936 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13937  * Based on:
13938  * Ext JS Library 1.1.1
13939  * Copyright(c) 2006-2007, Ext JS, LLC.
13940  *
13941  * Originally Released Under LGPL - original licence link has changed is not relivant.
13942  *
13943  * Fork - LGPL
13944  * <script type="text/javascript">
13945  */
13946
13947  
13948 Roo.data.Field = function(config){
13949     if(typeof config == "string"){
13950         config = {name: config};
13951     }
13952     Roo.apply(this, config);
13953     
13954     if(!this.type){
13955         this.type = "auto";
13956     }
13957     
13958     var st = Roo.data.SortTypes;
13959     // named sortTypes are supported, here we look them up
13960     if(typeof this.sortType == "string"){
13961         this.sortType = st[this.sortType];
13962     }
13963     
13964     // set default sortType for strings and dates
13965     if(!this.sortType){
13966         switch(this.type){
13967             case "string":
13968                 this.sortType = st.asUCString;
13969                 break;
13970             case "date":
13971                 this.sortType = st.asDate;
13972                 break;
13973             default:
13974                 this.sortType = st.none;
13975         }
13976     }
13977
13978     // define once
13979     var stripRe = /[\$,%]/g;
13980
13981     // prebuilt conversion function for this field, instead of
13982     // switching every time we're reading a value
13983     if(!this.convert){
13984         var cv, dateFormat = this.dateFormat;
13985         switch(this.type){
13986             case "":
13987             case "auto":
13988             case undefined:
13989                 cv = function(v){ return v; };
13990                 break;
13991             case "string":
13992                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13993                 break;
13994             case "int":
13995                 cv = function(v){
13996                     return v !== undefined && v !== null && v !== '' ?
13997                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13998                     };
13999                 break;
14000             case "float":
14001                 cv = function(v){
14002                     return v !== undefined && v !== null && v !== '' ?
14003                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14004                     };
14005                 break;
14006             case "bool":
14007             case "boolean":
14008                 cv = function(v){ return v === true || v === "true" || v == 1; };
14009                 break;
14010             case "date":
14011                 cv = function(v){
14012                     if(!v){
14013                         return '';
14014                     }
14015                     if(v instanceof Date){
14016                         return v;
14017                     }
14018                     if(dateFormat){
14019                         if(dateFormat == "timestamp"){
14020                             return new Date(v*1000);
14021                         }
14022                         return Date.parseDate(v, dateFormat);
14023                     }
14024                     var parsed = Date.parse(v);
14025                     return parsed ? new Date(parsed) : null;
14026                 };
14027              break;
14028             
14029         }
14030         this.convert = cv;
14031     }
14032 };
14033
14034 Roo.data.Field.prototype = {
14035     dateFormat: null,
14036     defaultValue: "",
14037     mapping: null,
14038     sortType : null,
14039     sortDir : "ASC"
14040 };/*
14041  * Based on:
14042  * Ext JS Library 1.1.1
14043  * Copyright(c) 2006-2007, Ext JS, LLC.
14044  *
14045  * Originally Released Under LGPL - original licence link has changed is not relivant.
14046  *
14047  * Fork - LGPL
14048  * <script type="text/javascript">
14049  */
14050  
14051 // Base class for reading structured data from a data source.  This class is intended to be
14052 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14053
14054 /**
14055  * @class Roo.data.DataReader
14056  * Base class for reading structured data from a data source.  This class is intended to be
14057  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14058  */
14059
14060 Roo.data.DataReader = function(meta, recordType){
14061     
14062     this.meta = meta;
14063     
14064     this.recordType = recordType instanceof Array ? 
14065         Roo.data.Record.create(recordType) : recordType;
14066 };
14067
14068 Roo.data.DataReader.prototype = {
14069     
14070     
14071     readerType : 'Data',
14072      /**
14073      * Create an empty record
14074      * @param {Object} data (optional) - overlay some values
14075      * @return {Roo.data.Record} record created.
14076      */
14077     newRow :  function(d) {
14078         var da =  {};
14079         this.recordType.prototype.fields.each(function(c) {
14080             switch( c.type) {
14081                 case 'int' : da[c.name] = 0; break;
14082                 case 'date' : da[c.name] = new Date(); break;
14083                 case 'float' : da[c.name] = 0.0; break;
14084                 case 'boolean' : da[c.name] = false; break;
14085                 default : da[c.name] = ""; break;
14086             }
14087             
14088         });
14089         return new this.recordType(Roo.apply(da, d));
14090     }
14091     
14092     
14093 };/*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103
14104 /**
14105  * @class Roo.data.DataProxy
14106  * @extends Roo.data.Observable
14107  * This class is an abstract base class for implementations which provide retrieval of
14108  * unformatted data objects.<br>
14109  * <p>
14110  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14111  * (of the appropriate type which knows how to parse the data object) to provide a block of
14112  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14113  * <p>
14114  * Custom implementations must implement the load method as described in
14115  * {@link Roo.data.HttpProxy#load}.
14116  */
14117 Roo.data.DataProxy = function(){
14118     this.addEvents({
14119         /**
14120          * @event beforeload
14121          * Fires before a network request is made to retrieve a data object.
14122          * @param {Object} This DataProxy object.
14123          * @param {Object} params The params parameter to the load function.
14124          */
14125         beforeload : true,
14126         /**
14127          * @event load
14128          * Fires before the load method's callback is called.
14129          * @param {Object} This DataProxy object.
14130          * @param {Object} o The data object.
14131          * @param {Object} arg The callback argument object passed to the load function.
14132          */
14133         load : true,
14134         /**
14135          * @event loadexception
14136          * Fires if an Exception occurs during data retrieval.
14137          * @param {Object} This DataProxy object.
14138          * @param {Object} o The data object.
14139          * @param {Object} arg The callback argument object passed to the load function.
14140          * @param {Object} e The Exception.
14141          */
14142         loadexception : true
14143     });
14144     Roo.data.DataProxy.superclass.constructor.call(this);
14145 };
14146
14147 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14148
14149     /**
14150      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14151      */
14152 /*
14153  * Based on:
14154  * Ext JS Library 1.1.1
14155  * Copyright(c) 2006-2007, Ext JS, LLC.
14156  *
14157  * Originally Released Under LGPL - original licence link has changed is not relivant.
14158  *
14159  * Fork - LGPL
14160  * <script type="text/javascript">
14161  */
14162 /**
14163  * @class Roo.data.MemoryProxy
14164  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14165  * to the Reader when its load method is called.
14166  * @constructor
14167  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14168  */
14169 Roo.data.MemoryProxy = function(data){
14170     if (data.data) {
14171         data = data.data;
14172     }
14173     Roo.data.MemoryProxy.superclass.constructor.call(this);
14174     this.data = data;
14175 };
14176
14177 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14178     
14179     /**
14180      * Load data from the requested source (in this case an in-memory
14181      * data object passed to the constructor), read the data object into
14182      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14183      * process that block using the passed callback.
14184      * @param {Object} params This parameter is not used by the MemoryProxy class.
14185      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14186      * object into a block of Roo.data.Records.
14187      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14188      * The function must be passed <ul>
14189      * <li>The Record block object</li>
14190      * <li>The "arg" argument from the load function</li>
14191      * <li>A boolean success indicator</li>
14192      * </ul>
14193      * @param {Object} scope The scope in which to call the callback
14194      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14195      */
14196     load : function(params, reader, callback, scope, arg){
14197         params = params || {};
14198         var result;
14199         try {
14200             result = reader.readRecords(params.data ? params.data :this.data);
14201         }catch(e){
14202             this.fireEvent("loadexception", this, arg, null, e);
14203             callback.call(scope, null, arg, false);
14204             return;
14205         }
14206         callback.call(scope, result, arg, true);
14207     },
14208     
14209     // private
14210     update : function(params, records){
14211         
14212     }
14213 });/*
14214  * Based on:
14215  * Ext JS Library 1.1.1
14216  * Copyright(c) 2006-2007, Ext JS, LLC.
14217  *
14218  * Originally Released Under LGPL - original licence link has changed is not relivant.
14219  *
14220  * Fork - LGPL
14221  * <script type="text/javascript">
14222  */
14223 /**
14224  * @class Roo.data.HttpProxy
14225  * @extends Roo.data.DataProxy
14226  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14227  * configured to reference a certain URL.<br><br>
14228  * <p>
14229  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14230  * from which the running page was served.<br><br>
14231  * <p>
14232  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14233  * <p>
14234  * Be aware that to enable the browser to parse an XML document, the server must set
14235  * the Content-Type header in the HTTP response to "text/xml".
14236  * @constructor
14237  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14238  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14239  * will be used to make the request.
14240  */
14241 Roo.data.HttpProxy = function(conn){
14242     Roo.data.HttpProxy.superclass.constructor.call(this);
14243     // is conn a conn config or a real conn?
14244     this.conn = conn;
14245     this.useAjax = !conn || !conn.events;
14246   
14247 };
14248
14249 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14250     // thse are take from connection...
14251     
14252     /**
14253      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14254      */
14255     /**
14256      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14257      * extra parameters to each request made by this object. (defaults to undefined)
14258      */
14259     /**
14260      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14261      *  to each request made by this object. (defaults to undefined)
14262      */
14263     /**
14264      * @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)
14265      */
14266     /**
14267      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14268      */
14269      /**
14270      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14271      * @type Boolean
14272      */
14273   
14274
14275     /**
14276      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14277      * @type Boolean
14278      */
14279     /**
14280      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14281      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14282      * a finer-grained basis than the DataProxy events.
14283      */
14284     getConnection : function(){
14285         return this.useAjax ? Roo.Ajax : this.conn;
14286     },
14287
14288     /**
14289      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14290      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14291      * process that block using the passed callback.
14292      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14293      * for the request to the remote server.
14294      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14295      * object into a block of Roo.data.Records.
14296      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14297      * The function must be passed <ul>
14298      * <li>The Record block object</li>
14299      * <li>The "arg" argument from the load function</li>
14300      * <li>A boolean success indicator</li>
14301      * </ul>
14302      * @param {Object} scope The scope in which to call the callback
14303      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14304      */
14305     load : function(params, reader, callback, scope, arg){
14306         if(this.fireEvent("beforeload", this, params) !== false){
14307             var  o = {
14308                 params : params || {},
14309                 request: {
14310                     callback : callback,
14311                     scope : scope,
14312                     arg : arg
14313                 },
14314                 reader: reader,
14315                 callback : this.loadResponse,
14316                 scope: this
14317             };
14318             if(this.useAjax){
14319                 Roo.applyIf(o, this.conn);
14320                 if(this.activeRequest){
14321                     Roo.Ajax.abort(this.activeRequest);
14322                 }
14323                 this.activeRequest = Roo.Ajax.request(o);
14324             }else{
14325                 this.conn.request(o);
14326             }
14327         }else{
14328             callback.call(scope||this, null, arg, false);
14329         }
14330     },
14331
14332     // private
14333     loadResponse : function(o, success, response){
14334         delete this.activeRequest;
14335         if(!success){
14336             this.fireEvent("loadexception", this, o, response);
14337             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14338             return;
14339         }
14340         var result;
14341         try {
14342             result = o.reader.read(response);
14343         }catch(e){
14344             this.fireEvent("loadexception", this, o, response, e);
14345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14346             return;
14347         }
14348         
14349         this.fireEvent("load", this, o, o.request.arg);
14350         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14351     },
14352
14353     // private
14354     update : function(dataSet){
14355
14356     },
14357
14358     // private
14359     updateResponse : function(dataSet){
14360
14361     }
14362 });/*
14363  * Based on:
14364  * Ext JS Library 1.1.1
14365  * Copyright(c) 2006-2007, Ext JS, LLC.
14366  *
14367  * Originally Released Under LGPL - original licence link has changed is not relivant.
14368  *
14369  * Fork - LGPL
14370  * <script type="text/javascript">
14371  */
14372
14373 /**
14374  * @class Roo.data.ScriptTagProxy
14375  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14376  * other than the originating domain of the running page.<br><br>
14377  * <p>
14378  * <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
14379  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14380  * <p>
14381  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14382  * source code that is used as the source inside a &lt;script> tag.<br><br>
14383  * <p>
14384  * In order for the browser to process the returned data, the server must wrap the data object
14385  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14386  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14387  * depending on whether the callback name was passed:
14388  * <p>
14389  * <pre><code>
14390 boolean scriptTag = false;
14391 String cb = request.getParameter("callback");
14392 if (cb != null) {
14393     scriptTag = true;
14394     response.setContentType("text/javascript");
14395 } else {
14396     response.setContentType("application/x-json");
14397 }
14398 Writer out = response.getWriter();
14399 if (scriptTag) {
14400     out.write(cb + "(");
14401 }
14402 out.print(dataBlock.toJsonString());
14403 if (scriptTag) {
14404     out.write(");");
14405 }
14406 </pre></code>
14407  *
14408  * @constructor
14409  * @param {Object} config A configuration object.
14410  */
14411 Roo.data.ScriptTagProxy = function(config){
14412     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14413     Roo.apply(this, config);
14414     this.head = document.getElementsByTagName("head")[0];
14415 };
14416
14417 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14418
14419 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14420     /**
14421      * @cfg {String} url The URL from which to request the data object.
14422      */
14423     /**
14424      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14425      */
14426     timeout : 30000,
14427     /**
14428      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14429      * the server the name of the callback function set up by the load call to process the returned data object.
14430      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14431      * javascript output which calls this named function passing the data object as its only parameter.
14432      */
14433     callbackParam : "callback",
14434     /**
14435      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14436      * name to the request.
14437      */
14438     nocache : true,
14439
14440     /**
14441      * Load data from the configured URL, read the data object into
14442      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14443      * process that block using the passed callback.
14444      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14445      * for the request to the remote server.
14446      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14447      * object into a block of Roo.data.Records.
14448      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14449      * The function must be passed <ul>
14450      * <li>The Record block object</li>
14451      * <li>The "arg" argument from the load function</li>
14452      * <li>A boolean success indicator</li>
14453      * </ul>
14454      * @param {Object} scope The scope in which to call the callback
14455      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14456      */
14457     load : function(params, reader, callback, scope, arg){
14458         if(this.fireEvent("beforeload", this, params) !== false){
14459
14460             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14461
14462             var url = this.url;
14463             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14464             if(this.nocache){
14465                 url += "&_dc=" + (new Date().getTime());
14466             }
14467             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14468             var trans = {
14469                 id : transId,
14470                 cb : "stcCallback"+transId,
14471                 scriptId : "stcScript"+transId,
14472                 params : params,
14473                 arg : arg,
14474                 url : url,
14475                 callback : callback,
14476                 scope : scope,
14477                 reader : reader
14478             };
14479             var conn = this;
14480
14481             window[trans.cb] = function(o){
14482                 conn.handleResponse(o, trans);
14483             };
14484
14485             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14486
14487             if(this.autoAbort !== false){
14488                 this.abort();
14489             }
14490
14491             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14492
14493             var script = document.createElement("script");
14494             script.setAttribute("src", url);
14495             script.setAttribute("type", "text/javascript");
14496             script.setAttribute("id", trans.scriptId);
14497             this.head.appendChild(script);
14498
14499             this.trans = trans;
14500         }else{
14501             callback.call(scope||this, null, arg, false);
14502         }
14503     },
14504
14505     // private
14506     isLoading : function(){
14507         return this.trans ? true : false;
14508     },
14509
14510     /**
14511      * Abort the current server request.
14512      */
14513     abort : function(){
14514         if(this.isLoading()){
14515             this.destroyTrans(this.trans);
14516         }
14517     },
14518
14519     // private
14520     destroyTrans : function(trans, isLoaded){
14521         this.head.removeChild(document.getElementById(trans.scriptId));
14522         clearTimeout(trans.timeoutId);
14523         if(isLoaded){
14524             window[trans.cb] = undefined;
14525             try{
14526                 delete window[trans.cb];
14527             }catch(e){}
14528         }else{
14529             // if hasn't been loaded, wait for load to remove it to prevent script error
14530             window[trans.cb] = function(){
14531                 window[trans.cb] = undefined;
14532                 try{
14533                     delete window[trans.cb];
14534                 }catch(e){}
14535             };
14536         }
14537     },
14538
14539     // private
14540     handleResponse : function(o, trans){
14541         this.trans = false;
14542         this.destroyTrans(trans, true);
14543         var result;
14544         try {
14545             result = trans.reader.readRecords(o);
14546         }catch(e){
14547             this.fireEvent("loadexception", this, o, trans.arg, e);
14548             trans.callback.call(trans.scope||window, null, trans.arg, false);
14549             return;
14550         }
14551         this.fireEvent("load", this, o, trans.arg);
14552         trans.callback.call(trans.scope||window, result, trans.arg, true);
14553     },
14554
14555     // private
14556     handleFailure : function(trans){
14557         this.trans = false;
14558         this.destroyTrans(trans, false);
14559         this.fireEvent("loadexception", this, null, trans.arg);
14560         trans.callback.call(trans.scope||window, null, trans.arg, false);
14561     }
14562 });/*
14563  * Based on:
14564  * Ext JS Library 1.1.1
14565  * Copyright(c) 2006-2007, Ext JS, LLC.
14566  *
14567  * Originally Released Under LGPL - original licence link has changed is not relivant.
14568  *
14569  * Fork - LGPL
14570  * <script type="text/javascript">
14571  */
14572
14573 /**
14574  * @class Roo.data.JsonReader
14575  * @extends Roo.data.DataReader
14576  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14577  * based on mappings in a provided Roo.data.Record constructor.
14578  * 
14579  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14580  * in the reply previously. 
14581  * 
14582  * <p>
14583  * Example code:
14584  * <pre><code>
14585 var RecordDef = Roo.data.Record.create([
14586     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14587     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14588 ]);
14589 var myReader = new Roo.data.JsonReader({
14590     totalProperty: "results",    // The property which contains the total dataset size (optional)
14591     root: "rows",                // The property which contains an Array of row objects
14592     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14593 }, RecordDef);
14594 </code></pre>
14595  * <p>
14596  * This would consume a JSON file like this:
14597  * <pre><code>
14598 { 'results': 2, 'rows': [
14599     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14600     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14601 }
14602 </code></pre>
14603  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14604  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14605  * paged from the remote server.
14606  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14607  * @cfg {String} root name of the property which contains the Array of row objects.
14608  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14609  * @cfg {Array} fields Array of field definition objects
14610  * @constructor
14611  * Create a new JsonReader
14612  * @param {Object} meta Metadata configuration options
14613  * @param {Object} recordType Either an Array of field definition objects,
14614  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14615  */
14616 Roo.data.JsonReader = function(meta, recordType){
14617     
14618     meta = meta || {};
14619     // set some defaults:
14620     Roo.applyIf(meta, {
14621         totalProperty: 'total',
14622         successProperty : 'success',
14623         root : 'data',
14624         id : 'id'
14625     });
14626     
14627     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14628 };
14629 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14630     
14631     readerType : 'Json',
14632     
14633     /**
14634      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14635      * Used by Store query builder to append _requestMeta to params.
14636      * 
14637      */
14638     metaFromRemote : false,
14639     /**
14640      * This method is only used by a DataProxy which has retrieved data from a remote server.
14641      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14642      * @return {Object} data A data block which is used by an Roo.data.Store object as
14643      * a cache of Roo.data.Records.
14644      */
14645     read : function(response){
14646         var json = response.responseText;
14647        
14648         var o = /* eval:var:o */ eval("("+json+")");
14649         if(!o) {
14650             throw {message: "JsonReader.read: Json object not found"};
14651         }
14652         
14653         if(o.metaData){
14654             
14655             delete this.ef;
14656             this.metaFromRemote = true;
14657             this.meta = o.metaData;
14658             this.recordType = Roo.data.Record.create(o.metaData.fields);
14659             this.onMetaChange(this.meta, this.recordType, o);
14660         }
14661         return this.readRecords(o);
14662     },
14663
14664     // private function a store will implement
14665     onMetaChange : function(meta, recordType, o){
14666
14667     },
14668
14669     /**
14670          * @ignore
14671          */
14672     simpleAccess: function(obj, subsc) {
14673         return obj[subsc];
14674     },
14675
14676         /**
14677          * @ignore
14678          */
14679     getJsonAccessor: function(){
14680         var re = /[\[\.]/;
14681         return function(expr) {
14682             try {
14683                 return(re.test(expr))
14684                     ? new Function("obj", "return obj." + expr)
14685                     : function(obj){
14686                         return obj[expr];
14687                     };
14688             } catch(e){}
14689             return Roo.emptyFn;
14690         };
14691     }(),
14692
14693     /**
14694      * Create a data block containing Roo.data.Records from an XML document.
14695      * @param {Object} o An object which contains an Array of row objects in the property specified
14696      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14697      * which contains the total size of the dataset.
14698      * @return {Object} data A data block which is used by an Roo.data.Store object as
14699      * a cache of Roo.data.Records.
14700      */
14701     readRecords : function(o){
14702         /**
14703          * After any data loads, the raw JSON data is available for further custom processing.
14704          * @type Object
14705          */
14706         this.o = o;
14707         var s = this.meta, Record = this.recordType,
14708             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14709
14710 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14711         if (!this.ef) {
14712             if(s.totalProperty) {
14713                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14714                 }
14715                 if(s.successProperty) {
14716                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14717                 }
14718                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14719                 if (s.id) {
14720                         var g = this.getJsonAccessor(s.id);
14721                         this.getId = function(rec) {
14722                                 var r = g(rec);  
14723                                 return (r === undefined || r === "") ? null : r;
14724                         };
14725                 } else {
14726                         this.getId = function(){return null;};
14727                 }
14728             this.ef = [];
14729             for(var jj = 0; jj < fl; jj++){
14730                 f = fi[jj];
14731                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14732                 this.ef[jj] = this.getJsonAccessor(map);
14733             }
14734         }
14735
14736         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14737         if(s.totalProperty){
14738             var vt = parseInt(this.getTotal(o), 10);
14739             if(!isNaN(vt)){
14740                 totalRecords = vt;
14741             }
14742         }
14743         if(s.successProperty){
14744             var vs = this.getSuccess(o);
14745             if(vs === false || vs === 'false'){
14746                 success = false;
14747             }
14748         }
14749         var records = [];
14750         for(var i = 0; i < c; i++){
14751                 var n = root[i];
14752             var values = {};
14753             var id = this.getId(n);
14754             for(var j = 0; j < fl; j++){
14755                 f = fi[j];
14756             var v = this.ef[j](n);
14757             if (!f.convert) {
14758                 Roo.log('missing convert for ' + f.name);
14759                 Roo.log(f);
14760                 continue;
14761             }
14762             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14763             }
14764             var record = new Record(values, id);
14765             record.json = n;
14766             records[i] = record;
14767         }
14768         return {
14769             raw : o,
14770             success : success,
14771             records : records,
14772             totalRecords : totalRecords
14773         };
14774     },
14775     // used when loading children.. @see loadDataFromChildren
14776     toLoadData: function(rec)
14777     {
14778         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14779         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14780         return { data : data, total : data.length };
14781         
14782     }
14783 });/*
14784  * Based on:
14785  * Ext JS Library 1.1.1
14786  * Copyright(c) 2006-2007, Ext JS, LLC.
14787  *
14788  * Originally Released Under LGPL - original licence link has changed is not relivant.
14789  *
14790  * Fork - LGPL
14791  * <script type="text/javascript">
14792  */
14793
14794 /**
14795  * @class Roo.data.ArrayReader
14796  * @extends Roo.data.DataReader
14797  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14798  * Each element of that Array represents a row of data fields. The
14799  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14800  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14801  * <p>
14802  * Example code:.
14803  * <pre><code>
14804 var RecordDef = Roo.data.Record.create([
14805     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14806     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14807 ]);
14808 var myReader = new Roo.data.ArrayReader({
14809     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14810 }, RecordDef);
14811 </code></pre>
14812  * <p>
14813  * This would consume an Array like this:
14814  * <pre><code>
14815 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14816   </code></pre>
14817  
14818  * @constructor
14819  * Create a new JsonReader
14820  * @param {Object} meta Metadata configuration options.
14821  * @param {Object|Array} recordType Either an Array of field definition objects
14822  * 
14823  * @cfg {Array} fields Array of field definition objects
14824  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14825  * as specified to {@link Roo.data.Record#create},
14826  * or an {@link Roo.data.Record} object
14827  *
14828  * 
14829  * created using {@link Roo.data.Record#create}.
14830  */
14831 Roo.data.ArrayReader = function(meta, recordType)
14832 {    
14833     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14834 };
14835
14836 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14837     
14838       /**
14839      * Create a data block containing Roo.data.Records from an XML document.
14840      * @param {Object} o An Array of row objects which represents the dataset.
14841      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14842      * a cache of Roo.data.Records.
14843      */
14844     readRecords : function(o)
14845     {
14846         var sid = this.meta ? this.meta.id : null;
14847         var recordType = this.recordType, fields = recordType.prototype.fields;
14848         var records = [];
14849         var root = o;
14850         for(var i = 0; i < root.length; i++){
14851                 var n = root[i];
14852             var values = {};
14853             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14854             for(var j = 0, jlen = fields.length; j < jlen; j++){
14855                 var f = fields.items[j];
14856                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14857                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14858                 v = f.convert(v);
14859                 values[f.name] = v;
14860             }
14861             var record = new recordType(values, id);
14862             record.json = n;
14863             records[records.length] = record;
14864         }
14865         return {
14866             records : records,
14867             totalRecords : records.length
14868         };
14869     },
14870     // used when loading children.. @see loadDataFromChildren
14871     toLoadData: function(rec)
14872     {
14873         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14874         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14875         
14876     }
14877     
14878     
14879 });/*
14880  * - LGPL
14881  * * 
14882  */
14883
14884 /**
14885  * @class Roo.bootstrap.ComboBox
14886  * @extends Roo.bootstrap.TriggerField
14887  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14888  * @cfg {Boolean} append (true|false) default false
14889  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14890  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14891  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14892  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14893  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14894  * @cfg {Boolean} animate default true
14895  * @cfg {Boolean} emptyResultText only for touch device
14896  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14897  * @cfg {String} emptyTitle default ''
14898  * @cfg {Number} width fixed with? experimental
14899  * @constructor
14900  * Create a new ComboBox.
14901  * @param {Object} config Configuration options
14902  */
14903 Roo.bootstrap.ComboBox = function(config){
14904     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14905     this.addEvents({
14906         /**
14907          * @event expand
14908          * Fires when the dropdown list is expanded
14909         * @param {Roo.bootstrap.ComboBox} combo This combo box
14910         */
14911         'expand' : true,
14912         /**
14913          * @event collapse
14914          * Fires when the dropdown list is collapsed
14915         * @param {Roo.bootstrap.ComboBox} combo This combo box
14916         */
14917         'collapse' : true,
14918         /**
14919          * @event beforeselect
14920          * Fires before a list item is selected. Return false to cancel the selection.
14921         * @param {Roo.bootstrap.ComboBox} combo This combo box
14922         * @param {Roo.data.Record} record The data record returned from the underlying store
14923         * @param {Number} index The index of the selected item in the dropdown list
14924         */
14925         'beforeselect' : true,
14926         /**
14927          * @event select
14928          * Fires when a list item is selected
14929         * @param {Roo.bootstrap.ComboBox} combo This combo box
14930         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14931         * @param {Number} index The index of the selected item in the dropdown list
14932         */
14933         'select' : true,
14934         /**
14935          * @event beforequery
14936          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14937          * The event object passed has these properties:
14938         * @param {Roo.bootstrap.ComboBox} combo This combo box
14939         * @param {String} query The query
14940         * @param {Boolean} forceAll true to force "all" query
14941         * @param {Boolean} cancel true to cancel the query
14942         * @param {Object} e The query event object
14943         */
14944         'beforequery': true,
14945          /**
14946          * @event add
14947          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         */
14950         'add' : true,
14951         /**
14952          * @event edit
14953          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14954         * @param {Roo.bootstrap.ComboBox} combo This combo box
14955         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14956         */
14957         'edit' : true,
14958         /**
14959          * @event remove
14960          * Fires when the remove value from the combobox array
14961         * @param {Roo.bootstrap.ComboBox} combo This combo box
14962         */
14963         'remove' : true,
14964         /**
14965          * @event afterremove
14966          * Fires when the remove value from the combobox array
14967         * @param {Roo.bootstrap.ComboBox} combo This combo box
14968         */
14969         'afterremove' : true,
14970         /**
14971          * @event specialfilter
14972          * Fires when specialfilter
14973             * @param {Roo.bootstrap.ComboBox} combo This combo box
14974             */
14975         'specialfilter' : true,
14976         /**
14977          * @event tick
14978          * Fires when tick the element
14979             * @param {Roo.bootstrap.ComboBox} combo This combo box
14980             */
14981         'tick' : true,
14982         /**
14983          * @event touchviewdisplay
14984          * Fires when touch view require special display (default is using displayField)
14985             * @param {Roo.bootstrap.ComboBox} combo This combo box
14986             * @param {Object} cfg set html .
14987             */
14988         'touchviewdisplay' : true
14989         
14990     });
14991     
14992     this.item = [];
14993     this.tickItems = [];
14994     
14995     this.selectedIndex = -1;
14996     if(this.mode == 'local'){
14997         if(config.queryDelay === undefined){
14998             this.queryDelay = 10;
14999         }
15000         if(config.minChars === undefined){
15001             this.minChars = 0;
15002         }
15003     }
15004 };
15005
15006 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15007      
15008     /**
15009      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15010      * rendering into an Roo.Editor, defaults to false)
15011      */
15012     /**
15013      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15014      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15015      */
15016     /**
15017      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15018      */
15019     /**
15020      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15021      * the dropdown list (defaults to undefined, with no header element)
15022      */
15023
15024      /**
15025      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15026      */
15027      
15028      /**
15029      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15030      */
15031     listWidth: undefined,
15032     /**
15033      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15034      * mode = 'remote' or 'text' if mode = 'local')
15035      */
15036     displayField: undefined,
15037     
15038     /**
15039      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15040      * mode = 'remote' or 'value' if mode = 'local'). 
15041      * Note: use of a valueField requires the user make a selection
15042      * in order for a value to be mapped.
15043      */
15044     valueField: undefined,
15045     /**
15046      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15047      */
15048     modalTitle : '',
15049     
15050     /**
15051      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15052      * field's data value (defaults to the underlying DOM element's name)
15053      */
15054     hiddenName: undefined,
15055     /**
15056      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15057      */
15058     listClass: '',
15059     /**
15060      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15061      */
15062     selectedClass: 'active',
15063     
15064     /**
15065      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15066      */
15067     shadow:'sides',
15068     /**
15069      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15070      * anchor positions (defaults to 'tl-bl')
15071      */
15072     listAlign: 'tl-bl?',
15073     /**
15074      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15075      */
15076     maxHeight: 300,
15077     /**
15078      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15079      * query specified by the allQuery config option (defaults to 'query')
15080      */
15081     triggerAction: 'query',
15082     /**
15083      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15084      * (defaults to 4, does not apply if editable = false)
15085      */
15086     minChars : 4,
15087     /**
15088      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15089      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15090      */
15091     typeAhead: false,
15092     /**
15093      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15094      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15095      */
15096     queryDelay: 500,
15097     /**
15098      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15099      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15100      */
15101     pageSize: 0,
15102     /**
15103      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15104      * when editable = true (defaults to false)
15105      */
15106     selectOnFocus:false,
15107     /**
15108      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15109      */
15110     queryParam: 'query',
15111     /**
15112      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15113      * when mode = 'remote' (defaults to 'Loading...')
15114      */
15115     loadingText: 'Loading...',
15116     /**
15117      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15118      */
15119     resizable: false,
15120     /**
15121      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15122      */
15123     handleHeight : 8,
15124     /**
15125      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15126      * traditional select (defaults to true)
15127      */
15128     editable: true,
15129     /**
15130      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15131      */
15132     allQuery: '',
15133     /**
15134      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15135      */
15136     mode: 'remote',
15137     /**
15138      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15139      * listWidth has a higher value)
15140      */
15141     minListWidth : 70,
15142     /**
15143      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15144      * allow the user to set arbitrary text into the field (defaults to false)
15145      */
15146     forceSelection:false,
15147     /**
15148      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15149      * if typeAhead = true (defaults to 250)
15150      */
15151     typeAheadDelay : 250,
15152     /**
15153      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15154      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15155      */
15156     valueNotFoundText : undefined,
15157     /**
15158      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15159      */
15160     blockFocus : false,
15161     
15162     /**
15163      * @cfg {Boolean} disableClear Disable showing of clear button.
15164      */
15165     disableClear : false,
15166     /**
15167      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15168      */
15169     alwaysQuery : false,
15170     
15171     /**
15172      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15173      */
15174     multiple : false,
15175     
15176     /**
15177      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15178      */
15179     invalidClass : "has-warning",
15180     
15181     /**
15182      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     validClass : "has-success",
15185     
15186     /**
15187      * @cfg {Boolean} specialFilter (true|false) special filter default false
15188      */
15189     specialFilter : false,
15190     
15191     /**
15192      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15193      */
15194     mobileTouchView : true,
15195     
15196     /**
15197      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15198      */
15199     useNativeIOS : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15203      */
15204     mobile_restrict_height : false,
15205     
15206     ios_options : false,
15207     
15208     //private
15209     addicon : false,
15210     editicon: false,
15211     
15212     page: 0,
15213     hasQuery: false,
15214     append: false,
15215     loadNext: false,
15216     autoFocus : true,
15217     tickable : false,
15218     btnPosition : 'right',
15219     triggerList : true,
15220     showToggleBtn : true,
15221     animate : true,
15222     emptyResultText: 'Empty',
15223     triggerText : 'Select',
15224     emptyTitle : '',
15225     width : false,
15226     
15227     // element that contains real text value.. (when hidden is used..)
15228     
15229     getAutoCreate : function()
15230     {   
15231         var cfg = false;
15232         //render
15233         /*
15234          * Render classic select for iso
15235          */
15236         
15237         if(Roo.isIOS && this.useNativeIOS){
15238             cfg = this.getAutoCreateNativeIOS();
15239             return cfg;
15240         }
15241         
15242         /*
15243          * Touch Devices
15244          */
15245         
15246         if(Roo.isTouch && this.mobileTouchView){
15247             cfg = this.getAutoCreateTouchView();
15248             return cfg;;
15249         }
15250         
15251         /*
15252          *  Normal ComboBox
15253          */
15254         if(!this.tickable){
15255             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15256             return cfg;
15257         }
15258         
15259         /*
15260          *  ComboBox with tickable selections
15261          */
15262              
15263         var align = this.labelAlign || this.parentLabelAlign();
15264         
15265         cfg = {
15266             cls : 'form-group roo-combobox-tickable' //input-group
15267         };
15268         
15269         var btn_text_select = '';
15270         var btn_text_done = '';
15271         var btn_text_cancel = '';
15272         
15273         if (this.btn_text_show) {
15274             btn_text_select = 'Select';
15275             btn_text_done = 'Done';
15276             btn_text_cancel = 'Cancel'; 
15277         }
15278         
15279         var buttons = {
15280             tag : 'div',
15281             cls : 'tickable-buttons',
15282             cn : [
15283                 {
15284                     tag : 'button',
15285                     type : 'button',
15286                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15287                     //html : this.triggerText
15288                     html: btn_text_select
15289                 },
15290                 {
15291                     tag : 'button',
15292                     type : 'button',
15293                     name : 'ok',
15294                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15295                     //html : 'Done'
15296                     html: btn_text_done
15297                 },
15298                 {
15299                     tag : 'button',
15300                     type : 'button',
15301                     name : 'cancel',
15302                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15303                     //html : 'Cancel'
15304                     html: btn_text_cancel
15305                 }
15306             ]
15307         };
15308         
15309         if(this.editable){
15310             buttons.cn.unshift({
15311                 tag: 'input',
15312                 cls: 'roo-select2-search-field-input'
15313             });
15314         }
15315         
15316         var _this = this;
15317         
15318         Roo.each(buttons.cn, function(c){
15319             if (_this.size) {
15320                 c.cls += ' btn-' + _this.size;
15321             }
15322
15323             if (_this.disabled) {
15324                 c.disabled = true;
15325             }
15326         });
15327         
15328         var box = {
15329             tag: 'div',
15330             style : 'display: contents',
15331             cn: [
15332                 {
15333                     tag: 'input',
15334                     type : 'hidden',
15335                     cls: 'form-hidden-field'
15336                 },
15337                 {
15338                     tag: 'ul',
15339                     cls: 'roo-select2-choices',
15340                     cn:[
15341                         {
15342                             tag: 'li',
15343                             cls: 'roo-select2-search-field',
15344                             cn: [
15345                                 buttons
15346                             ]
15347                         }
15348                     ]
15349                 }
15350             ]
15351         };
15352         
15353         var combobox = {
15354             cls: 'roo-select2-container input-group roo-select2-container-multi',
15355             cn: [
15356                 
15357                 box
15358 //                {
15359 //                    tag: 'ul',
15360 //                    cls: 'typeahead typeahead-long dropdown-menu',
15361 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15362 //                }
15363             ]
15364         };
15365         
15366         if(this.hasFeedback && !this.allowBlank){
15367             
15368             var feedback = {
15369                 tag: 'span',
15370                 cls: 'glyphicon form-control-feedback'
15371             };
15372
15373             combobox.cn.push(feedback);
15374         }
15375         
15376         
15377         
15378         var indicator = {
15379             tag : 'i',
15380             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15381             tooltip : 'This field is required'
15382         };
15383         if (Roo.bootstrap.version == 4) {
15384             indicator = {
15385                 tag : 'i',
15386                 style : 'display:none'
15387             };
15388         }
15389         if (align ==='left' && this.fieldLabel.length) {
15390             
15391             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15392             
15393             cfg.cn = [
15394                 indicator,
15395                 {
15396                     tag: 'label',
15397                     'for' :  id,
15398                     cls : 'control-label col-form-label',
15399                     html : this.fieldLabel
15400
15401                 },
15402                 {
15403                     cls : "", 
15404                     cn: [
15405                         combobox
15406                     ]
15407                 }
15408
15409             ];
15410             
15411             var labelCfg = cfg.cn[1];
15412             var contentCfg = cfg.cn[2];
15413             
15414
15415             if(this.indicatorpos == 'right'){
15416                 
15417                 cfg.cn = [
15418                     {
15419                         tag: 'label',
15420                         'for' :  id,
15421                         cls : 'control-label col-form-label',
15422                         cn : [
15423                             {
15424                                 tag : 'span',
15425                                 html : this.fieldLabel
15426                             },
15427                             indicator
15428                         ]
15429                     },
15430                     {
15431                         cls : "",
15432                         cn: [
15433                             combobox
15434                         ]
15435                     }
15436
15437                 ];
15438                 
15439                 
15440                 
15441                 labelCfg = cfg.cn[0];
15442                 contentCfg = cfg.cn[1];
15443             
15444             }
15445             
15446             if(this.labelWidth > 12){
15447                 labelCfg.style = "width: " + this.labelWidth + 'px';
15448             }
15449             if(this.width * 1 > 0){
15450                 contentCfg.style = "width: " + this.width + 'px';
15451             }
15452             if(this.labelWidth < 13 && this.labelmd == 0){
15453                 this.labelmd = this.labelWidth;
15454             }
15455             
15456             if(this.labellg > 0){
15457                 labelCfg.cls += ' col-lg-' + this.labellg;
15458                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15459             }
15460             
15461             if(this.labelmd > 0){
15462                 labelCfg.cls += ' col-md-' + this.labelmd;
15463                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15464             }
15465             
15466             if(this.labelsm > 0){
15467                 labelCfg.cls += ' col-sm-' + this.labelsm;
15468                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15469             }
15470             
15471             if(this.labelxs > 0){
15472                 labelCfg.cls += ' col-xs-' + this.labelxs;
15473                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15474             }
15475                 
15476                 
15477         } else if ( this.fieldLabel.length) {
15478 //                Roo.log(" label");
15479                  cfg.cn = [
15480                    indicator,
15481                     {
15482                         tag: 'label',
15483                         //cls : 'input-group-addon',
15484                         html : this.fieldLabel
15485                     },
15486                     combobox
15487                 ];
15488                 
15489                 if(this.indicatorpos == 'right'){
15490                     cfg.cn = [
15491                         {
15492                             tag: 'label',
15493                             //cls : 'input-group-addon',
15494                             html : this.fieldLabel
15495                         },
15496                         indicator,
15497                         combobox
15498                     ];
15499                     
15500                 }
15501
15502         } else {
15503             
15504 //                Roo.log(" no label && no align");
15505                 cfg = combobox
15506                      
15507                 
15508         }
15509          
15510         var settings=this;
15511         ['xs','sm','md','lg'].map(function(size){
15512             if (settings[size]) {
15513                 cfg.cls += ' col-' + size + '-' + settings[size];
15514             }
15515         });
15516         
15517         return cfg;
15518         
15519     },
15520     
15521     _initEventsCalled : false,
15522     
15523     // private
15524     initEvents: function()
15525     {   
15526         if (this._initEventsCalled) { // as we call render... prevent looping...
15527             return;
15528         }
15529         this._initEventsCalled = true;
15530         
15531         if (!this.store) {
15532             throw "can not find store for combo";
15533         }
15534         
15535         this.indicator = this.indicatorEl();
15536         
15537         this.store = Roo.factory(this.store, Roo.data);
15538         this.store.parent = this;
15539         
15540         // if we are building from html. then this element is so complex, that we can not really
15541         // use the rendered HTML.
15542         // so we have to trash and replace the previous code.
15543         if (Roo.XComponent.build_from_html) {
15544             // remove this element....
15545             var e = this.el.dom, k=0;
15546             while (e ) { e = e.previousSibling;  ++k;}
15547
15548             this.el.remove();
15549             
15550             this.el=false;
15551             this.rendered = false;
15552             
15553             this.render(this.parent().getChildContainer(true), k);
15554         }
15555         
15556         if(Roo.isIOS && this.useNativeIOS){
15557             this.initIOSView();
15558             return;
15559         }
15560         
15561         /*
15562          * Touch Devices
15563          */
15564         
15565         if(Roo.isTouch && this.mobileTouchView){
15566             this.initTouchView();
15567             return;
15568         }
15569         
15570         if(this.tickable){
15571             this.initTickableEvents();
15572             return;
15573         }
15574         
15575         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15576         
15577         if(this.hiddenName){
15578             
15579             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15580             
15581             this.hiddenField.dom.value =
15582                 this.hiddenValue !== undefined ? this.hiddenValue :
15583                 this.value !== undefined ? this.value : '';
15584
15585             // prevent input submission
15586             this.el.dom.removeAttribute('name');
15587             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15588              
15589              
15590         }
15591         //if(Roo.isGecko){
15592         //    this.el.dom.setAttribute('autocomplete', 'off');
15593         //}
15594         
15595         var cls = 'x-combo-list';
15596         
15597         //this.list = new Roo.Layer({
15598         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15599         //});
15600         
15601         var _this = this;
15602         
15603         (function(){
15604             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15605             _this.list.setWidth(lw);
15606         }).defer(100);
15607         
15608         this.list.on('mouseover', this.onViewOver, this);
15609         this.list.on('mousemove', this.onViewMove, this);
15610         this.list.on('scroll', this.onViewScroll, this);
15611         
15612         /*
15613         this.list.swallowEvent('mousewheel');
15614         this.assetHeight = 0;
15615
15616         if(this.title){
15617             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15618             this.assetHeight += this.header.getHeight();
15619         }
15620
15621         this.innerList = this.list.createChild({cls:cls+'-inner'});
15622         this.innerList.on('mouseover', this.onViewOver, this);
15623         this.innerList.on('mousemove', this.onViewMove, this);
15624         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15625         
15626         if(this.allowBlank && !this.pageSize && !this.disableClear){
15627             this.footer = this.list.createChild({cls:cls+'-ft'});
15628             this.pageTb = new Roo.Toolbar(this.footer);
15629            
15630         }
15631         if(this.pageSize){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15634                     {pageSize: this.pageSize});
15635             
15636         }
15637         
15638         if (this.pageTb && this.allowBlank && !this.disableClear) {
15639             var _this = this;
15640             this.pageTb.add(new Roo.Toolbar.Fill(), {
15641                 cls: 'x-btn-icon x-btn-clear',
15642                 text: '&#160;',
15643                 handler: function()
15644                 {
15645                     _this.collapse();
15646                     _this.clearValue();
15647                     _this.onSelect(false, -1);
15648                 }
15649             });
15650         }
15651         if (this.footer) {
15652             this.assetHeight += this.footer.getHeight();
15653         }
15654         */
15655             
15656         if(!this.tpl){
15657             this.tpl = Roo.bootstrap.version == 4 ?
15658                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15659                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15660         }
15661
15662         this.view = new Roo.View(this.list, this.tpl, {
15663             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15664         });
15665         //this.view.wrapEl.setDisplayed(false);
15666         this.view.on('click', this.onViewClick, this);
15667         
15668         
15669         this.store.on('beforeload', this.onBeforeLoad, this);
15670         this.store.on('load', this.onLoad, this);
15671         this.store.on('loadexception', this.onLoadException, this);
15672         /*
15673         if(this.resizable){
15674             this.resizer = new Roo.Resizable(this.list,  {
15675                pinned:true, handles:'se'
15676             });
15677             this.resizer.on('resize', function(r, w, h){
15678                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15679                 this.listWidth = w;
15680                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15681                 this.restrictHeight();
15682             }, this);
15683             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15684         }
15685         */
15686         if(!this.editable){
15687             this.editable = true;
15688             this.setEditable(false);
15689         }
15690         
15691         /*
15692         
15693         if (typeof(this.events.add.listeners) != 'undefined') {
15694             
15695             this.addicon = this.wrap.createChild(
15696                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15697        
15698             this.addicon.on('click', function(e) {
15699                 this.fireEvent('add', this);
15700             }, this);
15701         }
15702         if (typeof(this.events.edit.listeners) != 'undefined') {
15703             
15704             this.editicon = this.wrap.createChild(
15705                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15706             if (this.addicon) {
15707                 this.editicon.setStyle('margin-left', '40px');
15708             }
15709             this.editicon.on('click', function(e) {
15710                 
15711                 // we fire even  if inothing is selected..
15712                 this.fireEvent('edit', this, this.lastData );
15713                 
15714             }, this);
15715         }
15716         */
15717         
15718         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15719             "up" : function(e){
15720                 this.inKeyMode = true;
15721                 this.selectPrev();
15722             },
15723
15724             "down" : function(e){
15725                 if(!this.isExpanded()){
15726                     this.onTriggerClick();
15727                 }else{
15728                     this.inKeyMode = true;
15729                     this.selectNext();
15730                 }
15731             },
15732
15733             "enter" : function(e){
15734 //                this.onViewClick();
15735                 //return true;
15736                 this.collapse();
15737                 
15738                 if(this.fireEvent("specialkey", this, e)){
15739                     this.onViewClick(false);
15740                 }
15741                 
15742                 return true;
15743             },
15744
15745             "esc" : function(e){
15746                 this.collapse();
15747             },
15748
15749             "tab" : function(e){
15750                 this.collapse();
15751                 
15752                 if(this.fireEvent("specialkey", this, e)){
15753                     this.onViewClick(false);
15754                 }
15755                 
15756                 return true;
15757             },
15758
15759             scope : this,
15760
15761             doRelay : function(foo, bar, hname){
15762                 if(hname == 'down' || this.scope.isExpanded()){
15763                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15764                 }
15765                 return true;
15766             },
15767
15768             forceKeyDown: true
15769         });
15770         
15771         
15772         this.queryDelay = Math.max(this.queryDelay || 10,
15773                 this.mode == 'local' ? 10 : 250);
15774         
15775         
15776         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15777         
15778         if(this.typeAhead){
15779             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15780         }
15781         if(this.editable !== false){
15782             this.inputEl().on("keyup", this.onKeyUp, this);
15783         }
15784         if(this.forceSelection){
15785             this.inputEl().on('blur', this.doForce, this);
15786         }
15787         
15788         if(this.multiple){
15789             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15790             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15791         }
15792     },
15793     
15794     initTickableEvents: function()
15795     {   
15796         this.createList();
15797         
15798         if(this.hiddenName){
15799             
15800             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15801             
15802             this.hiddenField.dom.value =
15803                 this.hiddenValue !== undefined ? this.hiddenValue :
15804                 this.value !== undefined ? this.value : '';
15805
15806             // prevent input submission
15807             this.el.dom.removeAttribute('name');
15808             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15809              
15810              
15811         }
15812         
15813 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15814         
15815         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15816         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15817         if(this.triggerList){
15818             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15819         }
15820          
15821         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15822         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15823         
15824         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15825         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15826         
15827         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15828         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15829         
15830         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15831         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15832         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15833         
15834         this.okBtn.hide();
15835         this.cancelBtn.hide();
15836         
15837         var _this = this;
15838         
15839         (function(){
15840             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15841             _this.list.setWidth(lw);
15842         }).defer(100);
15843         
15844         this.list.on('mouseover', this.onViewOver, this);
15845         this.list.on('mousemove', this.onViewMove, this);
15846         
15847         this.list.on('scroll', this.onViewScroll, this);
15848         
15849         if(!this.tpl){
15850             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15851                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15852         }
15853
15854         this.view = new Roo.View(this.list, this.tpl, {
15855             singleSelect:true,
15856             tickable:true,
15857             parent:this,
15858             store: this.store,
15859             selectedClass: this.selectedClass
15860         });
15861         
15862         //this.view.wrapEl.setDisplayed(false);
15863         this.view.on('click', this.onViewClick, this);
15864         
15865         
15866         
15867         this.store.on('beforeload', this.onBeforeLoad, this);
15868         this.store.on('load', this.onLoad, this);
15869         this.store.on('loadexception', this.onLoadException, this);
15870         
15871         if(this.editable){
15872             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15873                 "up" : function(e){
15874                     this.inKeyMode = true;
15875                     this.selectPrev();
15876                 },
15877
15878                 "down" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectNext();
15881                 },
15882
15883                 "enter" : function(e){
15884                     if(this.fireEvent("specialkey", this, e)){
15885                         this.onViewClick(false);
15886                     }
15887                     
15888                     return true;
15889                 },
15890
15891                 "esc" : function(e){
15892                     this.onTickableFooterButtonClick(e, false, false);
15893                 },
15894
15895                 "tab" : function(e){
15896                     this.fireEvent("specialkey", this, e);
15897                     
15898                     this.onTickableFooterButtonClick(e, false, false);
15899                     
15900                     return true;
15901                 },
15902
15903                 scope : this,
15904
15905                 doRelay : function(e, fn, key){
15906                     if(this.scope.isExpanded()){
15907                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15908                     }
15909                     return true;
15910                 },
15911
15912                 forceKeyDown: true
15913             });
15914         }
15915         
15916         this.queryDelay = Math.max(this.queryDelay || 10,
15917                 this.mode == 'local' ? 10 : 250);
15918         
15919         
15920         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15921         
15922         if(this.typeAhead){
15923             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15924         }
15925         
15926         if(this.editable !== false){
15927             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15928         }
15929         
15930         this.indicator = this.indicatorEl();
15931         
15932         if(this.indicator){
15933             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15934             this.indicator.hide();
15935         }
15936         
15937     },
15938
15939     onDestroy : function(){
15940         if(this.view){
15941             this.view.setStore(null);
15942             this.view.el.removeAllListeners();
15943             this.view.el.remove();
15944             this.view.purgeListeners();
15945         }
15946         if(this.list){
15947             this.list.dom.innerHTML  = '';
15948         }
15949         
15950         if(this.store){
15951             this.store.un('beforeload', this.onBeforeLoad, this);
15952             this.store.un('load', this.onLoad, this);
15953             this.store.un('loadexception', this.onLoadException, this);
15954         }
15955         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15956     },
15957
15958     // private
15959     fireKey : function(e){
15960         if(e.isNavKeyPress() && !this.list.isVisible()){
15961             this.fireEvent("specialkey", this, e);
15962         }
15963     },
15964
15965     // private
15966     onResize: function(w, h)
15967     {
15968         
15969         
15970 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15971 //        
15972 //        if(typeof w != 'number'){
15973 //            // we do not handle it!?!?
15974 //            return;
15975 //        }
15976 //        var tw = this.trigger.getWidth();
15977 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15978 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15979 //        var x = w - tw;
15980 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15981 //            
15982 //        //this.trigger.setStyle('left', x+'px');
15983 //        
15984 //        if(this.list && this.listWidth === undefined){
15985 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15986 //            this.list.setWidth(lw);
15987 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15988 //        }
15989         
15990     
15991         
15992     },
15993
15994     /**
15995      * Allow or prevent the user from directly editing the field text.  If false is passed,
15996      * the user will only be able to select from the items defined in the dropdown list.  This method
15997      * is the runtime equivalent of setting the 'editable' config option at config time.
15998      * @param {Boolean} value True to allow the user to directly edit the field text
15999      */
16000     setEditable : function(value){
16001         if(value == this.editable){
16002             return;
16003         }
16004         this.editable = value;
16005         if(!value){
16006             this.inputEl().dom.setAttribute('readOnly', true);
16007             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16008             this.inputEl().addClass('x-combo-noedit');
16009         }else{
16010             this.inputEl().dom.setAttribute('readOnly', false);
16011             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16012             this.inputEl().removeClass('x-combo-noedit');
16013         }
16014     },
16015
16016     // private
16017     
16018     onBeforeLoad : function(combo,opts){
16019         if(!this.hasFocus){
16020             return;
16021         }
16022          if (!opts.add) {
16023             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16024          }
16025         this.restrictHeight();
16026         this.selectedIndex = -1;
16027     },
16028
16029     // private
16030     onLoad : function(){
16031         
16032         this.hasQuery = false;
16033         
16034         if(!this.hasFocus){
16035             return;
16036         }
16037         
16038         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16039             this.loading.hide();
16040         }
16041         
16042         if(this.store.getCount() > 0){
16043             
16044             this.expand();
16045             this.restrictHeight();
16046             if(this.lastQuery == this.allQuery){
16047                 if(this.editable && !this.tickable){
16048                     this.inputEl().dom.select();
16049                 }
16050                 
16051                 if(
16052                     !this.selectByValue(this.value, true) &&
16053                     this.autoFocus && 
16054                     (
16055                         !this.store.lastOptions ||
16056                         typeof(this.store.lastOptions.add) == 'undefined' || 
16057                         this.store.lastOptions.add != true
16058                     )
16059                 ){
16060                     this.select(0, true);
16061                 }
16062             }else{
16063                 if(this.autoFocus){
16064                     this.selectNext();
16065                 }
16066                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16067                     this.taTask.delay(this.typeAheadDelay);
16068                 }
16069             }
16070         }else{
16071             this.onEmptyResults();
16072         }
16073         
16074         //this.el.focus();
16075     },
16076     // private
16077     onLoadException : function()
16078     {
16079         this.hasQuery = false;
16080         
16081         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16082             this.loading.hide();
16083         }
16084         
16085         if(this.tickable && this.editable){
16086             return;
16087         }
16088         
16089         this.collapse();
16090         // only causes errors at present
16091         //Roo.log(this.store.reader.jsonData);
16092         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16093             // fixme
16094             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16095         //}
16096         
16097         
16098     },
16099     // private
16100     onTypeAhead : function(){
16101         if(this.store.getCount() > 0){
16102             var r = this.store.getAt(0);
16103             var newValue = r.data[this.displayField];
16104             var len = newValue.length;
16105             var selStart = this.getRawValue().length;
16106             
16107             if(selStart != len){
16108                 this.setRawValue(newValue);
16109                 this.selectText(selStart, newValue.length);
16110             }
16111         }
16112     },
16113
16114     // private
16115     onSelect : function(record, index){
16116         
16117         if(this.fireEvent('beforeselect', this, record, index) !== false){
16118         
16119             this.setFromData(index > -1 ? record.data : false);
16120             
16121             this.collapse();
16122             this.fireEvent('select', this, record, index);
16123         }
16124     },
16125
16126     /**
16127      * Returns the currently selected field value or empty string if no value is set.
16128      * @return {String} value The selected value
16129      */
16130     getValue : function()
16131     {
16132         if(Roo.isIOS && this.useNativeIOS){
16133             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16134         }
16135         
16136         if(this.multiple){
16137             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16138         }
16139         
16140         if(this.valueField){
16141             return typeof this.value != 'undefined' ? this.value : '';
16142         }else{
16143             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16144         }
16145     },
16146     
16147     getRawValue : function()
16148     {
16149         if(Roo.isIOS && this.useNativeIOS){
16150             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16151         }
16152         
16153         var v = this.inputEl().getValue();
16154         
16155         return v;
16156     },
16157
16158     /**
16159      * Clears any text/value currently set in the field
16160      */
16161     clearValue : function(){
16162         
16163         if(this.hiddenField){
16164             this.hiddenField.dom.value = '';
16165         }
16166         this.value = '';
16167         this.setRawValue('');
16168         this.lastSelectionText = '';
16169         this.lastData = false;
16170         
16171         var close = this.closeTriggerEl();
16172         
16173         if(close){
16174             close.hide();
16175         }
16176         
16177         this.validate();
16178         
16179     },
16180
16181     /**
16182      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16183      * will be displayed in the field.  If the value does not match the data value of an existing item,
16184      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16185      * Otherwise the field will be blank (although the value will still be set).
16186      * @param {String} value The value to match
16187      */
16188     setValue : function(v)
16189     {
16190         if(Roo.isIOS && this.useNativeIOS){
16191             this.setIOSValue(v);
16192             return;
16193         }
16194         
16195         if(this.multiple){
16196             this.syncValue();
16197             return;
16198         }
16199         
16200         var text = v;
16201         if(this.valueField){
16202             var r = this.findRecord(this.valueField, v);
16203             if(r){
16204                 text = r.data[this.displayField];
16205             }else if(this.valueNotFoundText !== undefined){
16206                 text = this.valueNotFoundText;
16207             }
16208         }
16209         this.lastSelectionText = text;
16210         if(this.hiddenField){
16211             this.hiddenField.dom.value = v;
16212         }
16213         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16214         this.value = v;
16215         
16216         var close = this.closeTriggerEl();
16217         
16218         if(close){
16219             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16220         }
16221         
16222         this.validate();
16223     },
16224     /**
16225      * @property {Object} the last set data for the element
16226      */
16227     
16228     lastData : false,
16229     /**
16230      * Sets the value of the field based on a object which is related to the record format for the store.
16231      * @param {Object} value the value to set as. or false on reset?
16232      */
16233     setFromData : function(o){
16234         
16235         if(this.multiple){
16236             this.addItem(o);
16237             return;
16238         }
16239             
16240         var dv = ''; // display value
16241         var vv = ''; // value value..
16242         this.lastData = o;
16243         if (this.displayField) {
16244             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16245         } else {
16246             // this is an error condition!!!
16247             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16248         }
16249         
16250         if(this.valueField){
16251             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16252         }
16253         
16254         var close = this.closeTriggerEl();
16255         
16256         if(close){
16257             if(dv.length || vv * 1 > 0){
16258                 close.show() ;
16259                 this.blockFocus=true;
16260             } else {
16261                 close.hide();
16262             }             
16263         }
16264         
16265         if(this.hiddenField){
16266             this.hiddenField.dom.value = vv;
16267             
16268             this.lastSelectionText = dv;
16269             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16270             this.value = vv;
16271             return;
16272         }
16273         // no hidden field.. - we store the value in 'value', but still display
16274         // display field!!!!
16275         this.lastSelectionText = dv;
16276         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16277         this.value = vv;
16278         
16279         
16280         
16281     },
16282     // private
16283     reset : function(){
16284         // overridden so that last data is reset..
16285         
16286         if(this.multiple){
16287             this.clearItem();
16288             return;
16289         }
16290         
16291         this.setValue(this.originalValue);
16292         //this.clearInvalid();
16293         this.lastData = false;
16294         if (this.view) {
16295             this.view.clearSelections();
16296         }
16297         
16298         this.validate();
16299     },
16300     // private
16301     findRecord : function(prop, value){
16302         var record;
16303         if(this.store.getCount() > 0){
16304             this.store.each(function(r){
16305                 if(r.data[prop] == value){
16306                     record = r;
16307                     return false;
16308                 }
16309                 return true;
16310             });
16311         }
16312         return record;
16313     },
16314     
16315     getName: function()
16316     {
16317         // returns hidden if it's set..
16318         if (!this.rendered) {return ''};
16319         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16320         
16321     },
16322     // private
16323     onViewMove : function(e, t){
16324         this.inKeyMode = false;
16325     },
16326
16327     // private
16328     onViewOver : function(e, t){
16329         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16330             return;
16331         }
16332         var item = this.view.findItemFromChild(t);
16333         
16334         if(item){
16335             var index = this.view.indexOf(item);
16336             this.select(index, false);
16337         }
16338     },
16339
16340     // private
16341     onViewClick : function(view, doFocus, el, e)
16342     {
16343         var index = this.view.getSelectedIndexes()[0];
16344         
16345         var r = this.store.getAt(index);
16346         
16347         if(this.tickable){
16348             
16349             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16350                 return;
16351             }
16352             
16353             var rm = false;
16354             var _this = this;
16355             
16356             Roo.each(this.tickItems, function(v,k){
16357                 
16358                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16359                     Roo.log(v);
16360                     _this.tickItems.splice(k, 1);
16361                     
16362                     if(typeof(e) == 'undefined' && view == false){
16363                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16364                     }
16365                     
16366                     rm = true;
16367                     return;
16368                 }
16369             });
16370             
16371             if(rm){
16372                 return;
16373             }
16374             
16375             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16376                 this.tickItems.push(r.data);
16377             }
16378             
16379             if(typeof(e) == 'undefined' && view == false){
16380                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16381             }
16382                     
16383             return;
16384         }
16385         
16386         if(r){
16387             this.onSelect(r, index);
16388         }
16389         if(doFocus !== false && !this.blockFocus){
16390             this.inputEl().focus();
16391         }
16392     },
16393
16394     // private
16395     restrictHeight : function(){
16396         //this.innerList.dom.style.height = '';
16397         //var inner = this.innerList.dom;
16398         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16399         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16400         //this.list.beginUpdate();
16401         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16402         this.list.alignTo(this.inputEl(), this.listAlign);
16403         this.list.alignTo(this.inputEl(), this.listAlign);
16404         //this.list.endUpdate();
16405     },
16406
16407     // private
16408     onEmptyResults : function(){
16409         
16410         if(this.tickable && this.editable){
16411             this.hasFocus = false;
16412             this.restrictHeight();
16413             return;
16414         }
16415         
16416         this.collapse();
16417     },
16418
16419     /**
16420      * Returns true if the dropdown list is expanded, else false.
16421      */
16422     isExpanded : function(){
16423         return this.list.isVisible();
16424     },
16425
16426     /**
16427      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16428      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16429      * @param {String} value The data value of the item to select
16430      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16431      * selected item if it is not currently in view (defaults to true)
16432      * @return {Boolean} True if the value matched an item in the list, else false
16433      */
16434     selectByValue : function(v, scrollIntoView){
16435         if(v !== undefined && v !== null){
16436             var r = this.findRecord(this.valueField || this.displayField, v);
16437             if(r){
16438                 this.select(this.store.indexOf(r), scrollIntoView);
16439                 return true;
16440             }
16441         }
16442         return false;
16443     },
16444
16445     /**
16446      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16447      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16448      * @param {Number} index The zero-based index of the list item to select
16449      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16450      * selected item if it is not currently in view (defaults to true)
16451      */
16452     select : function(index, scrollIntoView){
16453         this.selectedIndex = index;
16454         this.view.select(index);
16455         if(scrollIntoView !== false){
16456             var el = this.view.getNode(index);
16457             /*
16458              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16459              */
16460             if(el){
16461                 this.list.scrollChildIntoView(el, false);
16462             }
16463         }
16464     },
16465
16466     // private
16467     selectNext : function(){
16468         var ct = this.store.getCount();
16469         if(ct > 0){
16470             if(this.selectedIndex == -1){
16471                 this.select(0);
16472             }else if(this.selectedIndex < ct-1){
16473                 this.select(this.selectedIndex+1);
16474             }
16475         }
16476     },
16477
16478     // private
16479     selectPrev : function(){
16480         var ct = this.store.getCount();
16481         if(ct > 0){
16482             if(this.selectedIndex == -1){
16483                 this.select(0);
16484             }else if(this.selectedIndex != 0){
16485                 this.select(this.selectedIndex-1);
16486             }
16487         }
16488     },
16489
16490     // private
16491     onKeyUp : function(e){
16492         if(this.editable !== false && !e.isSpecialKey()){
16493             this.lastKey = e.getKey();
16494             this.dqTask.delay(this.queryDelay);
16495         }
16496     },
16497
16498     // private
16499     validateBlur : function(){
16500         return !this.list || !this.list.isVisible();   
16501     },
16502
16503     // private
16504     initQuery : function(){
16505         
16506         var v = this.getRawValue();
16507         
16508         if(this.tickable && this.editable){
16509             v = this.tickableInputEl().getValue();
16510         }
16511         
16512         this.doQuery(v);
16513     },
16514
16515     // private
16516     doForce : function(){
16517         if(this.inputEl().dom.value.length > 0){
16518             this.inputEl().dom.value =
16519                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16520              
16521         }
16522     },
16523
16524     /**
16525      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16526      * query allowing the query action to be canceled if needed.
16527      * @param {String} query The SQL query to execute
16528      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16529      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16530      * saved in the current store (defaults to false)
16531      */
16532     doQuery : function(q, forceAll){
16533         
16534         if(q === undefined || q === null){
16535             q = '';
16536         }
16537         var qe = {
16538             query: q,
16539             forceAll: forceAll,
16540             combo: this,
16541             cancel:false
16542         };
16543         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16544             return false;
16545         }
16546         q = qe.query;
16547         
16548         forceAll = qe.forceAll;
16549         if(forceAll === true || (q.length >= this.minChars)){
16550             
16551             this.hasQuery = true;
16552             
16553             if(this.lastQuery != q || this.alwaysQuery){
16554                 this.lastQuery = q;
16555                 if(this.mode == 'local'){
16556                     this.selectedIndex = -1;
16557                     if(forceAll){
16558                         this.store.clearFilter();
16559                     }else{
16560                         
16561                         if(this.specialFilter){
16562                             this.fireEvent('specialfilter', this);
16563                             this.onLoad();
16564                             return;
16565                         }
16566                         
16567                         this.store.filter(this.displayField, q);
16568                     }
16569                     
16570                     this.store.fireEvent("datachanged", this.store);
16571                     
16572                     this.onLoad();
16573                     
16574                     
16575                 }else{
16576                     
16577                     this.store.baseParams[this.queryParam] = q;
16578                     
16579                     var options = {params : this.getParams(q)};
16580                     
16581                     if(this.loadNext){
16582                         options.add = true;
16583                         options.params.start = this.page * this.pageSize;
16584                     }
16585                     
16586                     this.store.load(options);
16587                     
16588                     /*
16589                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16590                      *  we should expand the list on onLoad
16591                      *  so command out it
16592                      */
16593 //                    this.expand();
16594                 }
16595             }else{
16596                 this.selectedIndex = -1;
16597                 this.onLoad();   
16598             }
16599         }
16600         
16601         this.loadNext = false;
16602     },
16603     
16604     // private
16605     getParams : function(q){
16606         var p = {};
16607         //p[this.queryParam] = q;
16608         
16609         if(this.pageSize){
16610             p.start = 0;
16611             p.limit = this.pageSize;
16612         }
16613         return p;
16614     },
16615
16616     /**
16617      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16618      */
16619     collapse : function(){
16620         if(!this.isExpanded()){
16621             return;
16622         }
16623         
16624         this.list.hide();
16625         
16626         this.hasFocus = false;
16627         
16628         if(this.tickable){
16629             this.okBtn.hide();
16630             this.cancelBtn.hide();
16631             this.trigger.show();
16632             
16633             if(this.editable){
16634                 this.tickableInputEl().dom.value = '';
16635                 this.tickableInputEl().blur();
16636             }
16637             
16638         }
16639         
16640         Roo.get(document).un('mousedown', this.collapseIf, this);
16641         Roo.get(document).un('mousewheel', this.collapseIf, this);
16642         if (!this.editable) {
16643             Roo.get(document).un('keydown', this.listKeyPress, this);
16644         }
16645         this.fireEvent('collapse', this);
16646         
16647         this.validate();
16648     },
16649
16650     // private
16651     collapseIf : function(e){
16652         var in_combo  = e.within(this.el);
16653         var in_list =  e.within(this.list);
16654         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16655         
16656         if (in_combo || in_list || is_list) {
16657             //e.stopPropagation();
16658             return;
16659         }
16660         
16661         if(this.tickable){
16662             this.onTickableFooterButtonClick(e, false, false);
16663         }
16664
16665         this.collapse();
16666         
16667     },
16668
16669     /**
16670      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16671      */
16672     expand : function(){
16673        
16674         if(this.isExpanded() || !this.hasFocus){
16675             return;
16676         }
16677         
16678         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16679         this.list.setWidth(lw);
16680         
16681         Roo.log('expand');
16682         
16683         this.list.show();
16684         
16685         this.restrictHeight();
16686         
16687         if(this.tickable){
16688             
16689             this.tickItems = Roo.apply([], this.item);
16690             
16691             this.okBtn.show();
16692             this.cancelBtn.show();
16693             this.trigger.hide();
16694             
16695             if(this.editable){
16696                 this.tickableInputEl().focus();
16697             }
16698             
16699         }
16700         
16701         Roo.get(document).on('mousedown', this.collapseIf, this);
16702         Roo.get(document).on('mousewheel', this.collapseIf, this);
16703         if (!this.editable) {
16704             Roo.get(document).on('keydown', this.listKeyPress, this);
16705         }
16706         
16707         this.fireEvent('expand', this);
16708     },
16709
16710     // private
16711     // Implements the default empty TriggerField.onTriggerClick function
16712     onTriggerClick : function(e)
16713     {
16714         Roo.log('trigger click');
16715         
16716         if(this.disabled || !this.triggerList){
16717             return;
16718         }
16719         
16720         this.page = 0;
16721         this.loadNext = false;
16722         
16723         if(this.isExpanded()){
16724             this.collapse();
16725             if (!this.blockFocus) {
16726                 this.inputEl().focus();
16727             }
16728             
16729         }else {
16730             this.hasFocus = true;
16731             if(this.triggerAction == 'all') {
16732                 this.doQuery(this.allQuery, true);
16733             } else {
16734                 this.doQuery(this.getRawValue());
16735             }
16736             if (!this.blockFocus) {
16737                 this.inputEl().focus();
16738             }
16739         }
16740     },
16741     
16742     onTickableTriggerClick : function(e)
16743     {
16744         if(this.disabled){
16745             return;
16746         }
16747         
16748         this.page = 0;
16749         this.loadNext = false;
16750         this.hasFocus = true;
16751         
16752         if(this.triggerAction == 'all') {
16753             this.doQuery(this.allQuery, true);
16754         } else {
16755             this.doQuery(this.getRawValue());
16756         }
16757     },
16758     
16759     onSearchFieldClick : function(e)
16760     {
16761         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16762             this.onTickableFooterButtonClick(e, false, false);
16763             return;
16764         }
16765         
16766         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16767             return;
16768         }
16769         
16770         this.page = 0;
16771         this.loadNext = false;
16772         this.hasFocus = true;
16773         
16774         if(this.triggerAction == 'all') {
16775             this.doQuery(this.allQuery, true);
16776         } else {
16777             this.doQuery(this.getRawValue());
16778         }
16779     },
16780     
16781     listKeyPress : function(e)
16782     {
16783         //Roo.log('listkeypress');
16784         // scroll to first matching element based on key pres..
16785         if (e.isSpecialKey()) {
16786             return false;
16787         }
16788         var k = String.fromCharCode(e.getKey()).toUpperCase();
16789         //Roo.log(k);
16790         var match  = false;
16791         var csel = this.view.getSelectedNodes();
16792         var cselitem = false;
16793         if (csel.length) {
16794             var ix = this.view.indexOf(csel[0]);
16795             cselitem  = this.store.getAt(ix);
16796             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16797                 cselitem = false;
16798             }
16799             
16800         }
16801         
16802         this.store.each(function(v) { 
16803             if (cselitem) {
16804                 // start at existing selection.
16805                 if (cselitem.id == v.id) {
16806                     cselitem = false;
16807                 }
16808                 return true;
16809             }
16810                 
16811             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16812                 match = this.store.indexOf(v);
16813                 return false;
16814             }
16815             return true;
16816         }, this);
16817         
16818         if (match === false) {
16819             return true; // no more action?
16820         }
16821         // scroll to?
16822         this.view.select(match);
16823         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16824         sn.scrollIntoView(sn.dom.parentNode, false);
16825     },
16826     
16827     onViewScroll : function(e, t){
16828         
16829         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){
16830             return;
16831         }
16832         
16833         this.hasQuery = true;
16834         
16835         this.loading = this.list.select('.loading', true).first();
16836         
16837         if(this.loading === null){
16838             this.list.createChild({
16839                 tag: 'div',
16840                 cls: 'loading roo-select2-more-results roo-select2-active',
16841                 html: 'Loading more results...'
16842             });
16843             
16844             this.loading = this.list.select('.loading', true).first();
16845             
16846             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16847             
16848             this.loading.hide();
16849         }
16850         
16851         this.loading.show();
16852         
16853         var _combo = this;
16854         
16855         this.page++;
16856         this.loadNext = true;
16857         
16858         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16859         
16860         return;
16861     },
16862     
16863     addItem : function(o)
16864     {   
16865         var dv = ''; // display value
16866         
16867         if (this.displayField) {
16868             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16869         } else {
16870             // this is an error condition!!!
16871             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16872         }
16873         
16874         if(!dv.length){
16875             return;
16876         }
16877         
16878         var choice = this.choices.createChild({
16879             tag: 'li',
16880             cls: 'roo-select2-search-choice',
16881             cn: [
16882                 {
16883                     tag: 'div',
16884                     html: dv
16885                 },
16886                 {
16887                     tag: 'a',
16888                     href: '#',
16889                     cls: 'roo-select2-search-choice-close fa fa-times',
16890                     tabindex: '-1'
16891                 }
16892             ]
16893             
16894         }, this.searchField);
16895         
16896         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16897         
16898         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16899         
16900         this.item.push(o);
16901         
16902         this.lastData = o;
16903         
16904         this.syncValue();
16905         
16906         this.inputEl().dom.value = '';
16907         
16908         this.validate();
16909     },
16910     
16911     onRemoveItem : function(e, _self, o)
16912     {
16913         e.preventDefault();
16914         
16915         this.lastItem = Roo.apply([], this.item);
16916         
16917         var index = this.item.indexOf(o.data) * 1;
16918         
16919         if( index < 0){
16920             Roo.log('not this item?!');
16921             return;
16922         }
16923         
16924         this.item.splice(index, 1);
16925         o.item.remove();
16926         
16927         this.syncValue();
16928         
16929         this.fireEvent('remove', this, e);
16930         
16931         this.validate();
16932         
16933     },
16934     
16935     syncValue : function()
16936     {
16937         if(!this.item.length){
16938             this.clearValue();
16939             return;
16940         }
16941             
16942         var value = [];
16943         var _this = this;
16944         Roo.each(this.item, function(i){
16945             if(_this.valueField){
16946                 value.push(i[_this.valueField]);
16947                 return;
16948             }
16949
16950             value.push(i);
16951         });
16952
16953         this.value = value.join(',');
16954
16955         if(this.hiddenField){
16956             this.hiddenField.dom.value = this.value;
16957         }
16958         
16959         this.store.fireEvent("datachanged", this.store);
16960         
16961         this.validate();
16962     },
16963     
16964     clearItem : function()
16965     {
16966         if(!this.multiple){
16967             return;
16968         }
16969         
16970         this.item = [];
16971         
16972         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16973            c.remove();
16974         });
16975         
16976         this.syncValue();
16977         
16978         this.validate();
16979         
16980         if(this.tickable && !Roo.isTouch){
16981             this.view.refresh();
16982         }
16983     },
16984     
16985     inputEl: function ()
16986     {
16987         if(Roo.isIOS && this.useNativeIOS){
16988             return this.el.select('select.roo-ios-select', true).first();
16989         }
16990         
16991         if(Roo.isTouch && this.mobileTouchView){
16992             return this.el.select('input.form-control',true).first();
16993         }
16994         
16995         if(this.tickable){
16996             return this.searchField;
16997         }
16998         
16999         return this.el.select('input.form-control',true).first();
17000     },
17001     
17002     onTickableFooterButtonClick : function(e, btn, el)
17003     {
17004         e.preventDefault();
17005         
17006         this.lastItem = Roo.apply([], this.item);
17007         
17008         if(btn && btn.name == 'cancel'){
17009             this.tickItems = Roo.apply([], this.item);
17010             this.collapse();
17011             return;
17012         }
17013         
17014         this.clearItem();
17015         
17016         var _this = this;
17017         
17018         Roo.each(this.tickItems, function(o){
17019             _this.addItem(o);
17020         });
17021         
17022         this.collapse();
17023         
17024     },
17025     
17026     validate : function()
17027     {
17028         if(this.getVisibilityEl().hasClass('hidden')){
17029             return true;
17030         }
17031         
17032         var v = this.getRawValue();
17033         
17034         if(this.multiple){
17035             v = this.getValue();
17036         }
17037         
17038         if(this.disabled || this.allowBlank || v.length){
17039             this.markValid();
17040             return true;
17041         }
17042         
17043         this.markInvalid();
17044         return false;
17045     },
17046     
17047     tickableInputEl : function()
17048     {
17049         if(!this.tickable || !this.editable){
17050             return this.inputEl();
17051         }
17052         
17053         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17054     },
17055     
17056     
17057     getAutoCreateTouchView : function()
17058     {
17059         var id = Roo.id();
17060         
17061         var cfg = {
17062             cls: 'form-group' //input-group
17063         };
17064         
17065         var input =  {
17066             tag: 'input',
17067             id : id,
17068             type : this.inputType,
17069             cls : 'form-control x-combo-noedit',
17070             autocomplete: 'new-password',
17071             placeholder : this.placeholder || '',
17072             readonly : true
17073         };
17074         
17075         if (this.name) {
17076             input.name = this.name;
17077         }
17078         
17079         if (this.size) {
17080             input.cls += ' input-' + this.size;
17081         }
17082         
17083         if (this.disabled) {
17084             input.disabled = true;
17085         }
17086         
17087         var inputblock = {
17088             cls : 'roo-combobox-wrap',
17089             cn : [
17090                 input
17091             ]
17092         };
17093         
17094         if(this.before){
17095             inputblock.cls += ' input-group';
17096             
17097             inputblock.cn.unshift({
17098                 tag :'span',
17099                 cls : 'input-group-addon input-group-prepend input-group-text',
17100                 html : this.before
17101             });
17102         }
17103         
17104         if(this.removable && !this.multiple){
17105             inputblock.cls += ' roo-removable';
17106             
17107             inputblock.cn.push({
17108                 tag: 'button',
17109                 html : 'x',
17110                 cls : 'roo-combo-removable-btn close'
17111             });
17112         }
17113
17114         if(this.hasFeedback && !this.allowBlank){
17115             
17116             inputblock.cls += ' has-feedback';
17117             
17118             inputblock.cn.push({
17119                 tag: 'span',
17120                 cls: 'glyphicon form-control-feedback'
17121             });
17122             
17123         }
17124         
17125         if (this.after) {
17126             
17127             inputblock.cls += (this.before) ? '' : ' input-group';
17128             
17129             inputblock.cn.push({
17130                 tag :'span',
17131                 cls : 'input-group-addon input-group-append input-group-text',
17132                 html : this.after
17133             });
17134         }
17135
17136         
17137         var ibwrap = inputblock;
17138         
17139         if(this.multiple){
17140             ibwrap = {
17141                 tag: 'ul',
17142                 cls: 'roo-select2-choices',
17143                 cn:[
17144                     {
17145                         tag: 'li',
17146                         cls: 'roo-select2-search-field',
17147                         cn: [
17148
17149                             inputblock
17150                         ]
17151                     }
17152                 ]
17153             };
17154         
17155             
17156         }
17157         
17158         var combobox = {
17159             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17160             cn: [
17161                 {
17162                     tag: 'input',
17163                     type : 'hidden',
17164                     cls: 'form-hidden-field'
17165                 },
17166                 ibwrap
17167             ]
17168         };
17169         
17170         if(!this.multiple && this.showToggleBtn){
17171             
17172             var caret = {
17173                 cls: 'caret'
17174             };
17175             
17176             if (this.caret != false) {
17177                 caret = {
17178                      tag: 'i',
17179                      cls: 'fa fa-' + this.caret
17180                 };
17181                 
17182             }
17183             
17184             combobox.cn.push({
17185                 tag :'span',
17186                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17187                 cn : [
17188                     Roo.bootstrap.version == 3 ? caret : '',
17189                     {
17190                         tag: 'span',
17191                         cls: 'combobox-clear',
17192                         cn  : [
17193                             {
17194                                 tag : 'i',
17195                                 cls: 'icon-remove'
17196                             }
17197                         ]
17198                     }
17199                 ]
17200
17201             })
17202         }
17203         
17204         if(this.multiple){
17205             combobox.cls += ' roo-select2-container-multi';
17206         }
17207         
17208         var align = this.labelAlign || this.parentLabelAlign();
17209         
17210         if (align ==='left' && this.fieldLabel.length) {
17211
17212             cfg.cn = [
17213                 {
17214                    tag : 'i',
17215                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17216                    tooltip : 'This field is required'
17217                 },
17218                 {
17219                     tag: 'label',
17220                     cls : 'control-label col-form-label',
17221                     html : this.fieldLabel
17222
17223                 },
17224                 {
17225                     cls : 'roo-combobox-wrap ', 
17226                     cn: [
17227                         combobox
17228                     ]
17229                 }
17230             ];
17231             
17232             var labelCfg = cfg.cn[1];
17233             var contentCfg = cfg.cn[2];
17234             
17235
17236             if(this.indicatorpos == 'right'){
17237                 cfg.cn = [
17238                     {
17239                         tag: 'label',
17240                         'for' :  id,
17241                         cls : 'control-label col-form-label',
17242                         cn : [
17243                             {
17244                                 tag : 'span',
17245                                 html : this.fieldLabel
17246                             },
17247                             {
17248                                 tag : 'i',
17249                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17250                                 tooltip : 'This field is required'
17251                             }
17252                         ]
17253                     },
17254                     {
17255                         cls : "roo-combobox-wrap ",
17256                         cn: [
17257                             combobox
17258                         ]
17259                     }
17260
17261                 ];
17262                 
17263                 labelCfg = cfg.cn[0];
17264                 contentCfg = cfg.cn[1];
17265             }
17266             
17267            
17268             
17269             if(this.labelWidth > 12){
17270                 labelCfg.style = "width: " + this.labelWidth + 'px';
17271             }
17272            
17273             if(this.labelWidth < 13 && this.labelmd == 0){
17274                 this.labelmd = this.labelWidth;
17275             }
17276             
17277             if(this.labellg > 0){
17278                 labelCfg.cls += ' col-lg-' + this.labellg;
17279                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17280             }
17281             
17282             if(this.labelmd > 0){
17283                 labelCfg.cls += ' col-md-' + this.labelmd;
17284                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17285             }
17286             
17287             if(this.labelsm > 0){
17288                 labelCfg.cls += ' col-sm-' + this.labelsm;
17289                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17290             }
17291             
17292             if(this.labelxs > 0){
17293                 labelCfg.cls += ' col-xs-' + this.labelxs;
17294                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17295             }
17296                 
17297                 
17298         } else if ( this.fieldLabel.length) {
17299             cfg.cn = [
17300                 {
17301                    tag : 'i',
17302                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17303                    tooltip : 'This field is required'
17304                 },
17305                 {
17306                     tag: 'label',
17307                     cls : 'control-label',
17308                     html : this.fieldLabel
17309
17310                 },
17311                 {
17312                     cls : '', 
17313                     cn: [
17314                         combobox
17315                     ]
17316                 }
17317             ];
17318             
17319             if(this.indicatorpos == 'right'){
17320                 cfg.cn = [
17321                     {
17322                         tag: 'label',
17323                         cls : 'control-label',
17324                         html : this.fieldLabel,
17325                         cn : [
17326                             {
17327                                tag : 'i',
17328                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17329                                tooltip : 'This field is required'
17330                             }
17331                         ]
17332                     },
17333                     {
17334                         cls : '', 
17335                         cn: [
17336                             combobox
17337                         ]
17338                     }
17339                 ];
17340             }
17341         } else {
17342             cfg.cn = combobox;    
17343         }
17344         
17345         
17346         var settings = this;
17347         
17348         ['xs','sm','md','lg'].map(function(size){
17349             if (settings[size]) {
17350                 cfg.cls += ' col-' + size + '-' + settings[size];
17351             }
17352         });
17353         
17354         return cfg;
17355     },
17356     
17357     initTouchView : function()
17358     {
17359         this.renderTouchView();
17360         
17361         this.touchViewEl.on('scroll', function(){
17362             this.el.dom.scrollTop = 0;
17363         }, this);
17364         
17365         this.originalValue = this.getValue();
17366         
17367         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17368         
17369         this.inputEl().on("click", this.showTouchView, this);
17370         if (this.triggerEl) {
17371             this.triggerEl.on("click", this.showTouchView, this);
17372         }
17373         
17374         
17375         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17376         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17377         
17378         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17379         
17380         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17381         this.store.on('load', this.onTouchViewLoad, this);
17382         this.store.on('loadexception', this.onTouchViewLoadException, this);
17383         
17384         if(this.hiddenName){
17385             
17386             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17387             
17388             this.hiddenField.dom.value =
17389                 this.hiddenValue !== undefined ? this.hiddenValue :
17390                 this.value !== undefined ? this.value : '';
17391         
17392             this.el.dom.removeAttribute('name');
17393             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17394         }
17395         
17396         if(this.multiple){
17397             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17398             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17399         }
17400         
17401         if(this.removable && !this.multiple){
17402             var close = this.closeTriggerEl();
17403             if(close){
17404                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17405                 close.on('click', this.removeBtnClick, this, close);
17406             }
17407         }
17408         /*
17409          * fix the bug in Safari iOS8
17410          */
17411         this.inputEl().on("focus", function(e){
17412             document.activeElement.blur();
17413         }, this);
17414         
17415         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17416         
17417         return;
17418         
17419         
17420     },
17421     
17422     renderTouchView : function()
17423     {
17424         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17425         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17426         
17427         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17428         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17429         
17430         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17431         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17432         this.touchViewBodyEl.setStyle('overflow', 'auto');
17433         
17434         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17435         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17438         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440     },
17441     
17442     showTouchView : function()
17443     {
17444         if(this.disabled){
17445             return;
17446         }
17447         
17448         this.touchViewHeaderEl.hide();
17449
17450         if(this.modalTitle.length){
17451             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17452             this.touchViewHeaderEl.show();
17453         }
17454
17455         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17456         this.touchViewEl.show();
17457
17458         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17459         
17460         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17461         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17462
17463         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17464
17465         if(this.modalTitle.length){
17466             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17467         }
17468         
17469         this.touchViewBodyEl.setHeight(bodyHeight);
17470
17471         if(this.animate){
17472             var _this = this;
17473             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17474         }else{
17475             this.touchViewEl.addClass(['in','show']);
17476         }
17477         
17478         if(this._touchViewMask){
17479             Roo.get(document.body).addClass("x-body-masked");
17480             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17481             this._touchViewMask.setStyle('z-index', 10000);
17482             this._touchViewMask.addClass('show');
17483         }
17484         
17485         this.doTouchViewQuery();
17486         
17487     },
17488     
17489     hideTouchView : function()
17490     {
17491         this.touchViewEl.removeClass(['in','show']);
17492
17493         if(this.animate){
17494             var _this = this;
17495             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17496         }else{
17497             this.touchViewEl.setStyle('display', 'none');
17498         }
17499         
17500         if(this._touchViewMask){
17501             this._touchViewMask.removeClass('show');
17502             Roo.get(document.body).removeClass("x-body-masked");
17503         }
17504     },
17505     
17506     setTouchViewValue : function()
17507     {
17508         if(this.multiple){
17509             this.clearItem();
17510         
17511             var _this = this;
17512
17513             Roo.each(this.tickItems, function(o){
17514                 this.addItem(o);
17515             }, this);
17516         }
17517         
17518         this.hideTouchView();
17519     },
17520     
17521     doTouchViewQuery : function()
17522     {
17523         var qe = {
17524             query: '',
17525             forceAll: true,
17526             combo: this,
17527             cancel:false
17528         };
17529         
17530         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17531             return false;
17532         }
17533         
17534         if(!this.alwaysQuery || this.mode == 'local'){
17535             this.onTouchViewLoad();
17536             return;
17537         }
17538         
17539         this.store.load();
17540     },
17541     
17542     onTouchViewBeforeLoad : function(combo,opts)
17543     {
17544         return;
17545     },
17546
17547     // private
17548     onTouchViewLoad : function()
17549     {
17550         if(this.store.getCount() < 1){
17551             this.onTouchViewEmptyResults();
17552             return;
17553         }
17554         
17555         this.clearTouchView();
17556         
17557         var rawValue = this.getRawValue();
17558         
17559         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17560         
17561         this.tickItems = [];
17562         
17563         this.store.data.each(function(d, rowIndex){
17564             var row = this.touchViewListGroup.createChild(template);
17565             
17566             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17567                 row.addClass(d.data.cls);
17568             }
17569             
17570             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17571                 var cfg = {
17572                     data : d.data,
17573                     html : d.data[this.displayField]
17574                 };
17575                 
17576                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17577                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17578                 }
17579             }
17580             row.removeClass('selected');
17581             if(!this.multiple && this.valueField &&
17582                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17583             {
17584                 // radio buttons..
17585                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17586                 row.addClass('selected');
17587             }
17588             
17589             if(this.multiple && this.valueField &&
17590                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17591             {
17592                 
17593                 // checkboxes...
17594                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17595                 this.tickItems.push(d.data);
17596             }
17597             
17598             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17599             
17600         }, this);
17601         
17602         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17603         
17604         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17605
17606         if(this.modalTitle.length){
17607             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17608         }
17609
17610         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17611         
17612         if(this.mobile_restrict_height && listHeight < bodyHeight){
17613             this.touchViewBodyEl.setHeight(listHeight);
17614         }
17615         
17616         var _this = this;
17617         
17618         if(firstChecked && listHeight > bodyHeight){
17619             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17620         }
17621         
17622     },
17623     
17624     onTouchViewLoadException : function()
17625     {
17626         this.hideTouchView();
17627     },
17628     
17629     onTouchViewEmptyResults : function()
17630     {
17631         this.clearTouchView();
17632         
17633         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17634         
17635         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17636         
17637     },
17638     
17639     clearTouchView : function()
17640     {
17641         this.touchViewListGroup.dom.innerHTML = '';
17642     },
17643     
17644     onTouchViewClick : function(e, el, o)
17645     {
17646         e.preventDefault();
17647         
17648         var row = o.row;
17649         var rowIndex = o.rowIndex;
17650         
17651         var r = this.store.getAt(rowIndex);
17652         
17653         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17654             
17655             if(!this.multiple){
17656                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17657                     c.dom.removeAttribute('checked');
17658                 }, this);
17659
17660                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17661
17662                 this.setFromData(r.data);
17663
17664                 var close = this.closeTriggerEl();
17665
17666                 if(close){
17667                     close.show();
17668                 }
17669
17670                 this.hideTouchView();
17671
17672                 this.fireEvent('select', this, r, rowIndex);
17673
17674                 return;
17675             }
17676
17677             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17678                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17679                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17680                 return;
17681             }
17682
17683             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17684             this.addItem(r.data);
17685             this.tickItems.push(r.data);
17686         }
17687     },
17688     
17689     getAutoCreateNativeIOS : function()
17690     {
17691         var cfg = {
17692             cls: 'form-group' //input-group,
17693         };
17694         
17695         var combobox =  {
17696             tag: 'select',
17697             cls : 'roo-ios-select'
17698         };
17699         
17700         if (this.name) {
17701             combobox.name = this.name;
17702         }
17703         
17704         if (this.disabled) {
17705             combobox.disabled = true;
17706         }
17707         
17708         var settings = this;
17709         
17710         ['xs','sm','md','lg'].map(function(size){
17711             if (settings[size]) {
17712                 cfg.cls += ' col-' + size + '-' + settings[size];
17713             }
17714         });
17715         
17716         cfg.cn = combobox;
17717         
17718         return cfg;
17719         
17720     },
17721     
17722     initIOSView : function()
17723     {
17724         this.store.on('load', this.onIOSViewLoad, this);
17725         
17726         return;
17727     },
17728     
17729     onIOSViewLoad : function()
17730     {
17731         if(this.store.getCount() < 1){
17732             return;
17733         }
17734         
17735         this.clearIOSView();
17736         
17737         if(this.allowBlank) {
17738             
17739             var default_text = '-- SELECT --';
17740             
17741             if(this.placeholder.length){
17742                 default_text = this.placeholder;
17743             }
17744             
17745             if(this.emptyTitle.length){
17746                 default_text += ' - ' + this.emptyTitle + ' -';
17747             }
17748             
17749             var opt = this.inputEl().createChild({
17750                 tag: 'option',
17751                 value : 0,
17752                 html : default_text
17753             });
17754             
17755             var o = {};
17756             o[this.valueField] = 0;
17757             o[this.displayField] = default_text;
17758             
17759             this.ios_options.push({
17760                 data : o,
17761                 el : opt
17762             });
17763             
17764         }
17765         
17766         this.store.data.each(function(d, rowIndex){
17767             
17768             var html = '';
17769             
17770             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17771                 html = d.data[this.displayField];
17772             }
17773             
17774             var value = '';
17775             
17776             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17777                 value = d.data[this.valueField];
17778             }
17779             
17780             var option = {
17781                 tag: 'option',
17782                 value : value,
17783                 html : html
17784             };
17785             
17786             if(this.value == d.data[this.valueField]){
17787                 option['selected'] = true;
17788             }
17789             
17790             var opt = this.inputEl().createChild(option);
17791             
17792             this.ios_options.push({
17793                 data : d.data,
17794                 el : opt
17795             });
17796             
17797         }, this);
17798         
17799         this.inputEl().on('change', function(){
17800            this.fireEvent('select', this);
17801         }, this);
17802         
17803     },
17804     
17805     clearIOSView: function()
17806     {
17807         this.inputEl().dom.innerHTML = '';
17808         
17809         this.ios_options = [];
17810     },
17811     
17812     setIOSValue: function(v)
17813     {
17814         this.value = v;
17815         
17816         if(!this.ios_options){
17817             return;
17818         }
17819         
17820         Roo.each(this.ios_options, function(opts){
17821            
17822            opts.el.dom.removeAttribute('selected');
17823            
17824            if(opts.data[this.valueField] != v){
17825                return;
17826            }
17827            
17828            opts.el.dom.setAttribute('selected', true);
17829            
17830         }, this);
17831     }
17832
17833     /** 
17834     * @cfg {Boolean} grow 
17835     * @hide 
17836     */
17837     /** 
17838     * @cfg {Number} growMin 
17839     * @hide 
17840     */
17841     /** 
17842     * @cfg {Number} growMax 
17843     * @hide 
17844     */
17845     /**
17846      * @hide
17847      * @method autoSize
17848      */
17849 });
17850
17851 Roo.apply(Roo.bootstrap.ComboBox,  {
17852     
17853     header : {
17854         tag: 'div',
17855         cls: 'modal-header',
17856         cn: [
17857             {
17858                 tag: 'h4',
17859                 cls: 'modal-title'
17860             }
17861         ]
17862     },
17863     
17864     body : {
17865         tag: 'div',
17866         cls: 'modal-body',
17867         cn: [
17868             {
17869                 tag: 'ul',
17870                 cls: 'list-group'
17871             }
17872         ]
17873     },
17874     
17875     listItemRadio : {
17876         tag: 'li',
17877         cls: 'list-group-item',
17878         cn: [
17879             {
17880                 tag: 'span',
17881                 cls: 'roo-combobox-list-group-item-value'
17882             },
17883             {
17884                 tag: 'div',
17885                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17886                 cn: [
17887                     {
17888                         tag: 'input',
17889                         type: 'radio'
17890                     },
17891                     {
17892                         tag: 'label'
17893                     }
17894                 ]
17895             }
17896         ]
17897     },
17898     
17899     listItemCheckbox : {
17900         tag: 'li',
17901         cls: 'list-group-item',
17902         cn: [
17903             {
17904                 tag: 'span',
17905                 cls: 'roo-combobox-list-group-item-value'
17906             },
17907             {
17908                 tag: 'div',
17909                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17910                 cn: [
17911                     {
17912                         tag: 'input',
17913                         type: 'checkbox'
17914                     },
17915                     {
17916                         tag: 'label'
17917                     }
17918                 ]
17919             }
17920         ]
17921     },
17922     
17923     emptyResult : {
17924         tag: 'div',
17925         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17926     },
17927     
17928     footer : {
17929         tag: 'div',
17930         cls: 'modal-footer',
17931         cn: [
17932             {
17933                 tag: 'div',
17934                 cls: 'row',
17935                 cn: [
17936                     {
17937                         tag: 'div',
17938                         cls: 'col-xs-6 text-left',
17939                         cn: {
17940                             tag: 'button',
17941                             cls: 'btn btn-danger roo-touch-view-cancel',
17942                             html: 'Cancel'
17943                         }
17944                     },
17945                     {
17946                         tag: 'div',
17947                         cls: 'col-xs-6 text-right',
17948                         cn: {
17949                             tag: 'button',
17950                             cls: 'btn btn-success roo-touch-view-ok',
17951                             html: 'OK'
17952                         }
17953                     }
17954                 ]
17955             }
17956         ]
17957         
17958     }
17959 });
17960
17961 Roo.apply(Roo.bootstrap.ComboBox,  {
17962     
17963     touchViewTemplate : {
17964         tag: 'div',
17965         cls: 'modal fade roo-combobox-touch-view',
17966         cn: [
17967             {
17968                 tag: 'div',
17969                 cls: 'modal-dialog',
17970                 style : 'position:fixed', // we have to fix position....
17971                 cn: [
17972                     {
17973                         tag: 'div',
17974                         cls: 'modal-content',
17975                         cn: [
17976                             Roo.bootstrap.ComboBox.header,
17977                             Roo.bootstrap.ComboBox.body,
17978                             Roo.bootstrap.ComboBox.footer
17979                         ]
17980                     }
17981                 ]
17982             }
17983         ]
17984     }
17985 });/*
17986  * Based on:
17987  * Ext JS Library 1.1.1
17988  * Copyright(c) 2006-2007, Ext JS, LLC.
17989  *
17990  * Originally Released Under LGPL - original licence link has changed is not relivant.
17991  *
17992  * Fork - LGPL
17993  * <script type="text/javascript">
17994  */
17995
17996 /**
17997  * @class Roo.View
17998  * @extends Roo.util.Observable
17999  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18000  * This class also supports single and multi selection modes. <br>
18001  * Create a data model bound view:
18002  <pre><code>
18003  var store = new Roo.data.Store(...);
18004
18005  var view = new Roo.View({
18006     el : "my-element",
18007     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18008  
18009     singleSelect: true,
18010     selectedClass: "ydataview-selected",
18011     store: store
18012  });
18013
18014  // listen for node click?
18015  view.on("click", function(vw, index, node, e){
18016  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18017  });
18018
18019  // load XML data
18020  dataModel.load("foobar.xml");
18021  </code></pre>
18022  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18023  * <br><br>
18024  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18025  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18026  * 
18027  * Note: old style constructor is still suported (container, template, config)
18028  * 
18029  * @constructor
18030  * Create a new View
18031  * @param {Object} config The config object
18032  * 
18033  */
18034 Roo.View = function(config, depreciated_tpl, depreciated_config){
18035     
18036     this.parent = false;
18037     
18038     if (typeof(depreciated_tpl) == 'undefined') {
18039         // new way.. - universal constructor.
18040         Roo.apply(this, config);
18041         this.el  = Roo.get(this.el);
18042     } else {
18043         // old format..
18044         this.el  = Roo.get(config);
18045         this.tpl = depreciated_tpl;
18046         Roo.apply(this, depreciated_config);
18047     }
18048     this.wrapEl  = this.el.wrap().wrap();
18049     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18050     
18051     
18052     if(typeof(this.tpl) == "string"){
18053         this.tpl = new Roo.Template(this.tpl);
18054     } else {
18055         // support xtype ctors..
18056         this.tpl = new Roo.factory(this.tpl, Roo);
18057     }
18058     
18059     
18060     this.tpl.compile();
18061     
18062     /** @private */
18063     this.addEvents({
18064         /**
18065          * @event beforeclick
18066          * Fires before a click is processed. Returns false to cancel the default action.
18067          * @param {Roo.View} this
18068          * @param {Number} index The index of the target node
18069          * @param {HTMLElement} node The target node
18070          * @param {Roo.EventObject} e The raw event object
18071          */
18072             "beforeclick" : true,
18073         /**
18074          * @event click
18075          * Fires when a template node is clicked.
18076          * @param {Roo.View} this
18077          * @param {Number} index The index of the target node
18078          * @param {HTMLElement} node The target node
18079          * @param {Roo.EventObject} e The raw event object
18080          */
18081             "click" : true,
18082         /**
18083          * @event dblclick
18084          * Fires when a template node is double clicked.
18085          * @param {Roo.View} this
18086          * @param {Number} index The index of the target node
18087          * @param {HTMLElement} node The target node
18088          * @param {Roo.EventObject} e The raw event object
18089          */
18090             "dblclick" : true,
18091         /**
18092          * @event contextmenu
18093          * Fires when a template node is right clicked.
18094          * @param {Roo.View} this
18095          * @param {Number} index The index of the target node
18096          * @param {HTMLElement} node The target node
18097          * @param {Roo.EventObject} e The raw event object
18098          */
18099             "contextmenu" : true,
18100         /**
18101          * @event selectionchange
18102          * Fires when the selected nodes change.
18103          * @param {Roo.View} this
18104          * @param {Array} selections Array of the selected nodes
18105          */
18106             "selectionchange" : true,
18107     
18108         /**
18109          * @event beforeselect
18110          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18111          * @param {Roo.View} this
18112          * @param {HTMLElement} node The node to be selected
18113          * @param {Array} selections Array of currently selected nodes
18114          */
18115             "beforeselect" : true,
18116         /**
18117          * @event preparedata
18118          * Fires on every row to render, to allow you to change the data.
18119          * @param {Roo.View} this
18120          * @param {Object} data to be rendered (change this)
18121          */
18122           "preparedata" : true
18123           
18124           
18125         });
18126
18127
18128
18129     this.el.on({
18130         "click": this.onClick,
18131         "dblclick": this.onDblClick,
18132         "contextmenu": this.onContextMenu,
18133         scope:this
18134     });
18135
18136     this.selections = [];
18137     this.nodes = [];
18138     this.cmp = new Roo.CompositeElementLite([]);
18139     if(this.store){
18140         this.store = Roo.factory(this.store, Roo.data);
18141         this.setStore(this.store, true);
18142     }
18143     
18144     if ( this.footer && this.footer.xtype) {
18145            
18146          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18147         
18148         this.footer.dataSource = this.store;
18149         this.footer.container = fctr;
18150         this.footer = Roo.factory(this.footer, Roo);
18151         fctr.insertFirst(this.el);
18152         
18153         // this is a bit insane - as the paging toolbar seems to detach the el..
18154 //        dom.parentNode.parentNode.parentNode
18155          // they get detached?
18156     }
18157     
18158     
18159     Roo.View.superclass.constructor.call(this);
18160     
18161     
18162 };
18163
18164 Roo.extend(Roo.View, Roo.util.Observable, {
18165     
18166      /**
18167      * @cfg {Roo.data.Store} store Data store to load data from.
18168      */
18169     store : false,
18170     
18171     /**
18172      * @cfg {String|Roo.Element} el The container element.
18173      */
18174     el : '',
18175     
18176     /**
18177      * @cfg {String|Roo.Template} tpl The template used by this View 
18178      */
18179     tpl : false,
18180     /**
18181      * @cfg {String} dataName the named area of the template to use as the data area
18182      *                          Works with domtemplates roo-name="name"
18183      */
18184     dataName: false,
18185     /**
18186      * @cfg {String} selectedClass The css class to add to selected nodes
18187      */
18188     selectedClass : "x-view-selected",
18189      /**
18190      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18191      */
18192     emptyText : "",
18193     
18194     /**
18195      * @cfg {String} text to display on mask (default Loading)
18196      */
18197     mask : false,
18198     /**
18199      * @cfg {Boolean} multiSelect Allow multiple selection
18200      */
18201     multiSelect : false,
18202     /**
18203      * @cfg {Boolean} singleSelect Allow single selection
18204      */
18205     singleSelect:  false,
18206     
18207     /**
18208      * @cfg {Boolean} toggleSelect - selecting 
18209      */
18210     toggleSelect : false,
18211     
18212     /**
18213      * @cfg {Boolean} tickable - selecting 
18214      */
18215     tickable : false,
18216     
18217     /**
18218      * Returns the element this view is bound to.
18219      * @return {Roo.Element}
18220      */
18221     getEl : function(){
18222         return this.wrapEl;
18223     },
18224     
18225     
18226
18227     /**
18228      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18229      */
18230     refresh : function(){
18231         //Roo.log('refresh');
18232         var t = this.tpl;
18233         
18234         // if we are using something like 'domtemplate', then
18235         // the what gets used is:
18236         // t.applySubtemplate(NAME, data, wrapping data..)
18237         // the outer template then get' applied with
18238         //     the store 'extra data'
18239         // and the body get's added to the
18240         //      roo-name="data" node?
18241         //      <span class='roo-tpl-{name}'></span> ?????
18242         
18243         
18244         
18245         this.clearSelections();
18246         this.el.update("");
18247         var html = [];
18248         var records = this.store.getRange();
18249         if(records.length < 1) {
18250             
18251             // is this valid??  = should it render a template??
18252             
18253             this.el.update(this.emptyText);
18254             return;
18255         }
18256         var el = this.el;
18257         if (this.dataName) {
18258             this.el.update(t.apply(this.store.meta)); //????
18259             el = this.el.child('.roo-tpl-' + this.dataName);
18260         }
18261         
18262         for(var i = 0, len = records.length; i < len; i++){
18263             var data = this.prepareData(records[i].data, i, records[i]);
18264             this.fireEvent("preparedata", this, data, i, records[i]);
18265             
18266             var d = Roo.apply({}, data);
18267             
18268             if(this.tickable){
18269                 Roo.apply(d, {'roo-id' : Roo.id()});
18270                 
18271                 var _this = this;
18272             
18273                 Roo.each(this.parent.item, function(item){
18274                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18275                         return;
18276                     }
18277                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18278                 });
18279             }
18280             
18281             html[html.length] = Roo.util.Format.trim(
18282                 this.dataName ?
18283                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18284                     t.apply(d)
18285             );
18286         }
18287         
18288         
18289         
18290         el.update(html.join(""));
18291         this.nodes = el.dom.childNodes;
18292         this.updateIndexes(0);
18293     },
18294     
18295
18296     /**
18297      * Function to override to reformat the data that is sent to
18298      * the template for each node.
18299      * DEPRICATED - use the preparedata event handler.
18300      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18301      * a JSON object for an UpdateManager bound view).
18302      */
18303     prepareData : function(data, index, record)
18304     {
18305         this.fireEvent("preparedata", this, data, index, record);
18306         return data;
18307     },
18308
18309     onUpdate : function(ds, record){
18310         // Roo.log('on update');   
18311         this.clearSelections();
18312         var index = this.store.indexOf(record);
18313         var n = this.nodes[index];
18314         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18315         n.parentNode.removeChild(n);
18316         this.updateIndexes(index, index);
18317     },
18318
18319     
18320     
18321 // --------- FIXME     
18322     onAdd : function(ds, records, index)
18323     {
18324         //Roo.log(['on Add', ds, records, index] );        
18325         this.clearSelections();
18326         if(this.nodes.length == 0){
18327             this.refresh();
18328             return;
18329         }
18330         var n = this.nodes[index];
18331         for(var i = 0, len = records.length; i < len; i++){
18332             var d = this.prepareData(records[i].data, i, records[i]);
18333             if(n){
18334                 this.tpl.insertBefore(n, d);
18335             }else{
18336                 
18337                 this.tpl.append(this.el, d);
18338             }
18339         }
18340         this.updateIndexes(index);
18341     },
18342
18343     onRemove : function(ds, record, index){
18344        // Roo.log('onRemove');
18345         this.clearSelections();
18346         var el = this.dataName  ?
18347             this.el.child('.roo-tpl-' + this.dataName) :
18348             this.el; 
18349         
18350         el.dom.removeChild(this.nodes[index]);
18351         this.updateIndexes(index);
18352     },
18353
18354     /**
18355      * Refresh an individual node.
18356      * @param {Number} index
18357      */
18358     refreshNode : function(index){
18359         this.onUpdate(this.store, this.store.getAt(index));
18360     },
18361
18362     updateIndexes : function(startIndex, endIndex){
18363         var ns = this.nodes;
18364         startIndex = startIndex || 0;
18365         endIndex = endIndex || ns.length - 1;
18366         for(var i = startIndex; i <= endIndex; i++){
18367             ns[i].nodeIndex = i;
18368         }
18369     },
18370
18371     /**
18372      * Changes the data store this view uses and refresh the view.
18373      * @param {Store} store
18374      */
18375     setStore : function(store, initial){
18376         if(!initial && this.store){
18377             this.store.un("datachanged", this.refresh);
18378             this.store.un("add", this.onAdd);
18379             this.store.un("remove", this.onRemove);
18380             this.store.un("update", this.onUpdate);
18381             this.store.un("clear", this.refresh);
18382             this.store.un("beforeload", this.onBeforeLoad);
18383             this.store.un("load", this.onLoad);
18384             this.store.un("loadexception", this.onLoad);
18385         }
18386         if(store){
18387           
18388             store.on("datachanged", this.refresh, this);
18389             store.on("add", this.onAdd, this);
18390             store.on("remove", this.onRemove, this);
18391             store.on("update", this.onUpdate, this);
18392             store.on("clear", this.refresh, this);
18393             store.on("beforeload", this.onBeforeLoad, this);
18394             store.on("load", this.onLoad, this);
18395             store.on("loadexception", this.onLoad, this);
18396         }
18397         
18398         if(store){
18399             this.refresh();
18400         }
18401     },
18402     /**
18403      * onbeforeLoad - masks the loading area.
18404      *
18405      */
18406     onBeforeLoad : function(store,opts)
18407     {
18408          //Roo.log('onBeforeLoad');   
18409         if (!opts.add) {
18410             this.el.update("");
18411         }
18412         this.el.mask(this.mask ? this.mask : "Loading" ); 
18413     },
18414     onLoad : function ()
18415     {
18416         this.el.unmask();
18417     },
18418     
18419
18420     /**
18421      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18422      * @param {HTMLElement} node
18423      * @return {HTMLElement} The template node
18424      */
18425     findItemFromChild : function(node){
18426         var el = this.dataName  ?
18427             this.el.child('.roo-tpl-' + this.dataName,true) :
18428             this.el.dom; 
18429         
18430         if(!node || node.parentNode == el){
18431                     return node;
18432             }
18433             var p = node.parentNode;
18434             while(p && p != el){
18435             if(p.parentNode == el){
18436                 return p;
18437             }
18438             p = p.parentNode;
18439         }
18440             return null;
18441     },
18442
18443     /** @ignore */
18444     onClick : function(e){
18445         var item = this.findItemFromChild(e.getTarget());
18446         if(item){
18447             var index = this.indexOf(item);
18448             if(this.onItemClick(item, index, e) !== false){
18449                 this.fireEvent("click", this, index, item, e);
18450             }
18451         }else{
18452             this.clearSelections();
18453         }
18454     },
18455
18456     /** @ignore */
18457     onContextMenu : function(e){
18458         var item = this.findItemFromChild(e.getTarget());
18459         if(item){
18460             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18461         }
18462     },
18463
18464     /** @ignore */
18465     onDblClick : function(e){
18466         var item = this.findItemFromChild(e.getTarget());
18467         if(item){
18468             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18469         }
18470     },
18471
18472     onItemClick : function(item, index, e)
18473     {
18474         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18475             return false;
18476         }
18477         if (this.toggleSelect) {
18478             var m = this.isSelected(item) ? 'unselect' : 'select';
18479             //Roo.log(m);
18480             var _t = this;
18481             _t[m](item, true, false);
18482             return true;
18483         }
18484         if(this.multiSelect || this.singleSelect){
18485             if(this.multiSelect && e.shiftKey && this.lastSelection){
18486                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18487             }else{
18488                 this.select(item, this.multiSelect && e.ctrlKey);
18489                 this.lastSelection = item;
18490             }
18491             
18492             if(!this.tickable){
18493                 e.preventDefault();
18494             }
18495             
18496         }
18497         return true;
18498     },
18499
18500     /**
18501      * Get the number of selected nodes.
18502      * @return {Number}
18503      */
18504     getSelectionCount : function(){
18505         return this.selections.length;
18506     },
18507
18508     /**
18509      * Get the currently selected nodes.
18510      * @return {Array} An array of HTMLElements
18511      */
18512     getSelectedNodes : function(){
18513         return this.selections;
18514     },
18515
18516     /**
18517      * Get the indexes of the selected nodes.
18518      * @return {Array}
18519      */
18520     getSelectedIndexes : function(){
18521         var indexes = [], s = this.selections;
18522         for(var i = 0, len = s.length; i < len; i++){
18523             indexes.push(s[i].nodeIndex);
18524         }
18525         return indexes;
18526     },
18527
18528     /**
18529      * Clear all selections
18530      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18531      */
18532     clearSelections : function(suppressEvent){
18533         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18534             this.cmp.elements = this.selections;
18535             this.cmp.removeClass(this.selectedClass);
18536             this.selections = [];
18537             if(!suppressEvent){
18538                 this.fireEvent("selectionchange", this, this.selections);
18539             }
18540         }
18541     },
18542
18543     /**
18544      * Returns true if the passed node is selected
18545      * @param {HTMLElement/Number} node The node or node index
18546      * @return {Boolean}
18547      */
18548     isSelected : function(node){
18549         var s = this.selections;
18550         if(s.length < 1){
18551             return false;
18552         }
18553         node = this.getNode(node);
18554         return s.indexOf(node) !== -1;
18555     },
18556
18557     /**
18558      * Selects nodes.
18559      * @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
18560      * @param {Boolean} keepExisting (optional) true to keep existing selections
18561      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18562      */
18563     select : function(nodeInfo, keepExisting, suppressEvent){
18564         if(nodeInfo instanceof Array){
18565             if(!keepExisting){
18566                 this.clearSelections(true);
18567             }
18568             for(var i = 0, len = nodeInfo.length; i < len; i++){
18569                 this.select(nodeInfo[i], true, true);
18570             }
18571             return;
18572         } 
18573         var node = this.getNode(nodeInfo);
18574         if(!node || this.isSelected(node)){
18575             return; // already selected.
18576         }
18577         if(!keepExisting){
18578             this.clearSelections(true);
18579         }
18580         
18581         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18582             Roo.fly(node).addClass(this.selectedClass);
18583             this.selections.push(node);
18584             if(!suppressEvent){
18585                 this.fireEvent("selectionchange", this, this.selections);
18586             }
18587         }
18588         
18589         
18590     },
18591       /**
18592      * Unselects nodes.
18593      * @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
18594      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18595      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18596      */
18597     unselect : function(nodeInfo, keepExisting, suppressEvent)
18598     {
18599         if(nodeInfo instanceof Array){
18600             Roo.each(this.selections, function(s) {
18601                 this.unselect(s, nodeInfo);
18602             }, this);
18603             return;
18604         }
18605         var node = this.getNode(nodeInfo);
18606         if(!node || !this.isSelected(node)){
18607             //Roo.log("not selected");
18608             return; // not selected.
18609         }
18610         // fireevent???
18611         var ns = [];
18612         Roo.each(this.selections, function(s) {
18613             if (s == node ) {
18614                 Roo.fly(node).removeClass(this.selectedClass);
18615
18616                 return;
18617             }
18618             ns.push(s);
18619         },this);
18620         
18621         this.selections= ns;
18622         this.fireEvent("selectionchange", this, this.selections);
18623     },
18624
18625     /**
18626      * Gets a template node.
18627      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18628      * @return {HTMLElement} The node or null if it wasn't found
18629      */
18630     getNode : function(nodeInfo){
18631         if(typeof nodeInfo == "string"){
18632             return document.getElementById(nodeInfo);
18633         }else if(typeof nodeInfo == "number"){
18634             return this.nodes[nodeInfo];
18635         }
18636         return nodeInfo;
18637     },
18638
18639     /**
18640      * Gets a range template nodes.
18641      * @param {Number} startIndex
18642      * @param {Number} endIndex
18643      * @return {Array} An array of nodes
18644      */
18645     getNodes : function(start, end){
18646         var ns = this.nodes;
18647         start = start || 0;
18648         end = typeof end == "undefined" ? ns.length - 1 : end;
18649         var nodes = [];
18650         if(start <= end){
18651             for(var i = start; i <= end; i++){
18652                 nodes.push(ns[i]);
18653             }
18654         } else{
18655             for(var i = start; i >= end; i--){
18656                 nodes.push(ns[i]);
18657             }
18658         }
18659         return nodes;
18660     },
18661
18662     /**
18663      * Finds the index of the passed node
18664      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18665      * @return {Number} The index of the node or -1
18666      */
18667     indexOf : function(node){
18668         node = this.getNode(node);
18669         if(typeof node.nodeIndex == "number"){
18670             return node.nodeIndex;
18671         }
18672         var ns = this.nodes;
18673         for(var i = 0, len = ns.length; i < len; i++){
18674             if(ns[i] == node){
18675                 return i;
18676             }
18677         }
18678         return -1;
18679     }
18680 });
18681 /*
18682  * - LGPL
18683  *
18684  * based on jquery fullcalendar
18685  * 
18686  */
18687
18688 Roo.bootstrap = Roo.bootstrap || {};
18689 /**
18690  * @class Roo.bootstrap.Calendar
18691  * @extends Roo.bootstrap.Component
18692  * Bootstrap Calendar class
18693  * @cfg {Boolean} loadMask (true|false) default false
18694  * @cfg {Object} header generate the user specific header of the calendar, default false
18695
18696  * @constructor
18697  * Create a new Container
18698  * @param {Object} config The config object
18699  */
18700
18701
18702
18703 Roo.bootstrap.Calendar = function(config){
18704     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18705      this.addEvents({
18706         /**
18707              * @event select
18708              * Fires when a date is selected
18709              * @param {DatePicker} this
18710              * @param {Date} date The selected date
18711              */
18712         'select': true,
18713         /**
18714              * @event monthchange
18715              * Fires when the displayed month changes 
18716              * @param {DatePicker} this
18717              * @param {Date} date The selected month
18718              */
18719         'monthchange': true,
18720         /**
18721              * @event evententer
18722              * Fires when mouse over an event
18723              * @param {Calendar} this
18724              * @param {event} Event
18725              */
18726         'evententer': true,
18727         /**
18728              * @event eventleave
18729              * Fires when the mouse leaves an
18730              * @param {Calendar} this
18731              * @param {event}
18732              */
18733         'eventleave': true,
18734         /**
18735              * @event eventclick
18736              * Fires when the mouse click an
18737              * @param {Calendar} this
18738              * @param {event}
18739              */
18740         'eventclick': true
18741         
18742     });
18743
18744 };
18745
18746 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18747     
18748      /**
18749      * @cfg {Number} startDay
18750      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18751      */
18752     startDay : 0,
18753     
18754     loadMask : false,
18755     
18756     header : false,
18757       
18758     getAutoCreate : function(){
18759         
18760         
18761         var fc_button = function(name, corner, style, content ) {
18762             return Roo.apply({},{
18763                 tag : 'span',
18764                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18765                          (corner.length ?
18766                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18767                             ''
18768                         ),
18769                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18770                 unselectable: 'on'
18771             });
18772         };
18773         
18774         var header = {};
18775         
18776         if(!this.header){
18777             header = {
18778                 tag : 'table',
18779                 cls : 'fc-header',
18780                 style : 'width:100%',
18781                 cn : [
18782                     {
18783                         tag: 'tr',
18784                         cn : [
18785                             {
18786                                 tag : 'td',
18787                                 cls : 'fc-header-left',
18788                                 cn : [
18789                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18790                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18791                                     { tag: 'span', cls: 'fc-header-space' },
18792                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18793
18794
18795                                 ]
18796                             },
18797
18798                             {
18799                                 tag : 'td',
18800                                 cls : 'fc-header-center',
18801                                 cn : [
18802                                     {
18803                                         tag: 'span',
18804                                         cls: 'fc-header-title',
18805                                         cn : {
18806                                             tag: 'H2',
18807                                             html : 'month / year'
18808                                         }
18809                                     }
18810
18811                                 ]
18812                             },
18813                             {
18814                                 tag : 'td',
18815                                 cls : 'fc-header-right',
18816                                 cn : [
18817                               /*      fc_button('month', 'left', '', 'month' ),
18818                                     fc_button('week', '', '', 'week' ),
18819                                     fc_button('day', 'right', '', 'day' )
18820                                 */    
18821
18822                                 ]
18823                             }
18824
18825                         ]
18826                     }
18827                 ]
18828             };
18829         }
18830         
18831         header = this.header;
18832         
18833        
18834         var cal_heads = function() {
18835             var ret = [];
18836             // fixme - handle this.
18837             
18838             for (var i =0; i < Date.dayNames.length; i++) {
18839                 var d = Date.dayNames[i];
18840                 ret.push({
18841                     tag: 'th',
18842                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18843                     html : d.substring(0,3)
18844                 });
18845                 
18846             }
18847             ret[0].cls += ' fc-first';
18848             ret[6].cls += ' fc-last';
18849             return ret;
18850         };
18851         var cal_cell = function(n) {
18852             return  {
18853                 tag: 'td',
18854                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18855                 cn : [
18856                     {
18857                         cn : [
18858                             {
18859                                 cls: 'fc-day-number',
18860                                 html: 'D'
18861                             },
18862                             {
18863                                 cls: 'fc-day-content',
18864                              
18865                                 cn : [
18866                                      {
18867                                         style: 'position: relative;' // height: 17px;
18868                                     }
18869                                 ]
18870                             }
18871                             
18872                             
18873                         ]
18874                     }
18875                 ]
18876                 
18877             }
18878         };
18879         var cal_rows = function() {
18880             
18881             var ret = [];
18882             for (var r = 0; r < 6; r++) {
18883                 var row= {
18884                     tag : 'tr',
18885                     cls : 'fc-week',
18886                     cn : []
18887                 };
18888                 
18889                 for (var i =0; i < Date.dayNames.length; i++) {
18890                     var d = Date.dayNames[i];
18891                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18892
18893                 }
18894                 row.cn[0].cls+=' fc-first';
18895                 row.cn[0].cn[0].style = 'min-height:90px';
18896                 row.cn[6].cls+=' fc-last';
18897                 ret.push(row);
18898                 
18899             }
18900             ret[0].cls += ' fc-first';
18901             ret[4].cls += ' fc-prev-last';
18902             ret[5].cls += ' fc-last';
18903             return ret;
18904             
18905         };
18906         
18907         var cal_table = {
18908             tag: 'table',
18909             cls: 'fc-border-separate',
18910             style : 'width:100%',
18911             cellspacing  : 0,
18912             cn : [
18913                 { 
18914                     tag: 'thead',
18915                     cn : [
18916                         { 
18917                             tag: 'tr',
18918                             cls : 'fc-first fc-last',
18919                             cn : cal_heads()
18920                         }
18921                     ]
18922                 },
18923                 { 
18924                     tag: 'tbody',
18925                     cn : cal_rows()
18926                 }
18927                   
18928             ]
18929         };
18930          
18931          var cfg = {
18932             cls : 'fc fc-ltr',
18933             cn : [
18934                 header,
18935                 {
18936                     cls : 'fc-content',
18937                     style : "position: relative;",
18938                     cn : [
18939                         {
18940                             cls : 'fc-view fc-view-month fc-grid',
18941                             style : 'position: relative',
18942                             unselectable : 'on',
18943                             cn : [
18944                                 {
18945                                     cls : 'fc-event-container',
18946                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18947                                 },
18948                                 cal_table
18949                             ]
18950                         }
18951                     ]
18952     
18953                 }
18954            ] 
18955             
18956         };
18957         
18958          
18959         
18960         return cfg;
18961     },
18962     
18963     
18964     initEvents : function()
18965     {
18966         if(!this.store){
18967             throw "can not find store for calendar";
18968         }
18969         
18970         var mark = {
18971             tag: "div",
18972             cls:"x-dlg-mask",
18973             style: "text-align:center",
18974             cn: [
18975                 {
18976                     tag: "div",
18977                     style: "background-color:white;width:50%;margin:250 auto",
18978                     cn: [
18979                         {
18980                             tag: "img",
18981                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18982                         },
18983                         {
18984                             tag: "span",
18985                             html: "Loading"
18986                         }
18987                         
18988                     ]
18989                 }
18990             ]
18991         };
18992         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18993         
18994         var size = this.el.select('.fc-content', true).first().getSize();
18995         this.maskEl.setSize(size.width, size.height);
18996         this.maskEl.enableDisplayMode("block");
18997         if(!this.loadMask){
18998             this.maskEl.hide();
18999         }
19000         
19001         this.store = Roo.factory(this.store, Roo.data);
19002         this.store.on('load', this.onLoad, this);
19003         this.store.on('beforeload', this.onBeforeLoad, this);
19004         
19005         this.resize();
19006         
19007         this.cells = this.el.select('.fc-day',true);
19008         //Roo.log(this.cells);
19009         this.textNodes = this.el.query('.fc-day-number');
19010         this.cells.addClassOnOver('fc-state-hover');
19011         
19012         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19013         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19014         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19015         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19016         
19017         this.on('monthchange', this.onMonthChange, this);
19018         
19019         this.update(new Date().clearTime());
19020     },
19021     
19022     resize : function() {
19023         var sz  = this.el.getSize();
19024         
19025         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19026         this.el.select('.fc-day-content div',true).setHeight(34);
19027     },
19028     
19029     
19030     // private
19031     showPrevMonth : function(e){
19032         this.update(this.activeDate.add("mo", -1));
19033     },
19034     showToday : function(e){
19035         this.update(new Date().clearTime());
19036     },
19037     // private
19038     showNextMonth : function(e){
19039         this.update(this.activeDate.add("mo", 1));
19040     },
19041
19042     // private
19043     showPrevYear : function(){
19044         this.update(this.activeDate.add("y", -1));
19045     },
19046
19047     // private
19048     showNextYear : function(){
19049         this.update(this.activeDate.add("y", 1));
19050     },
19051
19052     
19053    // private
19054     update : function(date)
19055     {
19056         var vd = this.activeDate;
19057         this.activeDate = date;
19058 //        if(vd && this.el){
19059 //            var t = date.getTime();
19060 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19061 //                Roo.log('using add remove');
19062 //                
19063 //                this.fireEvent('monthchange', this, date);
19064 //                
19065 //                this.cells.removeClass("fc-state-highlight");
19066 //                this.cells.each(function(c){
19067 //                   if(c.dateValue == t){
19068 //                       c.addClass("fc-state-highlight");
19069 //                       setTimeout(function(){
19070 //                            try{c.dom.firstChild.focus();}catch(e){}
19071 //                       }, 50);
19072 //                       return false;
19073 //                   }
19074 //                   return true;
19075 //                });
19076 //                return;
19077 //            }
19078 //        }
19079         
19080         var days = date.getDaysInMonth();
19081         
19082         var firstOfMonth = date.getFirstDateOfMonth();
19083         var startingPos = firstOfMonth.getDay()-this.startDay;
19084         
19085         if(startingPos < this.startDay){
19086             startingPos += 7;
19087         }
19088         
19089         var pm = date.add(Date.MONTH, -1);
19090         var prevStart = pm.getDaysInMonth()-startingPos;
19091 //        
19092         this.cells = this.el.select('.fc-day',true);
19093         this.textNodes = this.el.query('.fc-day-number');
19094         this.cells.addClassOnOver('fc-state-hover');
19095         
19096         var cells = this.cells.elements;
19097         var textEls = this.textNodes;
19098         
19099         Roo.each(cells, function(cell){
19100             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19101         });
19102         
19103         days += startingPos;
19104
19105         // convert everything to numbers so it's fast
19106         var day = 86400000;
19107         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19108         //Roo.log(d);
19109         //Roo.log(pm);
19110         //Roo.log(prevStart);
19111         
19112         var today = new Date().clearTime().getTime();
19113         var sel = date.clearTime().getTime();
19114         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19115         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19116         var ddMatch = this.disabledDatesRE;
19117         var ddText = this.disabledDatesText;
19118         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19119         var ddaysText = this.disabledDaysText;
19120         var format = this.format;
19121         
19122         var setCellClass = function(cal, cell){
19123             cell.row = 0;
19124             cell.events = [];
19125             cell.more = [];
19126             //Roo.log('set Cell Class');
19127             cell.title = "";
19128             var t = d.getTime();
19129             
19130             //Roo.log(d);
19131             
19132             cell.dateValue = t;
19133             if(t == today){
19134                 cell.className += " fc-today";
19135                 cell.className += " fc-state-highlight";
19136                 cell.title = cal.todayText;
19137             }
19138             if(t == sel){
19139                 // disable highlight in other month..
19140                 //cell.className += " fc-state-highlight";
19141                 
19142             }
19143             // disabling
19144             if(t < min) {
19145                 cell.className = " fc-state-disabled";
19146                 cell.title = cal.minText;
19147                 return;
19148             }
19149             if(t > max) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.maxText;
19152                 return;
19153             }
19154             if(ddays){
19155                 if(ddays.indexOf(d.getDay()) != -1){
19156                     cell.title = ddaysText;
19157                     cell.className = " fc-state-disabled";
19158                 }
19159             }
19160             if(ddMatch && format){
19161                 var fvalue = d.dateFormat(format);
19162                 if(ddMatch.test(fvalue)){
19163                     cell.title = ddText.replace("%0", fvalue);
19164                     cell.className = " fc-state-disabled";
19165                 }
19166             }
19167             
19168             if (!cell.initialClassName) {
19169                 cell.initialClassName = cell.dom.className;
19170             }
19171             
19172             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19173         };
19174
19175         var i = 0;
19176         
19177         for(; i < startingPos; i++) {
19178             textEls[i].innerHTML = (++prevStart);
19179             d.setDate(d.getDate()+1);
19180             
19181             cells[i].className = "fc-past fc-other-month";
19182             setCellClass(this, cells[i]);
19183         }
19184         
19185         var intDay = 0;
19186         
19187         for(; i < days; i++){
19188             intDay = i - startingPos + 1;
19189             textEls[i].innerHTML = (intDay);
19190             d.setDate(d.getDate()+1);
19191             
19192             cells[i].className = ''; // "x-date-active";
19193             setCellClass(this, cells[i]);
19194         }
19195         var extraDays = 0;
19196         
19197         for(; i < 42; i++) {
19198             textEls[i].innerHTML = (++extraDays);
19199             d.setDate(d.getDate()+1);
19200             
19201             cells[i].className = "fc-future fc-other-month";
19202             setCellClass(this, cells[i]);
19203         }
19204         
19205         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19206         
19207         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19208         
19209         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19210         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19211         
19212         if(totalRows != 6){
19213             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19214             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19215         }
19216         
19217         this.fireEvent('monthchange', this, date);
19218         
19219         
19220         /*
19221         if(!this.internalRender){
19222             var main = this.el.dom.firstChild;
19223             var w = main.offsetWidth;
19224             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19225             Roo.fly(main).setWidth(w);
19226             this.internalRender = true;
19227             // opera does not respect the auto grow header center column
19228             // then, after it gets a width opera refuses to recalculate
19229             // without a second pass
19230             if(Roo.isOpera && !this.secondPass){
19231                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19232                 this.secondPass = true;
19233                 this.update.defer(10, this, [date]);
19234             }
19235         }
19236         */
19237         
19238     },
19239     
19240     findCell : function(dt) {
19241         dt = dt.clearTime().getTime();
19242         var ret = false;
19243         this.cells.each(function(c){
19244             //Roo.log("check " +c.dateValue + '?=' + dt);
19245             if(c.dateValue == dt){
19246                 ret = c;
19247                 return false;
19248             }
19249             return true;
19250         });
19251         
19252         return ret;
19253     },
19254     
19255     findCells : function(ev) {
19256         var s = ev.start.clone().clearTime().getTime();
19257        // Roo.log(s);
19258         var e= ev.end.clone().clearTime().getTime();
19259        // Roo.log(e);
19260         var ret = [];
19261         this.cells.each(function(c){
19262              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19263             
19264             if(c.dateValue > e){
19265                 return ;
19266             }
19267             if(c.dateValue < s){
19268                 return ;
19269             }
19270             ret.push(c);
19271         });
19272         
19273         return ret;    
19274     },
19275     
19276 //    findBestRow: function(cells)
19277 //    {
19278 //        var ret = 0;
19279 //        
19280 //        for (var i =0 ; i < cells.length;i++) {
19281 //            ret  = Math.max(cells[i].rows || 0,ret);
19282 //        }
19283 //        return ret;
19284 //        
19285 //    },
19286     
19287     
19288     addItem : function(ev)
19289     {
19290         // look for vertical location slot in
19291         var cells = this.findCells(ev);
19292         
19293 //        ev.row = this.findBestRow(cells);
19294         
19295         // work out the location.
19296         
19297         var crow = false;
19298         var rows = [];
19299         for(var i =0; i < cells.length; i++) {
19300             
19301             cells[i].row = cells[0].row;
19302             
19303             if(i == 0){
19304                 cells[i].row = cells[i].row + 1;
19305             }
19306             
19307             if (!crow) {
19308                 crow = {
19309                     start : cells[i],
19310                     end :  cells[i]
19311                 };
19312                 continue;
19313             }
19314             if (crow.start.getY() == cells[i].getY()) {
19315                 // on same row.
19316                 crow.end = cells[i];
19317                 continue;
19318             }
19319             // different row.
19320             rows.push(crow);
19321             crow = {
19322                 start: cells[i],
19323                 end : cells[i]
19324             };
19325             
19326         }
19327         
19328         rows.push(crow);
19329         ev.els = [];
19330         ev.rows = rows;
19331         ev.cells = cells;
19332         
19333         cells[0].events.push(ev);
19334         
19335         this.calevents.push(ev);
19336     },
19337     
19338     clearEvents: function() {
19339         
19340         if(!this.calevents){
19341             return;
19342         }
19343         
19344         Roo.each(this.cells.elements, function(c){
19345             c.row = 0;
19346             c.events = [];
19347             c.more = [];
19348         });
19349         
19350         Roo.each(this.calevents, function(e) {
19351             Roo.each(e.els, function(el) {
19352                 el.un('mouseenter' ,this.onEventEnter, this);
19353                 el.un('mouseleave' ,this.onEventLeave, this);
19354                 el.remove();
19355             },this);
19356         },this);
19357         
19358         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19359             e.remove();
19360         });
19361         
19362     },
19363     
19364     renderEvents: function()
19365     {   
19366         var _this = this;
19367         
19368         this.cells.each(function(c) {
19369             
19370             if(c.row < 5){
19371                 return;
19372             }
19373             
19374             var ev = c.events;
19375             
19376             var r = 4;
19377             if(c.row != c.events.length){
19378                 r = 4 - (4 - (c.row - c.events.length));
19379             }
19380             
19381             c.events = ev.slice(0, r);
19382             c.more = ev.slice(r);
19383             
19384             if(c.more.length && c.more.length == 1){
19385                 c.events.push(c.more.pop());
19386             }
19387             
19388             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19389             
19390         });
19391             
19392         this.cells.each(function(c) {
19393             
19394             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19395             
19396             
19397             for (var e = 0; e < c.events.length; e++){
19398                 var ev = c.events[e];
19399                 var rows = ev.rows;
19400                 
19401                 for(var i = 0; i < rows.length; i++) {
19402                 
19403                     // how many rows should it span..
19404
19405                     var  cfg = {
19406                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19407                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19408
19409                         unselectable : "on",
19410                         cn : [
19411                             {
19412                                 cls: 'fc-event-inner',
19413                                 cn : [
19414     //                                {
19415     //                                  tag:'span',
19416     //                                  cls: 'fc-event-time',
19417     //                                  html : cells.length > 1 ? '' : ev.time
19418     //                                },
19419                                     {
19420                                       tag:'span',
19421                                       cls: 'fc-event-title',
19422                                       html : String.format('{0}', ev.title)
19423                                     }
19424
19425
19426                                 ]
19427                             },
19428                             {
19429                                 cls: 'ui-resizable-handle ui-resizable-e',
19430                                 html : '&nbsp;&nbsp;&nbsp'
19431                             }
19432
19433                         ]
19434                     };
19435
19436                     if (i == 0) {
19437                         cfg.cls += ' fc-event-start';
19438                     }
19439                     if ((i+1) == rows.length) {
19440                         cfg.cls += ' fc-event-end';
19441                     }
19442
19443                     var ctr = _this.el.select('.fc-event-container',true).first();
19444                     var cg = ctr.createChild(cfg);
19445
19446                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19447                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19448
19449                     var r = (c.more.length) ? 1 : 0;
19450                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19451                     cg.setWidth(ebox.right - sbox.x -2);
19452
19453                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19454                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19455                     cg.on('click', _this.onEventClick, _this, ev);
19456
19457                     ev.els.push(cg);
19458                     
19459                 }
19460                 
19461             }
19462             
19463             
19464             if(c.more.length){
19465                 var  cfg = {
19466                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19467                     style : 'position: absolute',
19468                     unselectable : "on",
19469                     cn : [
19470                         {
19471                             cls: 'fc-event-inner',
19472                             cn : [
19473                                 {
19474                                   tag:'span',
19475                                   cls: 'fc-event-title',
19476                                   html : 'More'
19477                                 }
19478
19479
19480                             ]
19481                         },
19482                         {
19483                             cls: 'ui-resizable-handle ui-resizable-e',
19484                             html : '&nbsp;&nbsp;&nbsp'
19485                         }
19486
19487                     ]
19488                 };
19489
19490                 var ctr = _this.el.select('.fc-event-container',true).first();
19491                 var cg = ctr.createChild(cfg);
19492
19493                 var sbox = c.select('.fc-day-content',true).first().getBox();
19494                 var ebox = c.select('.fc-day-content',true).first().getBox();
19495                 //Roo.log(cg);
19496                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19497                 cg.setWidth(ebox.right - sbox.x -2);
19498
19499                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19500                 
19501             }
19502             
19503         });
19504         
19505         
19506         
19507     },
19508     
19509     onEventEnter: function (e, el,event,d) {
19510         this.fireEvent('evententer', this, el, event);
19511     },
19512     
19513     onEventLeave: function (e, el,event,d) {
19514         this.fireEvent('eventleave', this, el, event);
19515     },
19516     
19517     onEventClick: function (e, el,event,d) {
19518         this.fireEvent('eventclick', this, el, event);
19519     },
19520     
19521     onMonthChange: function () {
19522         this.store.load();
19523     },
19524     
19525     onMoreEventClick: function(e, el, more)
19526     {
19527         var _this = this;
19528         
19529         this.calpopover.placement = 'right';
19530         this.calpopover.setTitle('More');
19531         
19532         this.calpopover.setContent('');
19533         
19534         var ctr = this.calpopover.el.select('.popover-content', true).first();
19535         
19536         Roo.each(more, function(m){
19537             var cfg = {
19538                 cls : 'fc-event-hori fc-event-draggable',
19539                 html : m.title
19540             };
19541             var cg = ctr.createChild(cfg);
19542             
19543             cg.on('click', _this.onEventClick, _this, m);
19544         });
19545         
19546         this.calpopover.show(el);
19547         
19548         
19549     },
19550     
19551     onLoad: function () 
19552     {   
19553         this.calevents = [];
19554         var cal = this;
19555         
19556         if(this.store.getCount() > 0){
19557             this.store.data.each(function(d){
19558                cal.addItem({
19559                     id : d.data.id,
19560                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19561                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19562                     time : d.data.start_time,
19563                     title : d.data.title,
19564                     description : d.data.description,
19565                     venue : d.data.venue
19566                 });
19567             });
19568         }
19569         
19570         this.renderEvents();
19571         
19572         if(this.calevents.length && this.loadMask){
19573             this.maskEl.hide();
19574         }
19575     },
19576     
19577     onBeforeLoad: function()
19578     {
19579         this.clearEvents();
19580         if(this.loadMask){
19581             this.maskEl.show();
19582         }
19583     }
19584 });
19585
19586  
19587  /*
19588  * - LGPL
19589  *
19590  * element
19591  * 
19592  */
19593
19594 /**
19595  * @class Roo.bootstrap.Popover
19596  * @extends Roo.bootstrap.Component
19597  * Bootstrap Popover class
19598  * @cfg {String} html contents of the popover   (or false to use children..)
19599  * @cfg {String} title of popover (or false to hide)
19600  * @cfg {String} placement how it is placed
19601  * @cfg {String} trigger click || hover (or false to trigger manually)
19602  * @cfg {String} over what (parent or false to trigger manually.)
19603  * @cfg {Number} delay - delay before showing
19604  
19605  * @constructor
19606  * Create a new Popover
19607  * @param {Object} config The config object
19608  */
19609
19610 Roo.bootstrap.Popover = function(config){
19611     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19612     
19613     this.addEvents({
19614         // raw events
19615          /**
19616          * @event show
19617          * After the popover show
19618          * 
19619          * @param {Roo.bootstrap.Popover} this
19620          */
19621         "show" : true,
19622         /**
19623          * @event hide
19624          * After the popover hide
19625          * 
19626          * @param {Roo.bootstrap.Popover} this
19627          */
19628         "hide" : true
19629     });
19630 };
19631
19632 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19633     
19634     title: 'Fill in a title',
19635     html: false,
19636     
19637     placement : 'right',
19638     trigger : 'hover', // hover
19639     
19640     delay : 0,
19641     
19642     over: 'parent',
19643     
19644     can_build_overlaid : false,
19645     
19646     getChildContainer : function()
19647     {
19648         return this.el.select('.popover-content',true).first();
19649     },
19650     
19651     getAutoCreate : function(){
19652          
19653         var cfg = {
19654            cls : 'popover roo-dynamic',
19655            style: 'display:block',
19656            cn : [
19657                 {
19658                     cls : 'arrow'
19659                 },
19660                 {
19661                     cls : 'popover-inner',
19662                     cn : [
19663                         {
19664                             tag: 'h3',
19665                             cls: 'popover-title popover-header',
19666                             html : this.title
19667                         },
19668                         {
19669                             cls : 'popover-content popover-body',
19670                             html : this.html
19671                         }
19672                     ]
19673                     
19674                 }
19675            ]
19676         };
19677         
19678         return cfg;
19679     },
19680     setTitle: function(str)
19681     {
19682         this.title = str;
19683         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19684     },
19685     setContent: function(str)
19686     {
19687         this.html = str;
19688         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19689     },
19690     // as it get's added to the bottom of the page.
19691     onRender : function(ct, position)
19692     {
19693         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19694         if(!this.el){
19695             var cfg = Roo.apply({},  this.getAutoCreate());
19696             cfg.id = Roo.id();
19697             
19698             if (this.cls) {
19699                 cfg.cls += ' ' + this.cls;
19700             }
19701             if (this.style) {
19702                 cfg.style = this.style;
19703             }
19704             //Roo.log("adding to ");
19705             this.el = Roo.get(document.body).createChild(cfg, position);
19706 //            Roo.log(this.el);
19707         }
19708         this.initEvents();
19709     },
19710     
19711     initEvents : function()
19712     {
19713         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19714         this.el.enableDisplayMode('block');
19715         this.el.hide();
19716         if (this.over === false) {
19717             return; 
19718         }
19719         if (this.triggers === false) {
19720             return;
19721         }
19722         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19723         var triggers = this.trigger ? this.trigger.split(' ') : [];
19724         Roo.each(triggers, function(trigger) {
19725         
19726             if (trigger == 'click') {
19727                 on_el.on('click', this.toggle, this);
19728             } else if (trigger != 'manual') {
19729                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19730                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19731       
19732                 on_el.on(eventIn  ,this.enter, this);
19733                 on_el.on(eventOut, this.leave, this);
19734             }
19735         }, this);
19736         
19737     },
19738     
19739     
19740     // private
19741     timeout : null,
19742     hoverState : null,
19743     
19744     toggle : function () {
19745         this.hoverState == 'in' ? this.leave() : this.enter();
19746     },
19747     
19748     enter : function () {
19749         
19750         clearTimeout(this.timeout);
19751     
19752         this.hoverState = 'in';
19753     
19754         if (!this.delay || !this.delay.show) {
19755             this.show();
19756             return;
19757         }
19758         var _t = this;
19759         this.timeout = setTimeout(function () {
19760             if (_t.hoverState == 'in') {
19761                 _t.show();
19762             }
19763         }, this.delay.show)
19764     },
19765     
19766     leave : function() {
19767         clearTimeout(this.timeout);
19768     
19769         this.hoverState = 'out';
19770     
19771         if (!this.delay || !this.delay.hide) {
19772             this.hide();
19773             return;
19774         }
19775         var _t = this;
19776         this.timeout = setTimeout(function () {
19777             if (_t.hoverState == 'out') {
19778                 _t.hide();
19779             }
19780         }, this.delay.hide)
19781     },
19782     
19783     show : function (on_el)
19784     {
19785         if (!on_el) {
19786             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19787         }
19788         
19789         // set content.
19790         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19791         if (this.html !== false) {
19792             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19793         }
19794         this.el.removeClass([
19795             'fade','top','bottom', 'left', 'right','in',
19796             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19797         ]);
19798         if (!this.title.length) {
19799             this.el.select('.popover-title',true).hide();
19800         }
19801         
19802         var placement = typeof this.placement == 'function' ?
19803             this.placement.call(this, this.el, on_el) :
19804             this.placement;
19805             
19806         var autoToken = /\s?auto?\s?/i;
19807         var autoPlace = autoToken.test(placement);
19808         if (autoPlace) {
19809             placement = placement.replace(autoToken, '') || 'top';
19810         }
19811         
19812         //this.el.detach()
19813         //this.el.setXY([0,0]);
19814         this.el.show();
19815         this.el.dom.style.display='block';
19816         this.el.addClass(placement);
19817         
19818         //this.el.appendTo(on_el);
19819         
19820         var p = this.getPosition();
19821         var box = this.el.getBox();
19822         
19823         if (autoPlace) {
19824             // fixme..
19825         }
19826         var align = Roo.bootstrap.Popover.alignment[placement];
19827         
19828 //        Roo.log(align);
19829         this.el.alignTo(on_el, align[0],align[1]);
19830         //var arrow = this.el.select('.arrow',true).first();
19831         //arrow.set(align[2], 
19832         
19833         this.el.addClass('in');
19834         
19835         
19836         if (this.el.hasClass('fade')) {
19837             // fade it?
19838         }
19839         
19840         this.hoverState = 'in';
19841         
19842         this.fireEvent('show', this);
19843         
19844     },
19845     hide : function()
19846     {
19847         this.el.setXY([0,0]);
19848         this.el.removeClass('in');
19849         this.el.hide();
19850         this.hoverState = null;
19851         
19852         this.fireEvent('hide', this);
19853     }
19854     
19855 });
19856
19857 Roo.bootstrap.Popover.alignment = {
19858     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19859     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19860     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19861     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19862 };
19863
19864  /*
19865  * - LGPL
19866  *
19867  * Progress
19868  * 
19869  */
19870
19871 /**
19872  * @class Roo.bootstrap.Progress
19873  * @extends Roo.bootstrap.Component
19874  * Bootstrap Progress class
19875  * @cfg {Boolean} striped striped of the progress bar
19876  * @cfg {Boolean} active animated of the progress bar
19877  * 
19878  * 
19879  * @constructor
19880  * Create a new Progress
19881  * @param {Object} config The config object
19882  */
19883
19884 Roo.bootstrap.Progress = function(config){
19885     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19886 };
19887
19888 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19889     
19890     striped : false,
19891     active: false,
19892     
19893     getAutoCreate : function(){
19894         var cfg = {
19895             tag: 'div',
19896             cls: 'progress'
19897         };
19898         
19899         
19900         if(this.striped){
19901             cfg.cls += ' progress-striped';
19902         }
19903       
19904         if(this.active){
19905             cfg.cls += ' active';
19906         }
19907         
19908         
19909         return cfg;
19910     }
19911    
19912 });
19913
19914  
19915
19916  /*
19917  * - LGPL
19918  *
19919  * ProgressBar
19920  * 
19921  */
19922
19923 /**
19924  * @class Roo.bootstrap.ProgressBar
19925  * @extends Roo.bootstrap.Component
19926  * Bootstrap ProgressBar class
19927  * @cfg {Number} aria_valuenow aria-value now
19928  * @cfg {Number} aria_valuemin aria-value min
19929  * @cfg {Number} aria_valuemax aria-value max
19930  * @cfg {String} label label for the progress bar
19931  * @cfg {String} panel (success | info | warning | danger )
19932  * @cfg {String} role role of the progress bar
19933  * @cfg {String} sr_only text
19934  * 
19935  * 
19936  * @constructor
19937  * Create a new ProgressBar
19938  * @param {Object} config The config object
19939  */
19940
19941 Roo.bootstrap.ProgressBar = function(config){
19942     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19943 };
19944
19945 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19946     
19947     aria_valuenow : 0,
19948     aria_valuemin : 0,
19949     aria_valuemax : 100,
19950     label : false,
19951     panel : false,
19952     role : false,
19953     sr_only: false,
19954     
19955     getAutoCreate : function()
19956     {
19957         
19958         var cfg = {
19959             tag: 'div',
19960             cls: 'progress-bar',
19961             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19962         };
19963         
19964         if(this.sr_only){
19965             cfg.cn = {
19966                 tag: 'span',
19967                 cls: 'sr-only',
19968                 html: this.sr_only
19969             }
19970         }
19971         
19972         if(this.role){
19973             cfg.role = this.role;
19974         }
19975         
19976         if(this.aria_valuenow){
19977             cfg['aria-valuenow'] = this.aria_valuenow;
19978         }
19979         
19980         if(this.aria_valuemin){
19981             cfg['aria-valuemin'] = this.aria_valuemin;
19982         }
19983         
19984         if(this.aria_valuemax){
19985             cfg['aria-valuemax'] = this.aria_valuemax;
19986         }
19987         
19988         if(this.label && !this.sr_only){
19989             cfg.html = this.label;
19990         }
19991         
19992         if(this.panel){
19993             cfg.cls += ' progress-bar-' + this.panel;
19994         }
19995         
19996         return cfg;
19997     },
19998     
19999     update : function(aria_valuenow)
20000     {
20001         this.aria_valuenow = aria_valuenow;
20002         
20003         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20004     }
20005    
20006 });
20007
20008  
20009
20010  /*
20011  * - LGPL
20012  *
20013  * column
20014  * 
20015  */
20016
20017 /**
20018  * @class Roo.bootstrap.TabGroup
20019  * @extends Roo.bootstrap.Column
20020  * Bootstrap Column class
20021  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20022  * @cfg {Boolean} carousel true to make the group behave like a carousel
20023  * @cfg {Boolean} bullets show bullets for the panels
20024  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20025  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20026  * @cfg {Boolean} showarrow (true|false) show arrow default true
20027  * 
20028  * @constructor
20029  * Create a new TabGroup
20030  * @param {Object} config The config object
20031  */
20032
20033 Roo.bootstrap.TabGroup = function(config){
20034     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20035     if (!this.navId) {
20036         this.navId = Roo.id();
20037     }
20038     this.tabs = [];
20039     Roo.bootstrap.TabGroup.register(this);
20040     
20041 };
20042
20043 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20044     
20045     carousel : false,
20046     transition : false,
20047     bullets : 0,
20048     timer : 0,
20049     autoslide : false,
20050     slideFn : false,
20051     slideOnTouch : false,
20052     showarrow : true,
20053     
20054     getAutoCreate : function()
20055     {
20056         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20057         
20058         cfg.cls += ' tab-content';
20059         
20060         if (this.carousel) {
20061             cfg.cls += ' carousel slide';
20062             
20063             cfg.cn = [{
20064                cls : 'carousel-inner',
20065                cn : []
20066             }];
20067         
20068             if(this.bullets  && !Roo.isTouch){
20069                 
20070                 var bullets = {
20071                     cls : 'carousel-bullets',
20072                     cn : []
20073                 };
20074                
20075                 if(this.bullets_cls){
20076                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20077                 }
20078                 
20079                 bullets.cn.push({
20080                     cls : 'clear'
20081                 });
20082                 
20083                 cfg.cn[0].cn.push(bullets);
20084             }
20085             
20086             if(this.showarrow){
20087                 cfg.cn[0].cn.push({
20088                     tag : 'div',
20089                     class : 'carousel-arrow',
20090                     cn : [
20091                         {
20092                             tag : 'div',
20093                             class : 'carousel-prev',
20094                             cn : [
20095                                 {
20096                                     tag : 'i',
20097                                     class : 'fa fa-chevron-left'
20098                                 }
20099                             ]
20100                         },
20101                         {
20102                             tag : 'div',
20103                             class : 'carousel-next',
20104                             cn : [
20105                                 {
20106                                     tag : 'i',
20107                                     class : 'fa fa-chevron-right'
20108                                 }
20109                             ]
20110                         }
20111                     ]
20112                 });
20113             }
20114             
20115         }
20116         
20117         return cfg;
20118     },
20119     
20120     initEvents:  function()
20121     {
20122 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20123 //            this.el.on("touchstart", this.onTouchStart, this);
20124 //        }
20125         
20126         if(this.autoslide){
20127             var _this = this;
20128             
20129             this.slideFn = window.setInterval(function() {
20130                 _this.showPanelNext();
20131             }, this.timer);
20132         }
20133         
20134         if(this.showarrow){
20135             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20136             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20137         }
20138         
20139         
20140     },
20141     
20142 //    onTouchStart : function(e, el, o)
20143 //    {
20144 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20145 //            return;
20146 //        }
20147 //        
20148 //        this.showPanelNext();
20149 //    },
20150     
20151     
20152     getChildContainer : function()
20153     {
20154         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20155     },
20156     
20157     /**
20158     * register a Navigation item
20159     * @param {Roo.bootstrap.NavItem} the navitem to add
20160     */
20161     register : function(item)
20162     {
20163         this.tabs.push( item);
20164         item.navId = this.navId; // not really needed..
20165         this.addBullet();
20166     
20167     },
20168     
20169     getActivePanel : function()
20170     {
20171         var r = false;
20172         Roo.each(this.tabs, function(t) {
20173             if (t.active) {
20174                 r = t;
20175                 return false;
20176             }
20177             return null;
20178         });
20179         return r;
20180         
20181     },
20182     getPanelByName : function(n)
20183     {
20184         var r = false;
20185         Roo.each(this.tabs, function(t) {
20186             if (t.tabId == n) {
20187                 r = t;
20188                 return false;
20189             }
20190             return null;
20191         });
20192         return r;
20193     },
20194     indexOfPanel : function(p)
20195     {
20196         var r = false;
20197         Roo.each(this.tabs, function(t,i) {
20198             if (t.tabId == p.tabId) {
20199                 r = i;
20200                 return false;
20201             }
20202             return null;
20203         });
20204         return r;
20205     },
20206     /**
20207      * show a specific panel
20208      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20209      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20210      */
20211     showPanel : function (pan)
20212     {
20213         if(this.transition || typeof(pan) == 'undefined'){
20214             Roo.log("waiting for the transitionend");
20215             return false;
20216         }
20217         
20218         if (typeof(pan) == 'number') {
20219             pan = this.tabs[pan];
20220         }
20221         
20222         if (typeof(pan) == 'string') {
20223             pan = this.getPanelByName(pan);
20224         }
20225         
20226         var cur = this.getActivePanel();
20227         
20228         if(!pan || !cur){
20229             Roo.log('pan or acitve pan is undefined');
20230             return false;
20231         }
20232         
20233         if (pan.tabId == this.getActivePanel().tabId) {
20234             return true;
20235         }
20236         
20237         if (false === cur.fireEvent('beforedeactivate')) {
20238             return false;
20239         }
20240         
20241         if(this.bullets > 0 && !Roo.isTouch){
20242             this.setActiveBullet(this.indexOfPanel(pan));
20243         }
20244         
20245         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20246             
20247             //class="carousel-item carousel-item-next carousel-item-left"
20248             
20249             this.transition = true;
20250             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20251             var lr = dir == 'next' ? 'left' : 'right';
20252             pan.el.addClass(dir); // or prev
20253             pan.el.addClass('carousel-item-' + dir); // or prev
20254             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20255             cur.el.addClass(lr); // or right
20256             pan.el.addClass(lr);
20257             cur.el.addClass('carousel-item-' +lr); // or right
20258             pan.el.addClass('carousel-item-' +lr);
20259             
20260             
20261             var _this = this;
20262             cur.el.on('transitionend', function() {
20263                 Roo.log("trans end?");
20264                 
20265                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20266                 pan.setActive(true);
20267                 
20268                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20269                 cur.setActive(false);
20270                 
20271                 _this.transition = false;
20272                 
20273             }, this, { single:  true } );
20274             
20275             return true;
20276         }
20277         
20278         cur.setActive(false);
20279         pan.setActive(true);
20280         
20281         return true;
20282         
20283     },
20284     showPanelNext : function()
20285     {
20286         var i = this.indexOfPanel(this.getActivePanel());
20287         
20288         if (i >= this.tabs.length - 1 && !this.autoslide) {
20289             return;
20290         }
20291         
20292         if (i >= this.tabs.length - 1 && this.autoslide) {
20293             i = -1;
20294         }
20295         
20296         this.showPanel(this.tabs[i+1]);
20297     },
20298     
20299     showPanelPrev : function()
20300     {
20301         var i = this.indexOfPanel(this.getActivePanel());
20302         
20303         if (i  < 1 && !this.autoslide) {
20304             return;
20305         }
20306         
20307         if (i < 1 && this.autoslide) {
20308             i = this.tabs.length;
20309         }
20310         
20311         this.showPanel(this.tabs[i-1]);
20312     },
20313     
20314     
20315     addBullet: function()
20316     {
20317         if(!this.bullets || Roo.isTouch){
20318             return;
20319         }
20320         var ctr = this.el.select('.carousel-bullets',true).first();
20321         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20322         var bullet = ctr.createChild({
20323             cls : 'bullet bullet-' + i
20324         },ctr.dom.lastChild);
20325         
20326         
20327         var _this = this;
20328         
20329         bullet.on('click', (function(e, el, o, ii, t){
20330
20331             e.preventDefault();
20332
20333             this.showPanel(ii);
20334
20335             if(this.autoslide && this.slideFn){
20336                 clearInterval(this.slideFn);
20337                 this.slideFn = window.setInterval(function() {
20338                     _this.showPanelNext();
20339                 }, this.timer);
20340             }
20341
20342         }).createDelegate(this, [i, bullet], true));
20343                 
20344         
20345     },
20346      
20347     setActiveBullet : function(i)
20348     {
20349         if(Roo.isTouch){
20350             return;
20351         }
20352         
20353         Roo.each(this.el.select('.bullet', true).elements, function(el){
20354             el.removeClass('selected');
20355         });
20356
20357         var bullet = this.el.select('.bullet-' + i, true).first();
20358         
20359         if(!bullet){
20360             return;
20361         }
20362         
20363         bullet.addClass('selected');
20364     }
20365     
20366     
20367   
20368 });
20369
20370  
20371
20372  
20373  
20374 Roo.apply(Roo.bootstrap.TabGroup, {
20375     
20376     groups: {},
20377      /**
20378     * register a Navigation Group
20379     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20380     */
20381     register : function(navgrp)
20382     {
20383         this.groups[navgrp.navId] = navgrp;
20384         
20385     },
20386     /**
20387     * fetch a Navigation Group based on the navigation ID
20388     * if one does not exist , it will get created.
20389     * @param {string} the navgroup to add
20390     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20391     */
20392     get: function(navId) {
20393         if (typeof(this.groups[navId]) == 'undefined') {
20394             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20395         }
20396         return this.groups[navId] ;
20397     }
20398     
20399     
20400     
20401 });
20402
20403  /*
20404  * - LGPL
20405  *
20406  * TabPanel
20407  * 
20408  */
20409
20410 /**
20411  * @class Roo.bootstrap.TabPanel
20412  * @extends Roo.bootstrap.Component
20413  * Bootstrap TabPanel class
20414  * @cfg {Boolean} active panel active
20415  * @cfg {String} html panel content
20416  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20417  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20418  * @cfg {String} href click to link..
20419  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20420  * 
20421  * 
20422  * @constructor
20423  * Create a new TabPanel
20424  * @param {Object} config The config object
20425  */
20426
20427 Roo.bootstrap.TabPanel = function(config){
20428     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20429     this.addEvents({
20430         /**
20431              * @event changed
20432              * Fires when the active status changes
20433              * @param {Roo.bootstrap.TabPanel} this
20434              * @param {Boolean} state the new state
20435             
20436          */
20437         'changed': true,
20438         /**
20439              * @event beforedeactivate
20440              * Fires before a tab is de-activated - can be used to do validation on a form.
20441              * @param {Roo.bootstrap.TabPanel} this
20442              * @return {Boolean} false if there is an error
20443             
20444          */
20445         'beforedeactivate': true
20446      });
20447     
20448     this.tabId = this.tabId || Roo.id();
20449   
20450 };
20451
20452 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20453     
20454     active: false,
20455     html: false,
20456     tabId: false,
20457     navId : false,
20458     href : '',
20459     touchSlide : false,
20460     getAutoCreate : function(){
20461         
20462         
20463         var cfg = {
20464             tag: 'div',
20465             // item is needed for carousel - not sure if it has any effect otherwise
20466             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20467             html: this.html || ''
20468         };
20469         
20470         if(this.active){
20471             cfg.cls += ' active';
20472         }
20473         
20474         if(this.tabId){
20475             cfg.tabId = this.tabId;
20476         }
20477         
20478         
20479         
20480         return cfg;
20481     },
20482     
20483     initEvents:  function()
20484     {
20485         var p = this.parent();
20486         
20487         this.navId = this.navId || p.navId;
20488         
20489         if (typeof(this.navId) != 'undefined') {
20490             // not really needed.. but just in case.. parent should be a NavGroup.
20491             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20492             
20493             tg.register(this);
20494             
20495             var i = tg.tabs.length - 1;
20496             
20497             if(this.active && tg.bullets > 0 && i < tg.bullets){
20498                 tg.setActiveBullet(i);
20499             }
20500         }
20501         
20502         this.el.on('click', this.onClick, this);
20503         
20504         if(Roo.isTouch && this.touchSlide){
20505             this.el.on("touchstart", this.onTouchStart, this);
20506             this.el.on("touchmove", this.onTouchMove, this);
20507             this.el.on("touchend", this.onTouchEnd, this);
20508         }
20509         
20510     },
20511     
20512     onRender : function(ct, position)
20513     {
20514         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20515     },
20516     
20517     setActive : function(state)
20518     {
20519         Roo.log("panel - set active " + this.tabId + "=" + state);
20520         
20521         this.active = state;
20522         if (!state) {
20523             this.el.removeClass('active');
20524             
20525         } else  if (!this.el.hasClass('active')) {
20526             this.el.addClass('active');
20527         }
20528         
20529         this.fireEvent('changed', this, state);
20530     },
20531     
20532     onClick : function(e)
20533     {
20534         e.preventDefault();
20535         
20536         if(!this.href.length){
20537             return;
20538         }
20539         
20540         window.location.href = this.href;
20541     },
20542     
20543     startX : 0,
20544     startY : 0,
20545     endX : 0,
20546     endY : 0,
20547     swiping : false,
20548     
20549     onTouchStart : function(e)
20550     {
20551         this.swiping = false;
20552         
20553         this.startX = e.browserEvent.touches[0].clientX;
20554         this.startY = e.browserEvent.touches[0].clientY;
20555     },
20556     
20557     onTouchMove : function(e)
20558     {
20559         this.swiping = true;
20560         
20561         this.endX = e.browserEvent.touches[0].clientX;
20562         this.endY = e.browserEvent.touches[0].clientY;
20563     },
20564     
20565     onTouchEnd : function(e)
20566     {
20567         if(!this.swiping){
20568             this.onClick(e);
20569             return;
20570         }
20571         
20572         var tabGroup = this.parent();
20573         
20574         if(this.endX > this.startX){ // swiping right
20575             tabGroup.showPanelPrev();
20576             return;
20577         }
20578         
20579         if(this.startX > this.endX){ // swiping left
20580             tabGroup.showPanelNext();
20581             return;
20582         }
20583     }
20584     
20585     
20586 });
20587  
20588
20589  
20590
20591  /*
20592  * - LGPL
20593  *
20594  * DateField
20595  * 
20596  */
20597
20598 /**
20599  * @class Roo.bootstrap.DateField
20600  * @extends Roo.bootstrap.Input
20601  * Bootstrap DateField class
20602  * @cfg {Number} weekStart default 0
20603  * @cfg {String} viewMode default empty, (months|years)
20604  * @cfg {String} minViewMode default empty, (months|years)
20605  * @cfg {Number} startDate default -Infinity
20606  * @cfg {Number} endDate default Infinity
20607  * @cfg {Boolean} todayHighlight default false
20608  * @cfg {Boolean} todayBtn default false
20609  * @cfg {Boolean} calendarWeeks default false
20610  * @cfg {Object} daysOfWeekDisabled default empty
20611  * @cfg {Boolean} singleMode default false (true | false)
20612  * 
20613  * @cfg {Boolean} keyboardNavigation default true
20614  * @cfg {String} language default en
20615  * 
20616  * @constructor
20617  * Create a new DateField
20618  * @param {Object} config The config object
20619  */
20620
20621 Roo.bootstrap.DateField = function(config){
20622     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20623      this.addEvents({
20624             /**
20625              * @event show
20626              * Fires when this field show.
20627              * @param {Roo.bootstrap.DateField} this
20628              * @param {Mixed} date The date value
20629              */
20630             show : true,
20631             /**
20632              * @event show
20633              * Fires when this field hide.
20634              * @param {Roo.bootstrap.DateField} this
20635              * @param {Mixed} date The date value
20636              */
20637             hide : true,
20638             /**
20639              * @event select
20640              * Fires when select a date.
20641              * @param {Roo.bootstrap.DateField} this
20642              * @param {Mixed} date The date value
20643              */
20644             select : true,
20645             /**
20646              * @event beforeselect
20647              * Fires when before select a date.
20648              * @param {Roo.bootstrap.DateField} this
20649              * @param {Mixed} date The date value
20650              */
20651             beforeselect : true
20652         });
20653 };
20654
20655 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20656     
20657     /**
20658      * @cfg {String} format
20659      * The default date format string which can be overriden for localization support.  The format must be
20660      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20661      */
20662     format : "m/d/y",
20663     /**
20664      * @cfg {String} altFormats
20665      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20666      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20667      */
20668     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20669     
20670     weekStart : 0,
20671     
20672     viewMode : '',
20673     
20674     minViewMode : '',
20675     
20676     todayHighlight : false,
20677     
20678     todayBtn: false,
20679     
20680     language: 'en',
20681     
20682     keyboardNavigation: true,
20683     
20684     calendarWeeks: false,
20685     
20686     startDate: -Infinity,
20687     
20688     endDate: Infinity,
20689     
20690     daysOfWeekDisabled: [],
20691     
20692     _events: [],
20693     
20694     singleMode : false,
20695     
20696     UTCDate: function()
20697     {
20698         return new Date(Date.UTC.apply(Date, arguments));
20699     },
20700     
20701     UTCToday: function()
20702     {
20703         var today = new Date();
20704         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20705     },
20706     
20707     getDate: function() {
20708             var d = this.getUTCDate();
20709             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20710     },
20711     
20712     getUTCDate: function() {
20713             return this.date;
20714     },
20715     
20716     setDate: function(d) {
20717             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20718     },
20719     
20720     setUTCDate: function(d) {
20721             this.date = d;
20722             this.setValue(this.formatDate(this.date));
20723     },
20724         
20725     onRender: function(ct, position)
20726     {
20727         
20728         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20729         
20730         this.language = this.language || 'en';
20731         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20732         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20733         
20734         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20735         this.format = this.format || 'm/d/y';
20736         this.isInline = false;
20737         this.isInput = true;
20738         this.component = this.el.select('.add-on', true).first() || false;
20739         this.component = (this.component && this.component.length === 0) ? false : this.component;
20740         this.hasInput = this.component && this.inputEl().length;
20741         
20742         if (typeof(this.minViewMode === 'string')) {
20743             switch (this.minViewMode) {
20744                 case 'months':
20745                     this.minViewMode = 1;
20746                     break;
20747                 case 'years':
20748                     this.minViewMode = 2;
20749                     break;
20750                 default:
20751                     this.minViewMode = 0;
20752                     break;
20753             }
20754         }
20755         
20756         if (typeof(this.viewMode === 'string')) {
20757             switch (this.viewMode) {
20758                 case 'months':
20759                     this.viewMode = 1;
20760                     break;
20761                 case 'years':
20762                     this.viewMode = 2;
20763                     break;
20764                 default:
20765                     this.viewMode = 0;
20766                     break;
20767             }
20768         }
20769                 
20770         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20771         
20772 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20773         
20774         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20775         
20776         this.picker().on('mousedown', this.onMousedown, this);
20777         this.picker().on('click', this.onClick, this);
20778         
20779         this.picker().addClass('datepicker-dropdown');
20780         
20781         this.startViewMode = this.viewMode;
20782         
20783         if(this.singleMode){
20784             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20785                 v.setVisibilityMode(Roo.Element.DISPLAY);
20786                 v.hide();
20787             });
20788             
20789             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20790                 v.setStyle('width', '189px');
20791             });
20792         }
20793         
20794         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20795             if(!this.calendarWeeks){
20796                 v.remove();
20797                 return;
20798             }
20799             
20800             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20801             v.attr('colspan', function(i, val){
20802                 return parseInt(val) + 1;
20803             });
20804         });
20805                         
20806         
20807         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20808         
20809         this.setStartDate(this.startDate);
20810         this.setEndDate(this.endDate);
20811         
20812         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20813         
20814         this.fillDow();
20815         this.fillMonths();
20816         this.update();
20817         this.showMode();
20818         
20819         if(this.isInline) {
20820             this.showPopup();
20821         }
20822     },
20823     
20824     picker : function()
20825     {
20826         return this.pickerEl;
20827 //        return this.el.select('.datepicker', true).first();
20828     },
20829     
20830     fillDow: function()
20831     {
20832         var dowCnt = this.weekStart;
20833         
20834         var dow = {
20835             tag: 'tr',
20836             cn: [
20837                 
20838             ]
20839         };
20840         
20841         if(this.calendarWeeks){
20842             dow.cn.push({
20843                 tag: 'th',
20844                 cls: 'cw',
20845                 html: '&nbsp;'
20846             })
20847         }
20848         
20849         while (dowCnt < this.weekStart + 7) {
20850             dow.cn.push({
20851                 tag: 'th',
20852                 cls: 'dow',
20853                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20854             });
20855         }
20856         
20857         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20858     },
20859     
20860     fillMonths: function()
20861     {    
20862         var i = 0;
20863         var months = this.picker().select('>.datepicker-months td', true).first();
20864         
20865         months.dom.innerHTML = '';
20866         
20867         while (i < 12) {
20868             var month = {
20869                 tag: 'span',
20870                 cls: 'month',
20871                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20872             };
20873             
20874             months.createChild(month);
20875         }
20876         
20877     },
20878     
20879     update: function()
20880     {
20881         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;
20882         
20883         if (this.date < this.startDate) {
20884             this.viewDate = new Date(this.startDate);
20885         } else if (this.date > this.endDate) {
20886             this.viewDate = new Date(this.endDate);
20887         } else {
20888             this.viewDate = new Date(this.date);
20889         }
20890         
20891         this.fill();
20892     },
20893     
20894     fill: function() 
20895     {
20896         var d = new Date(this.viewDate),
20897                 year = d.getUTCFullYear(),
20898                 month = d.getUTCMonth(),
20899                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20900                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20901                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20902                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20903                 currentDate = this.date && this.date.valueOf(),
20904                 today = this.UTCToday();
20905         
20906         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20907         
20908 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20909         
20910 //        this.picker.select('>tfoot th.today').
20911 //                                              .text(dates[this.language].today)
20912 //                                              .toggle(this.todayBtn !== false);
20913     
20914         this.updateNavArrows();
20915         this.fillMonths();
20916                                                 
20917         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20918         
20919         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20920          
20921         prevMonth.setUTCDate(day);
20922         
20923         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20924         
20925         var nextMonth = new Date(prevMonth);
20926         
20927         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20928         
20929         nextMonth = nextMonth.valueOf();
20930         
20931         var fillMonths = false;
20932         
20933         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20934         
20935         while(prevMonth.valueOf() <= nextMonth) {
20936             var clsName = '';
20937             
20938             if (prevMonth.getUTCDay() === this.weekStart) {
20939                 if(fillMonths){
20940                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20941                 }
20942                     
20943                 fillMonths = {
20944                     tag: 'tr',
20945                     cn: []
20946                 };
20947                 
20948                 if(this.calendarWeeks){
20949                     // ISO 8601: First week contains first thursday.
20950                     // ISO also states week starts on Monday, but we can be more abstract here.
20951                     var
20952                     // Start of current week: based on weekstart/current date
20953                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20954                     // Thursday of this week
20955                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20956                     // First Thursday of year, year from thursday
20957                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20958                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20959                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20960                     
20961                     fillMonths.cn.push({
20962                         tag: 'td',
20963                         cls: 'cw',
20964                         html: calWeek
20965                     });
20966                 }
20967             }
20968             
20969             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20970                 clsName += ' old';
20971             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20972                 clsName += ' new';
20973             }
20974             if (this.todayHighlight &&
20975                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20976                 prevMonth.getUTCMonth() == today.getMonth() &&
20977                 prevMonth.getUTCDate() == today.getDate()) {
20978                 clsName += ' today';
20979             }
20980             
20981             if (currentDate && prevMonth.valueOf() === currentDate) {
20982                 clsName += ' active';
20983             }
20984             
20985             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20986                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20987                     clsName += ' disabled';
20988             }
20989             
20990             fillMonths.cn.push({
20991                 tag: 'td',
20992                 cls: 'day ' + clsName,
20993                 html: prevMonth.getDate()
20994             });
20995             
20996             prevMonth.setDate(prevMonth.getDate()+1);
20997         }
20998           
20999         var currentYear = this.date && this.date.getUTCFullYear();
21000         var currentMonth = this.date && this.date.getUTCMonth();
21001         
21002         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21003         
21004         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21005             v.removeClass('active');
21006             
21007             if(currentYear === year && k === currentMonth){
21008                 v.addClass('active');
21009             }
21010             
21011             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21012                 v.addClass('disabled');
21013             }
21014             
21015         });
21016         
21017         
21018         year = parseInt(year/10, 10) * 10;
21019         
21020         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21021         
21022         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21023         
21024         year -= 1;
21025         for (var i = -1; i < 11; i++) {
21026             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21027                 tag: 'span',
21028                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21029                 html: year
21030             });
21031             
21032             year += 1;
21033         }
21034     },
21035     
21036     showMode: function(dir) 
21037     {
21038         if (dir) {
21039             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21040         }
21041         
21042         Roo.each(this.picker().select('>div',true).elements, function(v){
21043             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21044             v.hide();
21045         });
21046         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21047     },
21048     
21049     place: function()
21050     {
21051         if(this.isInline) {
21052             return;
21053         }
21054         
21055         this.picker().removeClass(['bottom', 'top']);
21056         
21057         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21058             /*
21059              * place to the top of element!
21060              *
21061              */
21062             
21063             this.picker().addClass('top');
21064             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21065             
21066             return;
21067         }
21068         
21069         this.picker().addClass('bottom');
21070         
21071         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21072     },
21073     
21074     parseDate : function(value)
21075     {
21076         if(!value || value instanceof Date){
21077             return value;
21078         }
21079         var v = Date.parseDate(value, this.format);
21080         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21081             v = Date.parseDate(value, 'Y-m-d');
21082         }
21083         if(!v && this.altFormats){
21084             if(!this.altFormatsArray){
21085                 this.altFormatsArray = this.altFormats.split("|");
21086             }
21087             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21088                 v = Date.parseDate(value, this.altFormatsArray[i]);
21089             }
21090         }
21091         return v;
21092     },
21093     
21094     formatDate : function(date, fmt)
21095     {   
21096         return (!date || !(date instanceof Date)) ?
21097         date : date.dateFormat(fmt || this.format);
21098     },
21099     
21100     onFocus : function()
21101     {
21102         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21103         this.showPopup();
21104     },
21105     
21106     onBlur : function()
21107     {
21108         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21109         
21110         var d = this.inputEl().getValue();
21111         
21112         this.setValue(d);
21113                 
21114         this.hidePopup();
21115     },
21116     
21117     showPopup : function()
21118     {
21119         this.picker().show();
21120         this.update();
21121         this.place();
21122         
21123         this.fireEvent('showpopup', this, this.date);
21124     },
21125     
21126     hidePopup : function()
21127     {
21128         if(this.isInline) {
21129             return;
21130         }
21131         this.picker().hide();
21132         this.viewMode = this.startViewMode;
21133         this.showMode();
21134         
21135         this.fireEvent('hidepopup', this, this.date);
21136         
21137     },
21138     
21139     onMousedown: function(e)
21140     {
21141         e.stopPropagation();
21142         e.preventDefault();
21143     },
21144     
21145     keyup: function(e)
21146     {
21147         Roo.bootstrap.DateField.superclass.keyup.call(this);
21148         this.update();
21149     },
21150
21151     setValue: function(v)
21152     {
21153         if(this.fireEvent('beforeselect', this, v) !== false){
21154             var d = new Date(this.parseDate(v) ).clearTime();
21155         
21156             if(isNaN(d.getTime())){
21157                 this.date = this.viewDate = '';
21158                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21159                 return;
21160             }
21161
21162             v = this.formatDate(d);
21163
21164             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21165
21166             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21167
21168             this.update();
21169
21170             this.fireEvent('select', this, this.date);
21171         }
21172     },
21173     
21174     getValue: function()
21175     {
21176         return this.formatDate(this.date);
21177     },
21178     
21179     fireKey: function(e)
21180     {
21181         if (!this.picker().isVisible()){
21182             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21183                 this.showPopup();
21184             }
21185             return;
21186         }
21187         
21188         var dateChanged = false,
21189         dir, day, month,
21190         newDate, newViewDate;
21191         
21192         switch(e.keyCode){
21193             case 27: // escape
21194                 this.hidePopup();
21195                 e.preventDefault();
21196                 break;
21197             case 37: // left
21198             case 39: // right
21199                 if (!this.keyboardNavigation) {
21200                     break;
21201                 }
21202                 dir = e.keyCode == 37 ? -1 : 1;
21203                 
21204                 if (e.ctrlKey){
21205                     newDate = this.moveYear(this.date, dir);
21206                     newViewDate = this.moveYear(this.viewDate, dir);
21207                 } else if (e.shiftKey){
21208                     newDate = this.moveMonth(this.date, dir);
21209                     newViewDate = this.moveMonth(this.viewDate, dir);
21210                 } else {
21211                     newDate = new Date(this.date);
21212                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21213                     newViewDate = new Date(this.viewDate);
21214                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21215                 }
21216                 if (this.dateWithinRange(newDate)){
21217                     this.date = newDate;
21218                     this.viewDate = newViewDate;
21219                     this.setValue(this.formatDate(this.date));
21220 //                    this.update();
21221                     e.preventDefault();
21222                     dateChanged = true;
21223                 }
21224                 break;
21225             case 38: // up
21226             case 40: // down
21227                 if (!this.keyboardNavigation) {
21228                     break;
21229                 }
21230                 dir = e.keyCode == 38 ? -1 : 1;
21231                 if (e.ctrlKey){
21232                     newDate = this.moveYear(this.date, dir);
21233                     newViewDate = this.moveYear(this.viewDate, dir);
21234                 } else if (e.shiftKey){
21235                     newDate = this.moveMonth(this.date, dir);
21236                     newViewDate = this.moveMonth(this.viewDate, dir);
21237                 } else {
21238                     newDate = new Date(this.date);
21239                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21240                     newViewDate = new Date(this.viewDate);
21241                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21242                 }
21243                 if (this.dateWithinRange(newDate)){
21244                     this.date = newDate;
21245                     this.viewDate = newViewDate;
21246                     this.setValue(this.formatDate(this.date));
21247 //                    this.update();
21248                     e.preventDefault();
21249                     dateChanged = true;
21250                 }
21251                 break;
21252             case 13: // enter
21253                 this.setValue(this.formatDate(this.date));
21254                 this.hidePopup();
21255                 e.preventDefault();
21256                 break;
21257             case 9: // tab
21258                 this.setValue(this.formatDate(this.date));
21259                 this.hidePopup();
21260                 break;
21261             case 16: // shift
21262             case 17: // ctrl
21263             case 18: // alt
21264                 break;
21265             default :
21266                 this.hidePopup();
21267                 
21268         }
21269     },
21270     
21271     
21272     onClick: function(e) 
21273     {
21274         e.stopPropagation();
21275         e.preventDefault();
21276         
21277         var target = e.getTarget();
21278         
21279         if(target.nodeName.toLowerCase() === 'i'){
21280             target = Roo.get(target).dom.parentNode;
21281         }
21282         
21283         var nodeName = target.nodeName;
21284         var className = target.className;
21285         var html = target.innerHTML;
21286         //Roo.log(nodeName);
21287         
21288         switch(nodeName.toLowerCase()) {
21289             case 'th':
21290                 switch(className) {
21291                     case 'switch':
21292                         this.showMode(1);
21293                         break;
21294                     case 'prev':
21295                     case 'next':
21296                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21297                         switch(this.viewMode){
21298                                 case 0:
21299                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21300                                         break;
21301                                 case 1:
21302                                 case 2:
21303                                         this.viewDate = this.moveYear(this.viewDate, dir);
21304                                         break;
21305                         }
21306                         this.fill();
21307                         break;
21308                     case 'today':
21309                         var date = new Date();
21310                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21311 //                        this.fill()
21312                         this.setValue(this.formatDate(this.date));
21313                         
21314                         this.hidePopup();
21315                         break;
21316                 }
21317                 break;
21318             case 'span':
21319                 if (className.indexOf('disabled') < 0) {
21320                     this.viewDate.setUTCDate(1);
21321                     if (className.indexOf('month') > -1) {
21322                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21323                     } else {
21324                         var year = parseInt(html, 10) || 0;
21325                         this.viewDate.setUTCFullYear(year);
21326                         
21327                     }
21328                     
21329                     if(this.singleMode){
21330                         this.setValue(this.formatDate(this.viewDate));
21331                         this.hidePopup();
21332                         return;
21333                     }
21334                     
21335                     this.showMode(-1);
21336                     this.fill();
21337                 }
21338                 break;
21339                 
21340             case 'td':
21341                 //Roo.log(className);
21342                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21343                     var day = parseInt(html, 10) || 1;
21344                     var year = this.viewDate.getUTCFullYear(),
21345                         month = this.viewDate.getUTCMonth();
21346
21347                     if (className.indexOf('old') > -1) {
21348                         if(month === 0 ){
21349                             month = 11;
21350                             year -= 1;
21351                         }else{
21352                             month -= 1;
21353                         }
21354                     } else if (className.indexOf('new') > -1) {
21355                         if (month == 11) {
21356                             month = 0;
21357                             year += 1;
21358                         } else {
21359                             month += 1;
21360                         }
21361                     }
21362                     //Roo.log([year,month,day]);
21363                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21364                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21365 //                    this.fill();
21366                     //Roo.log(this.formatDate(this.date));
21367                     this.setValue(this.formatDate(this.date));
21368                     this.hidePopup();
21369                 }
21370                 break;
21371         }
21372     },
21373     
21374     setStartDate: function(startDate)
21375     {
21376         this.startDate = startDate || -Infinity;
21377         if (this.startDate !== -Infinity) {
21378             this.startDate = this.parseDate(this.startDate);
21379         }
21380         this.update();
21381         this.updateNavArrows();
21382     },
21383
21384     setEndDate: function(endDate)
21385     {
21386         this.endDate = endDate || Infinity;
21387         if (this.endDate !== Infinity) {
21388             this.endDate = this.parseDate(this.endDate);
21389         }
21390         this.update();
21391         this.updateNavArrows();
21392     },
21393     
21394     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21395     {
21396         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21397         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21398             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21399         }
21400         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21401             return parseInt(d, 10);
21402         });
21403         this.update();
21404         this.updateNavArrows();
21405     },
21406     
21407     updateNavArrows: function() 
21408     {
21409         if(this.singleMode){
21410             return;
21411         }
21412         
21413         var d = new Date(this.viewDate),
21414         year = d.getUTCFullYear(),
21415         month = d.getUTCMonth();
21416         
21417         Roo.each(this.picker().select('.prev', true).elements, function(v){
21418             v.show();
21419             switch (this.viewMode) {
21420                 case 0:
21421
21422                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21423                         v.hide();
21424                     }
21425                     break;
21426                 case 1:
21427                 case 2:
21428                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21429                         v.hide();
21430                     }
21431                     break;
21432             }
21433         });
21434         
21435         Roo.each(this.picker().select('.next', true).elements, function(v){
21436             v.show();
21437             switch (this.viewMode) {
21438                 case 0:
21439
21440                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21441                         v.hide();
21442                     }
21443                     break;
21444                 case 1:
21445                 case 2:
21446                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21447                         v.hide();
21448                     }
21449                     break;
21450             }
21451         })
21452     },
21453     
21454     moveMonth: function(date, dir)
21455     {
21456         if (!dir) {
21457             return date;
21458         }
21459         var new_date = new Date(date.valueOf()),
21460         day = new_date.getUTCDate(),
21461         month = new_date.getUTCMonth(),
21462         mag = Math.abs(dir),
21463         new_month, test;
21464         dir = dir > 0 ? 1 : -1;
21465         if (mag == 1){
21466             test = dir == -1
21467             // If going back one month, make sure month is not current month
21468             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21469             ? function(){
21470                 return new_date.getUTCMonth() == month;
21471             }
21472             // If going forward one month, make sure month is as expected
21473             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21474             : function(){
21475                 return new_date.getUTCMonth() != new_month;
21476             };
21477             new_month = month + dir;
21478             new_date.setUTCMonth(new_month);
21479             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21480             if (new_month < 0 || new_month > 11) {
21481                 new_month = (new_month + 12) % 12;
21482             }
21483         } else {
21484             // For magnitudes >1, move one month at a time...
21485             for (var i=0; i<mag; i++) {
21486                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21487                 new_date = this.moveMonth(new_date, dir);
21488             }
21489             // ...then reset the day, keeping it in the new month
21490             new_month = new_date.getUTCMonth();
21491             new_date.setUTCDate(day);
21492             test = function(){
21493                 return new_month != new_date.getUTCMonth();
21494             };
21495         }
21496         // Common date-resetting loop -- if date is beyond end of month, make it
21497         // end of month
21498         while (test()){
21499             new_date.setUTCDate(--day);
21500             new_date.setUTCMonth(new_month);
21501         }
21502         return new_date;
21503     },
21504
21505     moveYear: function(date, dir)
21506     {
21507         return this.moveMonth(date, dir*12);
21508     },
21509
21510     dateWithinRange: function(date)
21511     {
21512         return date >= this.startDate && date <= this.endDate;
21513     },
21514
21515     
21516     remove: function() 
21517     {
21518         this.picker().remove();
21519     },
21520     
21521     validateValue : function(value)
21522     {
21523         if(this.getVisibilityEl().hasClass('hidden')){
21524             return true;
21525         }
21526         
21527         if(value.length < 1)  {
21528             if(this.allowBlank){
21529                 return true;
21530             }
21531             return false;
21532         }
21533         
21534         if(value.length < this.minLength){
21535             return false;
21536         }
21537         if(value.length > this.maxLength){
21538             return false;
21539         }
21540         if(this.vtype){
21541             var vt = Roo.form.VTypes;
21542             if(!vt[this.vtype](value, this)){
21543                 return false;
21544             }
21545         }
21546         if(typeof this.validator == "function"){
21547             var msg = this.validator(value);
21548             if(msg !== true){
21549                 return false;
21550             }
21551         }
21552         
21553         if(this.regex && !this.regex.test(value)){
21554             return false;
21555         }
21556         
21557         if(typeof(this.parseDate(value)) == 'undefined'){
21558             return false;
21559         }
21560         
21561         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21562             return false;
21563         }      
21564         
21565         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21566             return false;
21567         } 
21568         
21569         
21570         return true;
21571     },
21572     
21573     reset : function()
21574     {
21575         this.date = this.viewDate = '';
21576         
21577         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21578     }
21579    
21580 });
21581
21582 Roo.apply(Roo.bootstrap.DateField,  {
21583     
21584     head : {
21585         tag: 'thead',
21586         cn: [
21587         {
21588             tag: 'tr',
21589             cn: [
21590             {
21591                 tag: 'th',
21592                 cls: 'prev',
21593                 html: '<i class="fa fa-arrow-left"/>'
21594             },
21595             {
21596                 tag: 'th',
21597                 cls: 'switch',
21598                 colspan: '5'
21599             },
21600             {
21601                 tag: 'th',
21602                 cls: 'next',
21603                 html: '<i class="fa fa-arrow-right"/>'
21604             }
21605
21606             ]
21607         }
21608         ]
21609     },
21610     
21611     content : {
21612         tag: 'tbody',
21613         cn: [
21614         {
21615             tag: 'tr',
21616             cn: [
21617             {
21618                 tag: 'td',
21619                 colspan: '7'
21620             }
21621             ]
21622         }
21623         ]
21624     },
21625     
21626     footer : {
21627         tag: 'tfoot',
21628         cn: [
21629         {
21630             tag: 'tr',
21631             cn: [
21632             {
21633                 tag: 'th',
21634                 colspan: '7',
21635                 cls: 'today'
21636             }
21637                     
21638             ]
21639         }
21640         ]
21641     },
21642     
21643     dates:{
21644         en: {
21645             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21646             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21647             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21648             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21649             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21650             today: "Today"
21651         }
21652     },
21653     
21654     modes: [
21655     {
21656         clsName: 'days',
21657         navFnc: 'Month',
21658         navStep: 1
21659     },
21660     {
21661         clsName: 'months',
21662         navFnc: 'FullYear',
21663         navStep: 1
21664     },
21665     {
21666         clsName: 'years',
21667         navFnc: 'FullYear',
21668         navStep: 10
21669     }]
21670 });
21671
21672 Roo.apply(Roo.bootstrap.DateField,  {
21673   
21674     template : {
21675         tag: 'div',
21676         cls: 'datepicker dropdown-menu roo-dynamic',
21677         cn: [
21678         {
21679             tag: 'div',
21680             cls: 'datepicker-days',
21681             cn: [
21682             {
21683                 tag: 'table',
21684                 cls: 'table-condensed',
21685                 cn:[
21686                 Roo.bootstrap.DateField.head,
21687                 {
21688                     tag: 'tbody'
21689                 },
21690                 Roo.bootstrap.DateField.footer
21691                 ]
21692             }
21693             ]
21694         },
21695         {
21696             tag: 'div',
21697             cls: 'datepicker-months',
21698             cn: [
21699             {
21700                 tag: 'table',
21701                 cls: 'table-condensed',
21702                 cn:[
21703                 Roo.bootstrap.DateField.head,
21704                 Roo.bootstrap.DateField.content,
21705                 Roo.bootstrap.DateField.footer
21706                 ]
21707             }
21708             ]
21709         },
21710         {
21711             tag: 'div',
21712             cls: 'datepicker-years',
21713             cn: [
21714             {
21715                 tag: 'table',
21716                 cls: 'table-condensed',
21717                 cn:[
21718                 Roo.bootstrap.DateField.head,
21719                 Roo.bootstrap.DateField.content,
21720                 Roo.bootstrap.DateField.footer
21721                 ]
21722             }
21723             ]
21724         }
21725         ]
21726     }
21727 });
21728
21729  
21730
21731  /*
21732  * - LGPL
21733  *
21734  * TimeField
21735  * 
21736  */
21737
21738 /**
21739  * @class Roo.bootstrap.TimeField
21740  * @extends Roo.bootstrap.Input
21741  * Bootstrap DateField class
21742  * 
21743  * 
21744  * @constructor
21745  * Create a new TimeField
21746  * @param {Object} config The config object
21747  */
21748
21749 Roo.bootstrap.TimeField = function(config){
21750     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21751     this.addEvents({
21752             /**
21753              * @event show
21754              * Fires when this field show.
21755              * @param {Roo.bootstrap.DateField} thisthis
21756              * @param {Mixed} date The date value
21757              */
21758             show : true,
21759             /**
21760              * @event show
21761              * Fires when this field hide.
21762              * @param {Roo.bootstrap.DateField} this
21763              * @param {Mixed} date The date value
21764              */
21765             hide : true,
21766             /**
21767              * @event select
21768              * Fires when select a date.
21769              * @param {Roo.bootstrap.DateField} this
21770              * @param {Mixed} date The date value
21771              */
21772             select : true
21773         });
21774 };
21775
21776 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21777     
21778     /**
21779      * @cfg {String} format
21780      * The default time format string which can be overriden for localization support.  The format must be
21781      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21782      */
21783     format : "H:i",
21784        
21785     onRender: function(ct, position)
21786     {
21787         
21788         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21789                 
21790         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21791         
21792         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21793         
21794         this.pop = this.picker().select('>.datepicker-time',true).first();
21795         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21796         
21797         this.picker().on('mousedown', this.onMousedown, this);
21798         this.picker().on('click', this.onClick, this);
21799         
21800         this.picker().addClass('datepicker-dropdown');
21801     
21802         this.fillTime();
21803         this.update();
21804             
21805         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21806         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21807         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21808         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21809         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21810         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21811
21812     },
21813     
21814     fireKey: function(e){
21815         if (!this.picker().isVisible()){
21816             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21817                 this.show();
21818             }
21819             return;
21820         }
21821
21822         e.preventDefault();
21823         
21824         switch(e.keyCode){
21825             case 27: // escape
21826                 this.hide();
21827                 break;
21828             case 37: // left
21829             case 39: // right
21830                 this.onTogglePeriod();
21831                 break;
21832             case 38: // up
21833                 this.onIncrementMinutes();
21834                 break;
21835             case 40: // down
21836                 this.onDecrementMinutes();
21837                 break;
21838             case 13: // enter
21839             case 9: // tab
21840                 this.setTime();
21841                 break;
21842         }
21843     },
21844     
21845     onClick: function(e) {
21846         e.stopPropagation();
21847         e.preventDefault();
21848     },
21849     
21850     picker : function()
21851     {
21852         return this.el.select('.datepicker', true).first();
21853     },
21854     
21855     fillTime: function()
21856     {    
21857         var time = this.pop.select('tbody', true).first();
21858         
21859         time.dom.innerHTML = '';
21860         
21861         time.createChild({
21862             tag: 'tr',
21863             cn: [
21864                 {
21865                     tag: 'td',
21866                     cn: [
21867                         {
21868                             tag: 'a',
21869                             href: '#',
21870                             cls: 'btn',
21871                             cn: [
21872                                 {
21873                                     tag: 'span',
21874                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21875                                 }
21876                             ]
21877                         } 
21878                     ]
21879                 },
21880                 {
21881                     tag: 'td',
21882                     cls: 'separator'
21883                 },
21884                 {
21885                     tag: 'td',
21886                     cn: [
21887                         {
21888                             tag: 'a',
21889                             href: '#',
21890                             cls: 'btn',
21891                             cn: [
21892                                 {
21893                                     tag: 'span',
21894                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21895                                 }
21896                             ]
21897                         }
21898                     ]
21899                 },
21900                 {
21901                     tag: 'td',
21902                     cls: 'separator'
21903                 }
21904             ]
21905         });
21906         
21907         time.createChild({
21908             tag: 'tr',
21909             cn: [
21910                 {
21911                     tag: 'td',
21912                     cn: [
21913                         {
21914                             tag: 'span',
21915                             cls: 'timepicker-hour',
21916                             html: '00'
21917                         }  
21918                     ]
21919                 },
21920                 {
21921                     tag: 'td',
21922                     cls: 'separator',
21923                     html: ':'
21924                 },
21925                 {
21926                     tag: 'td',
21927                     cn: [
21928                         {
21929                             tag: 'span',
21930                             cls: 'timepicker-minute',
21931                             html: '00'
21932                         }  
21933                     ]
21934                 },
21935                 {
21936                     tag: 'td',
21937                     cls: 'separator'
21938                 },
21939                 {
21940                     tag: 'td',
21941                     cn: [
21942                         {
21943                             tag: 'button',
21944                             type: 'button',
21945                             cls: 'btn btn-primary period',
21946                             html: 'AM'
21947                             
21948                         }
21949                     ]
21950                 }
21951             ]
21952         });
21953         
21954         time.createChild({
21955             tag: 'tr',
21956             cn: [
21957                 {
21958                     tag: 'td',
21959                     cn: [
21960                         {
21961                             tag: 'a',
21962                             href: '#',
21963                             cls: 'btn',
21964                             cn: [
21965                                 {
21966                                     tag: 'span',
21967                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21968                                 }
21969                             ]
21970                         }
21971                     ]
21972                 },
21973                 {
21974                     tag: 'td',
21975                     cls: 'separator'
21976                 },
21977                 {
21978                     tag: 'td',
21979                     cn: [
21980                         {
21981                             tag: 'a',
21982                             href: '#',
21983                             cls: 'btn',
21984                             cn: [
21985                                 {
21986                                     tag: 'span',
21987                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21988                                 }
21989                             ]
21990                         }
21991                     ]
21992                 },
21993                 {
21994                     tag: 'td',
21995                     cls: 'separator'
21996                 }
21997             ]
21998         });
21999         
22000     },
22001     
22002     update: function()
22003     {
22004         
22005         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22006         
22007         this.fill();
22008     },
22009     
22010     fill: function() 
22011     {
22012         var hours = this.time.getHours();
22013         var minutes = this.time.getMinutes();
22014         var period = 'AM';
22015         
22016         if(hours > 11){
22017             period = 'PM';
22018         }
22019         
22020         if(hours == 0){
22021             hours = 12;
22022         }
22023         
22024         
22025         if(hours > 12){
22026             hours = hours - 12;
22027         }
22028         
22029         if(hours < 10){
22030             hours = '0' + hours;
22031         }
22032         
22033         if(minutes < 10){
22034             minutes = '0' + minutes;
22035         }
22036         
22037         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22038         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22039         this.pop.select('button', true).first().dom.innerHTML = period;
22040         
22041     },
22042     
22043     place: function()
22044     {   
22045         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22046         
22047         var cls = ['bottom'];
22048         
22049         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22050             cls.pop();
22051             cls.push('top');
22052         }
22053         
22054         cls.push('right');
22055         
22056         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22057             cls.pop();
22058             cls.push('left');
22059         }
22060         
22061         this.picker().addClass(cls.join('-'));
22062         
22063         var _this = this;
22064         
22065         Roo.each(cls, function(c){
22066             if(c == 'bottom'){
22067                 _this.picker().setTop(_this.inputEl().getHeight());
22068                 return;
22069             }
22070             if(c == 'top'){
22071                 _this.picker().setTop(0 - _this.picker().getHeight());
22072                 return;
22073             }
22074             
22075             if(c == 'left'){
22076                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22077                 return;
22078             }
22079             if(c == 'right'){
22080                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22081                 return;
22082             }
22083         });
22084         
22085     },
22086   
22087     onFocus : function()
22088     {
22089         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22090         this.show();
22091     },
22092     
22093     onBlur : function()
22094     {
22095         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22096         this.hide();
22097     },
22098     
22099     show : function()
22100     {
22101         this.picker().show();
22102         this.pop.show();
22103         this.update();
22104         this.place();
22105         
22106         this.fireEvent('show', this, this.date);
22107     },
22108     
22109     hide : function()
22110     {
22111         this.picker().hide();
22112         this.pop.hide();
22113         
22114         this.fireEvent('hide', this, this.date);
22115     },
22116     
22117     setTime : function()
22118     {
22119         this.hide();
22120         this.setValue(this.time.format(this.format));
22121         
22122         this.fireEvent('select', this, this.date);
22123         
22124         
22125     },
22126     
22127     onMousedown: function(e){
22128         e.stopPropagation();
22129         e.preventDefault();
22130     },
22131     
22132     onIncrementHours: function()
22133     {
22134         Roo.log('onIncrementHours');
22135         this.time = this.time.add(Date.HOUR, 1);
22136         this.update();
22137         
22138     },
22139     
22140     onDecrementHours: function()
22141     {
22142         Roo.log('onDecrementHours');
22143         this.time = this.time.add(Date.HOUR, -1);
22144         this.update();
22145     },
22146     
22147     onIncrementMinutes: function()
22148     {
22149         Roo.log('onIncrementMinutes');
22150         this.time = this.time.add(Date.MINUTE, 1);
22151         this.update();
22152     },
22153     
22154     onDecrementMinutes: function()
22155     {
22156         Roo.log('onDecrementMinutes');
22157         this.time = this.time.add(Date.MINUTE, -1);
22158         this.update();
22159     },
22160     
22161     onTogglePeriod: function()
22162     {
22163         Roo.log('onTogglePeriod');
22164         this.time = this.time.add(Date.HOUR, 12);
22165         this.update();
22166     }
22167     
22168    
22169 });
22170
22171 Roo.apply(Roo.bootstrap.TimeField,  {
22172     
22173     content : {
22174         tag: 'tbody',
22175         cn: [
22176             {
22177                 tag: 'tr',
22178                 cn: [
22179                 {
22180                     tag: 'td',
22181                     colspan: '7'
22182                 }
22183                 ]
22184             }
22185         ]
22186     },
22187     
22188     footer : {
22189         tag: 'tfoot',
22190         cn: [
22191             {
22192                 tag: 'tr',
22193                 cn: [
22194                 {
22195                     tag: 'th',
22196                     colspan: '7',
22197                     cls: '',
22198                     cn: [
22199                         {
22200                             tag: 'button',
22201                             cls: 'btn btn-info ok',
22202                             html: 'OK'
22203                         }
22204                     ]
22205                 }
22206
22207                 ]
22208             }
22209         ]
22210     }
22211 });
22212
22213 Roo.apply(Roo.bootstrap.TimeField,  {
22214   
22215     template : {
22216         tag: 'div',
22217         cls: 'datepicker dropdown-menu',
22218         cn: [
22219             {
22220                 tag: 'div',
22221                 cls: 'datepicker-time',
22222                 cn: [
22223                 {
22224                     tag: 'table',
22225                     cls: 'table-condensed',
22226                     cn:[
22227                     Roo.bootstrap.TimeField.content,
22228                     Roo.bootstrap.TimeField.footer
22229                     ]
22230                 }
22231                 ]
22232             }
22233         ]
22234     }
22235 });
22236
22237  
22238
22239  /*
22240  * - LGPL
22241  *
22242  * MonthField
22243  * 
22244  */
22245
22246 /**
22247  * @class Roo.bootstrap.MonthField
22248  * @extends Roo.bootstrap.Input
22249  * Bootstrap MonthField class
22250  * 
22251  * @cfg {String} language default en
22252  * 
22253  * @constructor
22254  * Create a new MonthField
22255  * @param {Object} config The config object
22256  */
22257
22258 Roo.bootstrap.MonthField = function(config){
22259     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22260     
22261     this.addEvents({
22262         /**
22263          * @event show
22264          * Fires when this field show.
22265          * @param {Roo.bootstrap.MonthField} this
22266          * @param {Mixed} date The date value
22267          */
22268         show : true,
22269         /**
22270          * @event show
22271          * Fires when this field hide.
22272          * @param {Roo.bootstrap.MonthField} this
22273          * @param {Mixed} date The date value
22274          */
22275         hide : true,
22276         /**
22277          * @event select
22278          * Fires when select a date.
22279          * @param {Roo.bootstrap.MonthField} this
22280          * @param {String} oldvalue The old value
22281          * @param {String} newvalue The new value
22282          */
22283         select : true
22284     });
22285 };
22286
22287 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22288     
22289     onRender: function(ct, position)
22290     {
22291         
22292         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22293         
22294         this.language = this.language || 'en';
22295         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22296         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22297         
22298         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22299         this.isInline = false;
22300         this.isInput = true;
22301         this.component = this.el.select('.add-on', true).first() || false;
22302         this.component = (this.component && this.component.length === 0) ? false : this.component;
22303         this.hasInput = this.component && this.inputEL().length;
22304         
22305         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22306         
22307         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22308         
22309         this.picker().on('mousedown', this.onMousedown, this);
22310         this.picker().on('click', this.onClick, this);
22311         
22312         this.picker().addClass('datepicker-dropdown');
22313         
22314         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22315             v.setStyle('width', '189px');
22316         });
22317         
22318         this.fillMonths();
22319         
22320         this.update();
22321         
22322         if(this.isInline) {
22323             this.show();
22324         }
22325         
22326     },
22327     
22328     setValue: function(v, suppressEvent)
22329     {   
22330         var o = this.getValue();
22331         
22332         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22333         
22334         this.update();
22335
22336         if(suppressEvent !== true){
22337             this.fireEvent('select', this, o, v);
22338         }
22339         
22340     },
22341     
22342     getValue: function()
22343     {
22344         return this.value;
22345     },
22346     
22347     onClick: function(e) 
22348     {
22349         e.stopPropagation();
22350         e.preventDefault();
22351         
22352         var target = e.getTarget();
22353         
22354         if(target.nodeName.toLowerCase() === 'i'){
22355             target = Roo.get(target).dom.parentNode;
22356         }
22357         
22358         var nodeName = target.nodeName;
22359         var className = target.className;
22360         var html = target.innerHTML;
22361         
22362         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22363             return;
22364         }
22365         
22366         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22367         
22368         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22369         
22370         this.hide();
22371                         
22372     },
22373     
22374     picker : function()
22375     {
22376         return this.pickerEl;
22377     },
22378     
22379     fillMonths: function()
22380     {    
22381         var i = 0;
22382         var months = this.picker().select('>.datepicker-months td', true).first();
22383         
22384         months.dom.innerHTML = '';
22385         
22386         while (i < 12) {
22387             var month = {
22388                 tag: 'span',
22389                 cls: 'month',
22390                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22391             };
22392             
22393             months.createChild(month);
22394         }
22395         
22396     },
22397     
22398     update: function()
22399     {
22400         var _this = this;
22401         
22402         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22403             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22404         }
22405         
22406         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22407             e.removeClass('active');
22408             
22409             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22410                 e.addClass('active');
22411             }
22412         })
22413     },
22414     
22415     place: function()
22416     {
22417         if(this.isInline) {
22418             return;
22419         }
22420         
22421         this.picker().removeClass(['bottom', 'top']);
22422         
22423         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22424             /*
22425              * place to the top of element!
22426              *
22427              */
22428             
22429             this.picker().addClass('top');
22430             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22431             
22432             return;
22433         }
22434         
22435         this.picker().addClass('bottom');
22436         
22437         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22438     },
22439     
22440     onFocus : function()
22441     {
22442         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22443         this.show();
22444     },
22445     
22446     onBlur : function()
22447     {
22448         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22449         
22450         var d = this.inputEl().getValue();
22451         
22452         this.setValue(d);
22453                 
22454         this.hide();
22455     },
22456     
22457     show : function()
22458     {
22459         this.picker().show();
22460         this.picker().select('>.datepicker-months', true).first().show();
22461         this.update();
22462         this.place();
22463         
22464         this.fireEvent('show', this, this.date);
22465     },
22466     
22467     hide : function()
22468     {
22469         if(this.isInline) {
22470             return;
22471         }
22472         this.picker().hide();
22473         this.fireEvent('hide', this, this.date);
22474         
22475     },
22476     
22477     onMousedown: function(e)
22478     {
22479         e.stopPropagation();
22480         e.preventDefault();
22481     },
22482     
22483     keyup: function(e)
22484     {
22485         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22486         this.update();
22487     },
22488
22489     fireKey: function(e)
22490     {
22491         if (!this.picker().isVisible()){
22492             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22493                 this.show();
22494             }
22495             return;
22496         }
22497         
22498         var dir;
22499         
22500         switch(e.keyCode){
22501             case 27: // escape
22502                 this.hide();
22503                 e.preventDefault();
22504                 break;
22505             case 37: // left
22506             case 39: // right
22507                 dir = e.keyCode == 37 ? -1 : 1;
22508                 
22509                 this.vIndex = this.vIndex + dir;
22510                 
22511                 if(this.vIndex < 0){
22512                     this.vIndex = 0;
22513                 }
22514                 
22515                 if(this.vIndex > 11){
22516                     this.vIndex = 11;
22517                 }
22518                 
22519                 if(isNaN(this.vIndex)){
22520                     this.vIndex = 0;
22521                 }
22522                 
22523                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22524                 
22525                 break;
22526             case 38: // up
22527             case 40: // down
22528                 
22529                 dir = e.keyCode == 38 ? -1 : 1;
22530                 
22531                 this.vIndex = this.vIndex + dir * 4;
22532                 
22533                 if(this.vIndex < 0){
22534                     this.vIndex = 0;
22535                 }
22536                 
22537                 if(this.vIndex > 11){
22538                     this.vIndex = 11;
22539                 }
22540                 
22541                 if(isNaN(this.vIndex)){
22542                     this.vIndex = 0;
22543                 }
22544                 
22545                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22546                 break;
22547                 
22548             case 13: // enter
22549                 
22550                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22551                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22552                 }
22553                 
22554                 this.hide();
22555                 e.preventDefault();
22556                 break;
22557             case 9: // tab
22558                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22559                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22560                 }
22561                 this.hide();
22562                 break;
22563             case 16: // shift
22564             case 17: // ctrl
22565             case 18: // alt
22566                 break;
22567             default :
22568                 this.hide();
22569                 
22570         }
22571     },
22572     
22573     remove: function() 
22574     {
22575         this.picker().remove();
22576     }
22577    
22578 });
22579
22580 Roo.apply(Roo.bootstrap.MonthField,  {
22581     
22582     content : {
22583         tag: 'tbody',
22584         cn: [
22585         {
22586             tag: 'tr',
22587             cn: [
22588             {
22589                 tag: 'td',
22590                 colspan: '7'
22591             }
22592             ]
22593         }
22594         ]
22595     },
22596     
22597     dates:{
22598         en: {
22599             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22600             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22601         }
22602     }
22603 });
22604
22605 Roo.apply(Roo.bootstrap.MonthField,  {
22606   
22607     template : {
22608         tag: 'div',
22609         cls: 'datepicker dropdown-menu roo-dynamic',
22610         cn: [
22611             {
22612                 tag: 'div',
22613                 cls: 'datepicker-months',
22614                 cn: [
22615                 {
22616                     tag: 'table',
22617                     cls: 'table-condensed',
22618                     cn:[
22619                         Roo.bootstrap.DateField.content
22620                     ]
22621                 }
22622                 ]
22623             }
22624         ]
22625     }
22626 });
22627
22628  
22629
22630  
22631  /*
22632  * - LGPL
22633  *
22634  * CheckBox
22635  * 
22636  */
22637
22638 /**
22639  * @class Roo.bootstrap.CheckBox
22640  * @extends Roo.bootstrap.Input
22641  * Bootstrap CheckBox class
22642  * 
22643  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22644  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22645  * @cfg {String} boxLabel The text that appears beside the checkbox
22646  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22647  * @cfg {Boolean} checked initnal the element
22648  * @cfg {Boolean} inline inline the element (default false)
22649  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22650  * @cfg {String} tooltip label tooltip
22651  * 
22652  * @constructor
22653  * Create a new CheckBox
22654  * @param {Object} config The config object
22655  */
22656
22657 Roo.bootstrap.CheckBox = function(config){
22658     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22659    
22660     this.addEvents({
22661         /**
22662         * @event check
22663         * Fires when the element is checked or unchecked.
22664         * @param {Roo.bootstrap.CheckBox} this This input
22665         * @param {Boolean} checked The new checked value
22666         */
22667        check : true,
22668        /**
22669         * @event click
22670         * Fires when the element is click.
22671         * @param {Roo.bootstrap.CheckBox} this This input
22672         */
22673        click : true
22674     });
22675     
22676 };
22677
22678 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22679   
22680     inputType: 'checkbox',
22681     inputValue: 1,
22682     valueOff: 0,
22683     boxLabel: false,
22684     checked: false,
22685     weight : false,
22686     inline: false,
22687     tooltip : '',
22688     
22689     // checkbox success does not make any sense really.. 
22690     invalidClass : "",
22691     validClass : "",
22692     
22693     
22694     getAutoCreate : function()
22695     {
22696         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22697         
22698         var id = Roo.id();
22699         
22700         var cfg = {};
22701         
22702         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22703         
22704         if(this.inline){
22705             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22706         }
22707         
22708         var input =  {
22709             tag: 'input',
22710             id : id,
22711             type : this.inputType,
22712             value : this.inputValue,
22713             cls : 'roo-' + this.inputType, //'form-box',
22714             placeholder : this.placeholder || ''
22715             
22716         };
22717         
22718         if(this.inputType != 'radio'){
22719             var hidden =  {
22720                 tag: 'input',
22721                 type : 'hidden',
22722                 cls : 'roo-hidden-value',
22723                 value : this.checked ? this.inputValue : this.valueOff
22724             };
22725         }
22726         
22727             
22728         if (this.weight) { // Validity check?
22729             cfg.cls += " " + this.inputType + "-" + this.weight;
22730         }
22731         
22732         if (this.disabled) {
22733             input.disabled=true;
22734         }
22735         
22736         if(this.checked){
22737             input.checked = this.checked;
22738         }
22739         
22740         if (this.name) {
22741             
22742             input.name = this.name;
22743             
22744             if(this.inputType != 'radio'){
22745                 hidden.name = this.name;
22746                 input.name = '_hidden_' + this.name;
22747             }
22748         }
22749         
22750         if (this.size) {
22751             input.cls += ' input-' + this.size;
22752         }
22753         
22754         var settings=this;
22755         
22756         ['xs','sm','md','lg'].map(function(size){
22757             if (settings[size]) {
22758                 cfg.cls += ' col-' + size + '-' + settings[size];
22759             }
22760         });
22761         
22762         var inputblock = input;
22763          
22764         if (this.before || this.after) {
22765             
22766             inputblock = {
22767                 cls : 'input-group',
22768                 cn :  [] 
22769             };
22770             
22771             if (this.before) {
22772                 inputblock.cn.push({
22773                     tag :'span',
22774                     cls : 'input-group-addon',
22775                     html : this.before
22776                 });
22777             }
22778             
22779             inputblock.cn.push(input);
22780             
22781             if(this.inputType != 'radio'){
22782                 inputblock.cn.push(hidden);
22783             }
22784             
22785             if (this.after) {
22786                 inputblock.cn.push({
22787                     tag :'span',
22788                     cls : 'input-group-addon',
22789                     html : this.after
22790                 });
22791             }
22792             
22793         }
22794         var boxLabelCfg = false;
22795         
22796         if(this.boxLabel){
22797            
22798             boxLabelCfg = {
22799                 tag: 'label',
22800                 //'for': id, // box label is handled by onclick - so no for...
22801                 cls: 'box-label',
22802                 html: this.boxLabel
22803             };
22804             if(this.tooltip){
22805                 boxLabelCfg.tooltip = this.tooltip;
22806             }
22807              
22808         }
22809         
22810         
22811         if (align ==='left' && this.fieldLabel.length) {
22812 //                Roo.log("left and has label");
22813             cfg.cn = [
22814                 {
22815                     tag: 'label',
22816                     'for' :  id,
22817                     cls : 'control-label',
22818                     html : this.fieldLabel
22819                 },
22820                 {
22821                     cls : "", 
22822                     cn: [
22823                         inputblock
22824                     ]
22825                 }
22826             ];
22827             
22828             if (boxLabelCfg) {
22829                 cfg.cn[1].cn.push(boxLabelCfg);
22830             }
22831             
22832             if(this.labelWidth > 12){
22833                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22834             }
22835             
22836             if(this.labelWidth < 13 && this.labelmd == 0){
22837                 this.labelmd = this.labelWidth;
22838             }
22839             
22840             if(this.labellg > 0){
22841                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22842                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22843             }
22844             
22845             if(this.labelmd > 0){
22846                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22847                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22848             }
22849             
22850             if(this.labelsm > 0){
22851                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22852                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22853             }
22854             
22855             if(this.labelxs > 0){
22856                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22857                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22858             }
22859             
22860         } else if ( this.fieldLabel.length) {
22861 //                Roo.log(" label");
22862                 cfg.cn = [
22863                    
22864                     {
22865                         tag: this.boxLabel ? 'span' : 'label',
22866                         'for': id,
22867                         cls: 'control-label box-input-label',
22868                         //cls : 'input-group-addon',
22869                         html : this.fieldLabel
22870                     },
22871                     
22872                     inputblock
22873                     
22874                 ];
22875                 if (boxLabelCfg) {
22876                     cfg.cn.push(boxLabelCfg);
22877                 }
22878
22879         } else {
22880             
22881 //                Roo.log(" no label && no align");
22882                 cfg.cn = [  inputblock ] ;
22883                 if (boxLabelCfg) {
22884                     cfg.cn.push(boxLabelCfg);
22885                 }
22886
22887                 
22888         }
22889         
22890        
22891         
22892         if(this.inputType != 'radio'){
22893             cfg.cn.push(hidden);
22894         }
22895         
22896         return cfg;
22897         
22898     },
22899     
22900     /**
22901      * return the real input element.
22902      */
22903     inputEl: function ()
22904     {
22905         return this.el.select('input.roo-' + this.inputType,true).first();
22906     },
22907     hiddenEl: function ()
22908     {
22909         return this.el.select('input.roo-hidden-value',true).first();
22910     },
22911     
22912     labelEl: function()
22913     {
22914         return this.el.select('label.control-label',true).first();
22915     },
22916     /* depricated... */
22917     
22918     label: function()
22919     {
22920         return this.labelEl();
22921     },
22922     
22923     boxLabelEl: function()
22924     {
22925         return this.el.select('label.box-label',true).first();
22926     },
22927     
22928     initEvents : function()
22929     {
22930 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22931         
22932         this.inputEl().on('click', this.onClick,  this);
22933         
22934         if (this.boxLabel) { 
22935             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22936         }
22937         
22938         this.startValue = this.getValue();
22939         
22940         if(this.groupId){
22941             Roo.bootstrap.CheckBox.register(this);
22942         }
22943     },
22944     
22945     onClick : function(e)
22946     {   
22947         if(this.fireEvent('click', this, e) !== false){
22948             this.setChecked(!this.checked);
22949         }
22950         
22951     },
22952     
22953     setChecked : function(state,suppressEvent)
22954     {
22955         this.startValue = this.getValue();
22956
22957         if(this.inputType == 'radio'){
22958             
22959             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22960                 e.dom.checked = false;
22961             });
22962             
22963             this.inputEl().dom.checked = true;
22964             
22965             this.inputEl().dom.value = this.inputValue;
22966             
22967             if(suppressEvent !== true){
22968                 this.fireEvent('check', this, true);
22969             }
22970             
22971             this.validate();
22972             
22973             return;
22974         }
22975         
22976         this.checked = state;
22977         
22978         this.inputEl().dom.checked = state;
22979         
22980         
22981         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22982         
22983         if(suppressEvent !== true){
22984             this.fireEvent('check', this, state);
22985         }
22986         
22987         this.validate();
22988     },
22989     
22990     getValue : function()
22991     {
22992         if(this.inputType == 'radio'){
22993             return this.getGroupValue();
22994         }
22995         
22996         return this.hiddenEl().dom.value;
22997         
22998     },
22999     
23000     getGroupValue : function()
23001     {
23002         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23003             return '';
23004         }
23005         
23006         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23007     },
23008     
23009     setValue : function(v,suppressEvent)
23010     {
23011         if(this.inputType == 'radio'){
23012             this.setGroupValue(v, suppressEvent);
23013             return;
23014         }
23015         
23016         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23017         
23018         this.validate();
23019     },
23020     
23021     setGroupValue : function(v, suppressEvent)
23022     {
23023         this.startValue = this.getValue();
23024         
23025         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23026             e.dom.checked = false;
23027             
23028             if(e.dom.value == v){
23029                 e.dom.checked = true;
23030             }
23031         });
23032         
23033         if(suppressEvent !== true){
23034             this.fireEvent('check', this, true);
23035         }
23036
23037         this.validate();
23038         
23039         return;
23040     },
23041     
23042     validate : function()
23043     {
23044         if(this.getVisibilityEl().hasClass('hidden')){
23045             return true;
23046         }
23047         
23048         if(
23049                 this.disabled || 
23050                 (this.inputType == 'radio' && this.validateRadio()) ||
23051                 (this.inputType == 'checkbox' && this.validateCheckbox())
23052         ){
23053             this.markValid();
23054             return true;
23055         }
23056         
23057         this.markInvalid();
23058         return false;
23059     },
23060     
23061     validateRadio : function()
23062     {
23063         if(this.getVisibilityEl().hasClass('hidden')){
23064             return true;
23065         }
23066         
23067         if(this.allowBlank){
23068             return true;
23069         }
23070         
23071         var valid = false;
23072         
23073         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23074             if(!e.dom.checked){
23075                 return;
23076             }
23077             
23078             valid = true;
23079             
23080             return false;
23081         });
23082         
23083         return valid;
23084     },
23085     
23086     validateCheckbox : function()
23087     {
23088         if(!this.groupId){
23089             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23090             //return (this.getValue() == this.inputValue) ? true : false;
23091         }
23092         
23093         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23094         
23095         if(!group){
23096             return false;
23097         }
23098         
23099         var r = false;
23100         
23101         for(var i in group){
23102             if(group[i].el.isVisible(true)){
23103                 r = false;
23104                 break;
23105             }
23106             
23107             r = true;
23108         }
23109         
23110         for(var i in group){
23111             if(r){
23112                 break;
23113             }
23114             
23115             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23116         }
23117         
23118         return r;
23119     },
23120     
23121     /**
23122      * Mark this field as valid
23123      */
23124     markValid : function()
23125     {
23126         var _this = this;
23127         
23128         this.fireEvent('valid', this);
23129         
23130         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23131         
23132         if(this.groupId){
23133             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23134         }
23135         
23136         if(label){
23137             label.markValid();
23138         }
23139
23140         if(this.inputType == 'radio'){
23141             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23142                 var fg = e.findParent('.form-group', false, true);
23143                 if (Roo.bootstrap.version == 3) {
23144                     fg.removeClass([_this.invalidClass, _this.validClass]);
23145                     fg.addClass(_this.validClass);
23146                 } else {
23147                     fg.removeClass(['is-valid', 'is-invalid']);
23148                     fg.addClass('is-valid');
23149                 }
23150             });
23151             
23152             return;
23153         }
23154
23155         if(!this.groupId){
23156             var fg = this.el.findParent('.form-group', false, true);
23157             if (Roo.bootstrap.version == 3) {
23158                 fg.removeClass([this.invalidClass, this.validClass]);
23159                 fg.addClass(this.validClass);
23160             } else {
23161                 fg.removeClass(['is-valid', 'is-invalid']);
23162                 fg.addClass('is-valid');
23163             }
23164             return;
23165         }
23166         
23167         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23168         
23169         if(!group){
23170             return;
23171         }
23172         
23173         for(var i in group){
23174             var fg = group[i].el.findParent('.form-group', false, true);
23175             if (Roo.bootstrap.version == 3) {
23176                 fg.removeClass([this.invalidClass, this.validClass]);
23177                 fg.addClass(this.validClass);
23178             } else {
23179                 fg.removeClass(['is-valid', 'is-invalid']);
23180                 fg.addClass('is-valid');
23181             }
23182         }
23183     },
23184     
23185      /**
23186      * Mark this field as invalid
23187      * @param {String} msg The validation message
23188      */
23189     markInvalid : function(msg)
23190     {
23191         if(this.allowBlank){
23192             return;
23193         }
23194         
23195         var _this = this;
23196         
23197         this.fireEvent('invalid', this, msg);
23198         
23199         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23200         
23201         if(this.groupId){
23202             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23203         }
23204         
23205         if(label){
23206             label.markInvalid();
23207         }
23208             
23209         if(this.inputType == 'radio'){
23210             
23211             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23212                 var fg = e.findParent('.form-group', false, true);
23213                 if (Roo.bootstrap.version == 3) {
23214                     fg.removeClass([_this.invalidClass, _this.validClass]);
23215                     fg.addClass(_this.invalidClass);
23216                 } else {
23217                     fg.removeClass(['is-invalid', 'is-valid']);
23218                     fg.addClass('is-invalid');
23219                 }
23220             });
23221             
23222             return;
23223         }
23224         
23225         if(!this.groupId){
23226             var fg = this.el.findParent('.form-group', false, true);
23227             if (Roo.bootstrap.version == 3) {
23228                 fg.removeClass([_this.invalidClass, _this.validClass]);
23229                 fg.addClass(_this.invalidClass);
23230             } else {
23231                 fg.removeClass(['is-invalid', 'is-valid']);
23232                 fg.addClass('is-invalid');
23233             }
23234             return;
23235         }
23236         
23237         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23238         
23239         if(!group){
23240             return;
23241         }
23242         
23243         for(var i in group){
23244             var fg = group[i].el.findParent('.form-group', false, true);
23245             if (Roo.bootstrap.version == 3) {
23246                 fg.removeClass([_this.invalidClass, _this.validClass]);
23247                 fg.addClass(_this.invalidClass);
23248             } else {
23249                 fg.removeClass(['is-invalid', 'is-valid']);
23250                 fg.addClass('is-invalid');
23251             }
23252         }
23253         
23254     },
23255     
23256     clearInvalid : function()
23257     {
23258         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23259         
23260         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23261         
23262         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23263         
23264         if (label && label.iconEl) {
23265             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23266             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23267         }
23268     },
23269     
23270     disable : function()
23271     {
23272         if(this.inputType != 'radio'){
23273             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23274             return;
23275         }
23276         
23277         var _this = this;
23278         
23279         if(this.rendered){
23280             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23281                 _this.getActionEl().addClass(this.disabledClass);
23282                 e.dom.disabled = true;
23283             });
23284         }
23285         
23286         this.disabled = true;
23287         this.fireEvent("disable", this);
23288         return this;
23289     },
23290
23291     enable : function()
23292     {
23293         if(this.inputType != 'radio'){
23294             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23295             return;
23296         }
23297         
23298         var _this = this;
23299         
23300         if(this.rendered){
23301             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23302                 _this.getActionEl().removeClass(this.disabledClass);
23303                 e.dom.disabled = false;
23304             });
23305         }
23306         
23307         this.disabled = false;
23308         this.fireEvent("enable", this);
23309         return this;
23310     },
23311     
23312     setBoxLabel : function(v)
23313     {
23314         this.boxLabel = v;
23315         
23316         if(this.rendered){
23317             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23318         }
23319     }
23320
23321 });
23322
23323 Roo.apply(Roo.bootstrap.CheckBox, {
23324     
23325     groups: {},
23326     
23327      /**
23328     * register a CheckBox Group
23329     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23330     */
23331     register : function(checkbox)
23332     {
23333         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23334             this.groups[checkbox.groupId] = {};
23335         }
23336         
23337         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23338             return;
23339         }
23340         
23341         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23342         
23343     },
23344     /**
23345     * fetch a CheckBox Group based on the group ID
23346     * @param {string} the group ID
23347     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23348     */
23349     get: function(groupId) {
23350         if (typeof(this.groups[groupId]) == 'undefined') {
23351             return false;
23352         }
23353         
23354         return this.groups[groupId] ;
23355     }
23356     
23357     
23358 });
23359 /*
23360  * - LGPL
23361  *
23362  * RadioItem
23363  * 
23364  */
23365
23366 /**
23367  * @class Roo.bootstrap.Radio
23368  * @extends Roo.bootstrap.Component
23369  * Bootstrap Radio class
23370  * @cfg {String} boxLabel - the label associated
23371  * @cfg {String} value - the value of radio
23372  * 
23373  * @constructor
23374  * Create a new Radio
23375  * @param {Object} config The config object
23376  */
23377 Roo.bootstrap.Radio = function(config){
23378     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23379     
23380 };
23381
23382 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23383     
23384     boxLabel : '',
23385     
23386     value : '',
23387     
23388     getAutoCreate : function()
23389     {
23390         var cfg = {
23391             tag : 'div',
23392             cls : 'form-group radio',
23393             cn : [
23394                 {
23395                     tag : 'label',
23396                     cls : 'box-label',
23397                     html : this.boxLabel
23398                 }
23399             ]
23400         };
23401         
23402         return cfg;
23403     },
23404     
23405     initEvents : function() 
23406     {
23407         this.parent().register(this);
23408         
23409         this.el.on('click', this.onClick, this);
23410         
23411     },
23412     
23413     onClick : function(e)
23414     {
23415         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23416             this.setChecked(true);
23417         }
23418     },
23419     
23420     setChecked : function(state, suppressEvent)
23421     {
23422         this.parent().setValue(this.value, suppressEvent);
23423         
23424     },
23425     
23426     setBoxLabel : function(v)
23427     {
23428         this.boxLabel = v;
23429         
23430         if(this.rendered){
23431             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23432         }
23433     }
23434     
23435 });
23436  
23437
23438  /*
23439  * - LGPL
23440  *
23441  * Input
23442  * 
23443  */
23444
23445 /**
23446  * @class Roo.bootstrap.SecurePass
23447  * @extends Roo.bootstrap.Input
23448  * Bootstrap SecurePass class
23449  *
23450  * 
23451  * @constructor
23452  * Create a new SecurePass
23453  * @param {Object} config The config object
23454  */
23455  
23456 Roo.bootstrap.SecurePass = function (config) {
23457     // these go here, so the translation tool can replace them..
23458     this.errors = {
23459         PwdEmpty: "Please type a password, and then retype it to confirm.",
23460         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23461         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23462         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23463         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23464         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23465         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23466         TooWeak: "Your password is Too Weak."
23467     },
23468     this.meterLabel = "Password strength:";
23469     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23470     this.meterClass = [
23471         "roo-password-meter-tooweak", 
23472         "roo-password-meter-weak", 
23473         "roo-password-meter-medium", 
23474         "roo-password-meter-strong", 
23475         "roo-password-meter-grey"
23476     ];
23477     
23478     this.errors = {};
23479     
23480     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23481 }
23482
23483 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23484     /**
23485      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23486      * {
23487      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23488      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23489      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23490      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23491      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23492      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23493      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23494      * })
23495      */
23496     // private
23497     
23498     meterWidth: 300,
23499     errorMsg :'',    
23500     errors: false,
23501     imageRoot: '/',
23502     /**
23503      * @cfg {String/Object} Label for the strength meter (defaults to
23504      * 'Password strength:')
23505      */
23506     // private
23507     meterLabel: '',
23508     /**
23509      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23510      * ['Weak', 'Medium', 'Strong'])
23511      */
23512     // private    
23513     pwdStrengths: false,    
23514     // private
23515     strength: 0,
23516     // private
23517     _lastPwd: null,
23518     // private
23519     kCapitalLetter: 0,
23520     kSmallLetter: 1,
23521     kDigit: 2,
23522     kPunctuation: 3,
23523     
23524     insecure: false,
23525     // private
23526     initEvents: function ()
23527     {
23528         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23529
23530         if (this.el.is('input[type=password]') && Roo.isSafari) {
23531             this.el.on('keydown', this.SafariOnKeyDown, this);
23532         }
23533
23534         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23535     },
23536     // private
23537     onRender: function (ct, position)
23538     {
23539         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23540         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23541         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23542
23543         this.trigger.createChild({
23544                    cn: [
23545                     {
23546                     //id: 'PwdMeter',
23547                     tag: 'div',
23548                     cls: 'roo-password-meter-grey col-xs-12',
23549                     style: {
23550                         //width: 0,
23551                         //width: this.meterWidth + 'px'                                                
23552                         }
23553                     },
23554                     {                            
23555                          cls: 'roo-password-meter-text'                          
23556                     }
23557                 ]            
23558         });
23559
23560          
23561         if (this.hideTrigger) {
23562             this.trigger.setDisplayed(false);
23563         }
23564         this.setSize(this.width || '', this.height || '');
23565     },
23566     // private
23567     onDestroy: function ()
23568     {
23569         if (this.trigger) {
23570             this.trigger.removeAllListeners();
23571             this.trigger.remove();
23572         }
23573         if (this.wrap) {
23574             this.wrap.remove();
23575         }
23576         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23577     },
23578     // private
23579     checkStrength: function ()
23580     {
23581         var pwd = this.inputEl().getValue();
23582         if (pwd == this._lastPwd) {
23583             return;
23584         }
23585
23586         var strength;
23587         if (this.ClientSideStrongPassword(pwd)) {
23588             strength = 3;
23589         } else if (this.ClientSideMediumPassword(pwd)) {
23590             strength = 2;
23591         } else if (this.ClientSideWeakPassword(pwd)) {
23592             strength = 1;
23593         } else {
23594             strength = 0;
23595         }
23596         
23597         Roo.log('strength1: ' + strength);
23598         
23599         //var pm = this.trigger.child('div/div/div').dom;
23600         var pm = this.trigger.child('div/div');
23601         pm.removeClass(this.meterClass);
23602         pm.addClass(this.meterClass[strength]);
23603                 
23604         
23605         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23606                 
23607         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23608         
23609         this._lastPwd = pwd;
23610     },
23611     reset: function ()
23612     {
23613         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23614         
23615         this._lastPwd = '';
23616         
23617         var pm = this.trigger.child('div/div');
23618         pm.removeClass(this.meterClass);
23619         pm.addClass('roo-password-meter-grey');        
23620         
23621         
23622         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23623         
23624         pt.innerHTML = '';
23625         this.inputEl().dom.type='password';
23626     },
23627     // private
23628     validateValue: function (value)
23629     {
23630         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23631             return false;
23632         }
23633         if (value.length == 0) {
23634             if (this.allowBlank) {
23635                 this.clearInvalid();
23636                 return true;
23637             }
23638
23639             this.markInvalid(this.errors.PwdEmpty);
23640             this.errorMsg = this.errors.PwdEmpty;
23641             return false;
23642         }
23643         
23644         if(this.insecure){
23645             return true;
23646         }
23647         
23648         if (!value.match(/[\x21-\x7e]+/)) {
23649             this.markInvalid(this.errors.PwdBadChar);
23650             this.errorMsg = this.errors.PwdBadChar;
23651             return false;
23652         }
23653         if (value.length < 6) {
23654             this.markInvalid(this.errors.PwdShort);
23655             this.errorMsg = this.errors.PwdShort;
23656             return false;
23657         }
23658         if (value.length > 16) {
23659             this.markInvalid(this.errors.PwdLong);
23660             this.errorMsg = this.errors.PwdLong;
23661             return false;
23662         }
23663         var strength;
23664         if (this.ClientSideStrongPassword(value)) {
23665             strength = 3;
23666         } else if (this.ClientSideMediumPassword(value)) {
23667             strength = 2;
23668         } else if (this.ClientSideWeakPassword(value)) {
23669             strength = 1;
23670         } else {
23671             strength = 0;
23672         }
23673
23674         
23675         if (strength < 2) {
23676             //this.markInvalid(this.errors.TooWeak);
23677             this.errorMsg = this.errors.TooWeak;
23678             //return false;
23679         }
23680         
23681         
23682         console.log('strength2: ' + strength);
23683         
23684         //var pm = this.trigger.child('div/div/div').dom;
23685         
23686         var pm = this.trigger.child('div/div');
23687         pm.removeClass(this.meterClass);
23688         pm.addClass(this.meterClass[strength]);
23689                 
23690         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23691                 
23692         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23693         
23694         this.errorMsg = ''; 
23695         return true;
23696     },
23697     // private
23698     CharacterSetChecks: function (type)
23699     {
23700         this.type = type;
23701         this.fResult = false;
23702     },
23703     // private
23704     isctype: function (character, type)
23705     {
23706         switch (type) {  
23707             case this.kCapitalLetter:
23708                 if (character >= 'A' && character <= 'Z') {
23709                     return true;
23710                 }
23711                 break;
23712             
23713             case this.kSmallLetter:
23714                 if (character >= 'a' && character <= 'z') {
23715                     return true;
23716                 }
23717                 break;
23718             
23719             case this.kDigit:
23720                 if (character >= '0' && character <= '9') {
23721                     return true;
23722                 }
23723                 break;
23724             
23725             case this.kPunctuation:
23726                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23727                     return true;
23728                 }
23729                 break;
23730             
23731             default:
23732                 return false;
23733         }
23734
23735     },
23736     // private
23737     IsLongEnough: function (pwd, size)
23738     {
23739         return !(pwd == null || isNaN(size) || pwd.length < size);
23740     },
23741     // private
23742     SpansEnoughCharacterSets: function (word, nb)
23743     {
23744         if (!this.IsLongEnough(word, nb))
23745         {
23746             return false;
23747         }
23748
23749         var characterSetChecks = new Array(
23750             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23751             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23752         );
23753         
23754         for (var index = 0; index < word.length; ++index) {
23755             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23756                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23757                     characterSetChecks[nCharSet].fResult = true;
23758                     break;
23759                 }
23760             }
23761         }
23762
23763         var nCharSets = 0;
23764         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23765             if (characterSetChecks[nCharSet].fResult) {
23766                 ++nCharSets;
23767             }
23768         }
23769
23770         if (nCharSets < nb) {
23771             return false;
23772         }
23773         return true;
23774     },
23775     // private
23776     ClientSideStrongPassword: function (pwd)
23777     {
23778         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23779     },
23780     // private
23781     ClientSideMediumPassword: function (pwd)
23782     {
23783         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23784     },
23785     // private
23786     ClientSideWeakPassword: function (pwd)
23787     {
23788         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23789     }
23790           
23791 })//<script type="text/javascript">
23792
23793 /*
23794  * Based  Ext JS Library 1.1.1
23795  * Copyright(c) 2006-2007, Ext JS, LLC.
23796  * LGPL
23797  *
23798  */
23799  
23800 /**
23801  * @class Roo.HtmlEditorCore
23802  * @extends Roo.Component
23803  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23804  *
23805  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23806  */
23807
23808 Roo.HtmlEditorCore = function(config){
23809     
23810     
23811     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23812     
23813     
23814     this.addEvents({
23815         /**
23816          * @event initialize
23817          * Fires when the editor is fully initialized (including the iframe)
23818          * @param {Roo.HtmlEditorCore} this
23819          */
23820         initialize: true,
23821         /**
23822          * @event activate
23823          * Fires when the editor is first receives the focus. Any insertion must wait
23824          * until after this event.
23825          * @param {Roo.HtmlEditorCore} this
23826          */
23827         activate: true,
23828          /**
23829          * @event beforesync
23830          * Fires before the textarea is updated with content from the editor iframe. Return false
23831          * to cancel the sync.
23832          * @param {Roo.HtmlEditorCore} this
23833          * @param {String} html
23834          */
23835         beforesync: true,
23836          /**
23837          * @event beforepush
23838          * Fires before the iframe editor is updated with content from the textarea. Return false
23839          * to cancel the push.
23840          * @param {Roo.HtmlEditorCore} this
23841          * @param {String} html
23842          */
23843         beforepush: true,
23844          /**
23845          * @event sync
23846          * Fires when the textarea is updated with content from the editor iframe.
23847          * @param {Roo.HtmlEditorCore} this
23848          * @param {String} html
23849          */
23850         sync: true,
23851          /**
23852          * @event push
23853          * Fires when the iframe editor is updated with content from the textarea.
23854          * @param {Roo.HtmlEditorCore} this
23855          * @param {String} html
23856          */
23857         push: true,
23858         
23859         /**
23860          * @event editorevent
23861          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23862          * @param {Roo.HtmlEditorCore} this
23863          */
23864         editorevent: true
23865         
23866     });
23867     
23868     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23869     
23870     // defaults : white / black...
23871     this.applyBlacklists();
23872     
23873     
23874     
23875 };
23876
23877
23878 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23879
23880
23881      /**
23882      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23883      */
23884     
23885     owner : false,
23886     
23887      /**
23888      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23889      *                        Roo.resizable.
23890      */
23891     resizable : false,
23892      /**
23893      * @cfg {Number} height (in pixels)
23894      */   
23895     height: 300,
23896    /**
23897      * @cfg {Number} width (in pixels)
23898      */   
23899     width: 500,
23900     
23901     /**
23902      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23903      * 
23904      */
23905     stylesheets: false,
23906     
23907     // id of frame..
23908     frameId: false,
23909     
23910     // private properties
23911     validationEvent : false,
23912     deferHeight: true,
23913     initialized : false,
23914     activated : false,
23915     sourceEditMode : false,
23916     onFocus : Roo.emptyFn,
23917     iframePad:3,
23918     hideMode:'offsets',
23919     
23920     clearUp: true,
23921     
23922     // blacklist + whitelisted elements..
23923     black: false,
23924     white: false,
23925      
23926     bodyCls : '',
23927
23928     /**
23929      * Protected method that will not generally be called directly. It
23930      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23931      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23932      */
23933     getDocMarkup : function(){
23934         // body styles..
23935         var st = '';
23936         
23937         // inherit styels from page...?? 
23938         if (this.stylesheets === false) {
23939             
23940             Roo.get(document.head).select('style').each(function(node) {
23941                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23942             });
23943             
23944             Roo.get(document.head).select('link').each(function(node) { 
23945                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23946             });
23947             
23948         } else if (!this.stylesheets.length) {
23949                 // simple..
23950                 st = '<style type="text/css">' +
23951                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23952                    '</style>';
23953         } else {
23954             for (var i in this.stylesheets) { 
23955                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23956             }
23957             
23958         }
23959         
23960         st +=  '<style type="text/css">' +
23961             'IMG { cursor: pointer } ' +
23962         '</style>';
23963
23964         var cls = 'roo-htmleditor-body';
23965         
23966         if(this.bodyCls.length){
23967             cls += ' ' + this.bodyCls;
23968         }
23969         
23970         return '<html><head>' + st  +
23971             //<style type="text/css">' +
23972             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23973             //'</style>' +
23974             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23975     },
23976
23977     // private
23978     onRender : function(ct, position)
23979     {
23980         var _t = this;
23981         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23982         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23983         
23984         
23985         this.el.dom.style.border = '0 none';
23986         this.el.dom.setAttribute('tabIndex', -1);
23987         this.el.addClass('x-hidden hide');
23988         
23989         
23990         
23991         if(Roo.isIE){ // fix IE 1px bogus margin
23992             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23993         }
23994        
23995         
23996         this.frameId = Roo.id();
23997         
23998          
23999         
24000         var iframe = this.owner.wrap.createChild({
24001             tag: 'iframe',
24002             cls: 'form-control', // bootstrap..
24003             id: this.frameId,
24004             name: this.frameId,
24005             frameBorder : 'no',
24006             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24007         }, this.el
24008         );
24009         
24010         
24011         this.iframe = iframe.dom;
24012
24013          this.assignDocWin();
24014         
24015         this.doc.designMode = 'on';
24016        
24017         this.doc.open();
24018         this.doc.write(this.getDocMarkup());
24019         this.doc.close();
24020
24021         
24022         var task = { // must defer to wait for browser to be ready
24023             run : function(){
24024                 //console.log("run task?" + this.doc.readyState);
24025                 this.assignDocWin();
24026                 if(this.doc.body || this.doc.readyState == 'complete'){
24027                     try {
24028                         this.doc.designMode="on";
24029                     } catch (e) {
24030                         return;
24031                     }
24032                     Roo.TaskMgr.stop(task);
24033                     this.initEditor.defer(10, this);
24034                 }
24035             },
24036             interval : 10,
24037             duration: 10000,
24038             scope: this
24039         };
24040         Roo.TaskMgr.start(task);
24041
24042     },
24043
24044     // private
24045     onResize : function(w, h)
24046     {
24047          Roo.log('resize: ' +w + ',' + h );
24048         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24049         if(!this.iframe){
24050             return;
24051         }
24052         if(typeof w == 'number'){
24053             
24054             this.iframe.style.width = w + 'px';
24055         }
24056         if(typeof h == 'number'){
24057             
24058             this.iframe.style.height = h + 'px';
24059             if(this.doc){
24060                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24061             }
24062         }
24063         
24064     },
24065
24066     /**
24067      * Toggles the editor between standard and source edit mode.
24068      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24069      */
24070     toggleSourceEdit : function(sourceEditMode){
24071         
24072         this.sourceEditMode = sourceEditMode === true;
24073         
24074         if(this.sourceEditMode){
24075  
24076             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24077             
24078         }else{
24079             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24080             //this.iframe.className = '';
24081             this.deferFocus();
24082         }
24083         //this.setSize(this.owner.wrap.getSize());
24084         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24085     },
24086
24087     
24088   
24089
24090     /**
24091      * Protected method that will not generally be called directly. If you need/want
24092      * custom HTML cleanup, this is the method you should override.
24093      * @param {String} html The HTML to be cleaned
24094      * return {String} The cleaned HTML
24095      */
24096     cleanHtml : function(html){
24097         html = String(html);
24098         if(html.length > 5){
24099             if(Roo.isSafari){ // strip safari nonsense
24100                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24101             }
24102         }
24103         if(html == '&nbsp;'){
24104             html = '';
24105         }
24106         return html;
24107     },
24108
24109     /**
24110      * HTML Editor -> Textarea
24111      * Protected method that will not generally be called directly. Syncs the contents
24112      * of the editor iframe with the textarea.
24113      */
24114     syncValue : function(){
24115         if(this.initialized){
24116             var bd = (this.doc.body || this.doc.documentElement);
24117             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24118             var html = bd.innerHTML;
24119             if(Roo.isSafari){
24120                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24121                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24122                 if(m && m[1]){
24123                     html = '<div style="'+m[0]+'">' + html + '</div>';
24124                 }
24125             }
24126             html = this.cleanHtml(html);
24127             // fix up the special chars.. normaly like back quotes in word...
24128             // however we do not want to do this with chinese..
24129             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24130                 
24131                 var cc = match.charCodeAt();
24132
24133                 // Get the character value, handling surrogate pairs
24134                 if (match.length == 2) {
24135                     // It's a surrogate pair, calculate the Unicode code point
24136                     var high = match.charCodeAt(0) - 0xD800;
24137                     var low  = match.charCodeAt(1) - 0xDC00;
24138                     cc = (high * 0x400) + low + 0x10000;
24139                 }  else if (
24140                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24141                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24142                     (cc >= 0xf900 && cc < 0xfb00 )
24143                 ) {
24144                         return match;
24145                 }  
24146          
24147                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24148                 return "&#" + cc + ";";
24149                 
24150                 
24151             });
24152             
24153             
24154              
24155             if(this.owner.fireEvent('beforesync', this, html) !== false){
24156                 this.el.dom.value = html;
24157                 this.owner.fireEvent('sync', this, html);
24158             }
24159         }
24160     },
24161
24162     /**
24163      * Protected method that will not generally be called directly. Pushes the value of the textarea
24164      * into the iframe editor.
24165      */
24166     pushValue : function(){
24167         if(this.initialized){
24168             var v = this.el.dom.value.trim();
24169             
24170 //            if(v.length < 1){
24171 //                v = '&#160;';
24172 //            }
24173             
24174             if(this.owner.fireEvent('beforepush', this, v) !== false){
24175                 var d = (this.doc.body || this.doc.documentElement);
24176                 d.innerHTML = v;
24177                 this.cleanUpPaste();
24178                 this.el.dom.value = d.innerHTML;
24179                 this.owner.fireEvent('push', this, v);
24180             }
24181         }
24182     },
24183
24184     // private
24185     deferFocus : function(){
24186         this.focus.defer(10, this);
24187     },
24188
24189     // doc'ed in Field
24190     focus : function(){
24191         if(this.win && !this.sourceEditMode){
24192             this.win.focus();
24193         }else{
24194             this.el.focus();
24195         }
24196     },
24197     
24198     assignDocWin: function()
24199     {
24200         var iframe = this.iframe;
24201         
24202          if(Roo.isIE){
24203             this.doc = iframe.contentWindow.document;
24204             this.win = iframe.contentWindow;
24205         } else {
24206 //            if (!Roo.get(this.frameId)) {
24207 //                return;
24208 //            }
24209 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24210 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24211             
24212             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24213                 return;
24214             }
24215             
24216             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24217             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24218         }
24219     },
24220     
24221     // private
24222     initEditor : function(){
24223         //console.log("INIT EDITOR");
24224         this.assignDocWin();
24225         
24226         
24227         
24228         this.doc.designMode="on";
24229         this.doc.open();
24230         this.doc.write(this.getDocMarkup());
24231         this.doc.close();
24232         
24233         var dbody = (this.doc.body || this.doc.documentElement);
24234         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24235         // this copies styles from the containing element into thsi one..
24236         // not sure why we need all of this..
24237         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24238         
24239         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24240         //ss['background-attachment'] = 'fixed'; // w3c
24241         dbody.bgProperties = 'fixed'; // ie
24242         //Roo.DomHelper.applyStyles(dbody, ss);
24243         Roo.EventManager.on(this.doc, {
24244             //'mousedown': this.onEditorEvent,
24245             'mouseup': this.onEditorEvent,
24246             'dblclick': this.onEditorEvent,
24247             'click': this.onEditorEvent,
24248             'keyup': this.onEditorEvent,
24249             buffer:100,
24250             scope: this
24251         });
24252         if(Roo.isGecko){
24253             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24254         }
24255         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24256             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24257         }
24258         this.initialized = true;
24259
24260         this.owner.fireEvent('initialize', this);
24261         this.pushValue();
24262     },
24263
24264     // private
24265     onDestroy : function(){
24266         
24267         
24268         
24269         if(this.rendered){
24270             
24271             //for (var i =0; i < this.toolbars.length;i++) {
24272             //    // fixme - ask toolbars for heights?
24273             //    this.toolbars[i].onDestroy();
24274            // }
24275             
24276             //this.wrap.dom.innerHTML = '';
24277             //this.wrap.remove();
24278         }
24279     },
24280
24281     // private
24282     onFirstFocus : function(){
24283         
24284         this.assignDocWin();
24285         
24286         
24287         this.activated = true;
24288          
24289     
24290         if(Roo.isGecko){ // prevent silly gecko errors
24291             this.win.focus();
24292             var s = this.win.getSelection();
24293             if(!s.focusNode || s.focusNode.nodeType != 3){
24294                 var r = s.getRangeAt(0);
24295                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24296                 r.collapse(true);
24297                 this.deferFocus();
24298             }
24299             try{
24300                 this.execCmd('useCSS', true);
24301                 this.execCmd('styleWithCSS', false);
24302             }catch(e){}
24303         }
24304         this.owner.fireEvent('activate', this);
24305     },
24306
24307     // private
24308     adjustFont: function(btn){
24309         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24310         //if(Roo.isSafari){ // safari
24311         //    adjust *= 2;
24312        // }
24313         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24314         if(Roo.isSafari){ // safari
24315             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24316             v =  (v < 10) ? 10 : v;
24317             v =  (v > 48) ? 48 : v;
24318             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24319             
24320         }
24321         
24322         
24323         v = Math.max(1, v+adjust);
24324         
24325         this.execCmd('FontSize', v  );
24326     },
24327
24328     onEditorEvent : function(e)
24329     {
24330         this.owner.fireEvent('editorevent', this, e);
24331       //  this.updateToolbar();
24332         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24333     },
24334
24335     insertTag : function(tg)
24336     {
24337         // could be a bit smarter... -> wrap the current selected tRoo..
24338         if (tg.toLowerCase() == 'span' ||
24339             tg.toLowerCase() == 'code' ||
24340             tg.toLowerCase() == 'sup' ||
24341             tg.toLowerCase() == 'sub' 
24342             ) {
24343             
24344             range = this.createRange(this.getSelection());
24345             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24346             wrappingNode.appendChild(range.extractContents());
24347             range.insertNode(wrappingNode);
24348
24349             return;
24350             
24351             
24352             
24353         }
24354         this.execCmd("formatblock",   tg);
24355         
24356     },
24357     
24358     insertText : function(txt)
24359     {
24360         
24361         
24362         var range = this.createRange();
24363         range.deleteContents();
24364                //alert(Sender.getAttribute('label'));
24365                
24366         range.insertNode(this.doc.createTextNode(txt));
24367     } ,
24368     
24369      
24370
24371     /**
24372      * Executes a Midas editor command on the editor document and performs necessary focus and
24373      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24374      * @param {String} cmd The Midas command
24375      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24376      */
24377     relayCmd : function(cmd, value){
24378         this.win.focus();
24379         this.execCmd(cmd, value);
24380         this.owner.fireEvent('editorevent', this);
24381         //this.updateToolbar();
24382         this.owner.deferFocus();
24383     },
24384
24385     /**
24386      * Executes a Midas editor command directly on the editor document.
24387      * For visual commands, you should use {@link #relayCmd} instead.
24388      * <b>This should only be called after the editor is initialized.</b>
24389      * @param {String} cmd The Midas command
24390      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24391      */
24392     execCmd : function(cmd, value){
24393         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24394         this.syncValue();
24395     },
24396  
24397  
24398    
24399     /**
24400      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24401      * to insert tRoo.
24402      * @param {String} text | dom node.. 
24403      */
24404     insertAtCursor : function(text)
24405     {
24406         
24407         if(!this.activated){
24408             return;
24409         }
24410         /*
24411         if(Roo.isIE){
24412             this.win.focus();
24413             var r = this.doc.selection.createRange();
24414             if(r){
24415                 r.collapse(true);
24416                 r.pasteHTML(text);
24417                 this.syncValue();
24418                 this.deferFocus();
24419             
24420             }
24421             return;
24422         }
24423         */
24424         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24425             this.win.focus();
24426             
24427             
24428             // from jquery ui (MIT licenced)
24429             var range, node;
24430             var win = this.win;
24431             
24432             if (win.getSelection && win.getSelection().getRangeAt) {
24433                 range = win.getSelection().getRangeAt(0);
24434                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24435                 range.insertNode(node);
24436             } else if (win.document.selection && win.document.selection.createRange) {
24437                 // no firefox support
24438                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24439                 win.document.selection.createRange().pasteHTML(txt);
24440             } else {
24441                 // no firefox support
24442                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24443                 this.execCmd('InsertHTML', txt);
24444             } 
24445             
24446             this.syncValue();
24447             
24448             this.deferFocus();
24449         }
24450     },
24451  // private
24452     mozKeyPress : function(e){
24453         if(e.ctrlKey){
24454             var c = e.getCharCode(), cmd;
24455           
24456             if(c > 0){
24457                 c = String.fromCharCode(c).toLowerCase();
24458                 switch(c){
24459                     case 'b':
24460                         cmd = 'bold';
24461                         break;
24462                     case 'i':
24463                         cmd = 'italic';
24464                         break;
24465                     
24466                     case 'u':
24467                         cmd = 'underline';
24468                         break;
24469                     
24470                     case 'v':
24471                         this.cleanUpPaste.defer(100, this);
24472                         return;
24473                         
24474                 }
24475                 if(cmd){
24476                     this.win.focus();
24477                     this.execCmd(cmd);
24478                     this.deferFocus();
24479                     e.preventDefault();
24480                 }
24481                 
24482             }
24483         }
24484     },
24485
24486     // private
24487     fixKeys : function(){ // load time branching for fastest keydown performance
24488         if(Roo.isIE){
24489             return function(e){
24490                 var k = e.getKey(), r;
24491                 if(k == e.TAB){
24492                     e.stopEvent();
24493                     r = this.doc.selection.createRange();
24494                     if(r){
24495                         r.collapse(true);
24496                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24497                         this.deferFocus();
24498                     }
24499                     return;
24500                 }
24501                 
24502                 if(k == e.ENTER){
24503                     r = this.doc.selection.createRange();
24504                     if(r){
24505                         var target = r.parentElement();
24506                         if(!target || target.tagName.toLowerCase() != 'li'){
24507                             e.stopEvent();
24508                             r.pasteHTML('<br />');
24509                             r.collapse(false);
24510                             r.select();
24511                         }
24512                     }
24513                 }
24514                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24515                     this.cleanUpPaste.defer(100, this);
24516                     return;
24517                 }
24518                 
24519                 
24520             };
24521         }else if(Roo.isOpera){
24522             return function(e){
24523                 var k = e.getKey();
24524                 if(k == e.TAB){
24525                     e.stopEvent();
24526                     this.win.focus();
24527                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24528                     this.deferFocus();
24529                 }
24530                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24531                     this.cleanUpPaste.defer(100, this);
24532                     return;
24533                 }
24534                 
24535             };
24536         }else if(Roo.isSafari){
24537             return function(e){
24538                 var k = e.getKey();
24539                 
24540                 if(k == e.TAB){
24541                     e.stopEvent();
24542                     this.execCmd('InsertText','\t');
24543                     this.deferFocus();
24544                     return;
24545                 }
24546                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24547                     this.cleanUpPaste.defer(100, this);
24548                     return;
24549                 }
24550                 
24551              };
24552         }
24553     }(),
24554     
24555     getAllAncestors: function()
24556     {
24557         var p = this.getSelectedNode();
24558         var a = [];
24559         if (!p) {
24560             a.push(p); // push blank onto stack..
24561             p = this.getParentElement();
24562         }
24563         
24564         
24565         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24566             a.push(p);
24567             p = p.parentNode;
24568         }
24569         a.push(this.doc.body);
24570         return a;
24571     },
24572     lastSel : false,
24573     lastSelNode : false,
24574     
24575     
24576     getSelection : function() 
24577     {
24578         this.assignDocWin();
24579         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24580     },
24581     
24582     getSelectedNode: function() 
24583     {
24584         // this may only work on Gecko!!!
24585         
24586         // should we cache this!!!!
24587         
24588         
24589         
24590          
24591         var range = this.createRange(this.getSelection()).cloneRange();
24592         
24593         if (Roo.isIE) {
24594             var parent = range.parentElement();
24595             while (true) {
24596                 var testRange = range.duplicate();
24597                 testRange.moveToElementText(parent);
24598                 if (testRange.inRange(range)) {
24599                     break;
24600                 }
24601                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24602                     break;
24603                 }
24604                 parent = parent.parentElement;
24605             }
24606             return parent;
24607         }
24608         
24609         // is ancestor a text element.
24610         var ac =  range.commonAncestorContainer;
24611         if (ac.nodeType == 3) {
24612             ac = ac.parentNode;
24613         }
24614         
24615         var ar = ac.childNodes;
24616          
24617         var nodes = [];
24618         var other_nodes = [];
24619         var has_other_nodes = false;
24620         for (var i=0;i<ar.length;i++) {
24621             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24622                 continue;
24623             }
24624             // fullly contained node.
24625             
24626             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24627                 nodes.push(ar[i]);
24628                 continue;
24629             }
24630             
24631             // probably selected..
24632             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24633                 other_nodes.push(ar[i]);
24634                 continue;
24635             }
24636             // outer..
24637             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24638                 continue;
24639             }
24640             
24641             
24642             has_other_nodes = true;
24643         }
24644         if (!nodes.length && other_nodes.length) {
24645             nodes= other_nodes;
24646         }
24647         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24648             return false;
24649         }
24650         
24651         return nodes[0];
24652     },
24653     createRange: function(sel)
24654     {
24655         // this has strange effects when using with 
24656         // top toolbar - not sure if it's a great idea.
24657         //this.editor.contentWindow.focus();
24658         if (typeof sel != "undefined") {
24659             try {
24660                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24661             } catch(e) {
24662                 return this.doc.createRange();
24663             }
24664         } else {
24665             return this.doc.createRange();
24666         }
24667     },
24668     getParentElement: function()
24669     {
24670         
24671         this.assignDocWin();
24672         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24673         
24674         var range = this.createRange(sel);
24675          
24676         try {
24677             var p = range.commonAncestorContainer;
24678             while (p.nodeType == 3) { // text node
24679                 p = p.parentNode;
24680             }
24681             return p;
24682         } catch (e) {
24683             return null;
24684         }
24685     
24686     },
24687     /***
24688      *
24689      * Range intersection.. the hard stuff...
24690      *  '-1' = before
24691      *  '0' = hits..
24692      *  '1' = after.
24693      *         [ -- selected range --- ]
24694      *   [fail]                        [fail]
24695      *
24696      *    basically..
24697      *      if end is before start or  hits it. fail.
24698      *      if start is after end or hits it fail.
24699      *
24700      *   if either hits (but other is outside. - then it's not 
24701      *   
24702      *    
24703      **/
24704     
24705     
24706     // @see http://www.thismuchiknow.co.uk/?p=64.
24707     rangeIntersectsNode : function(range, node)
24708     {
24709         var nodeRange = node.ownerDocument.createRange();
24710         try {
24711             nodeRange.selectNode(node);
24712         } catch (e) {
24713             nodeRange.selectNodeContents(node);
24714         }
24715     
24716         var rangeStartRange = range.cloneRange();
24717         rangeStartRange.collapse(true);
24718     
24719         var rangeEndRange = range.cloneRange();
24720         rangeEndRange.collapse(false);
24721     
24722         var nodeStartRange = nodeRange.cloneRange();
24723         nodeStartRange.collapse(true);
24724     
24725         var nodeEndRange = nodeRange.cloneRange();
24726         nodeEndRange.collapse(false);
24727     
24728         return rangeStartRange.compareBoundaryPoints(
24729                  Range.START_TO_START, nodeEndRange) == -1 &&
24730                rangeEndRange.compareBoundaryPoints(
24731                  Range.START_TO_START, nodeStartRange) == 1;
24732         
24733          
24734     },
24735     rangeCompareNode : function(range, node)
24736     {
24737         var nodeRange = node.ownerDocument.createRange();
24738         try {
24739             nodeRange.selectNode(node);
24740         } catch (e) {
24741             nodeRange.selectNodeContents(node);
24742         }
24743         
24744         
24745         range.collapse(true);
24746     
24747         nodeRange.collapse(true);
24748      
24749         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24750         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24751          
24752         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24753         
24754         var nodeIsBefore   =  ss == 1;
24755         var nodeIsAfter    = ee == -1;
24756         
24757         if (nodeIsBefore && nodeIsAfter) {
24758             return 0; // outer
24759         }
24760         if (!nodeIsBefore && nodeIsAfter) {
24761             return 1; //right trailed.
24762         }
24763         
24764         if (nodeIsBefore && !nodeIsAfter) {
24765             return 2;  // left trailed.
24766         }
24767         // fully contined.
24768         return 3;
24769     },
24770
24771     // private? - in a new class?
24772     cleanUpPaste :  function()
24773     {
24774         // cleans up the whole document..
24775         Roo.log('cleanuppaste');
24776         
24777         this.cleanUpChildren(this.doc.body);
24778         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24779         if (clean != this.doc.body.innerHTML) {
24780             this.doc.body.innerHTML = clean;
24781         }
24782         
24783     },
24784     
24785     cleanWordChars : function(input) {// change the chars to hex code
24786         var he = Roo.HtmlEditorCore;
24787         
24788         var output = input;
24789         Roo.each(he.swapCodes, function(sw) { 
24790             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24791             
24792             output = output.replace(swapper, sw[1]);
24793         });
24794         
24795         return output;
24796     },
24797     
24798     
24799     cleanUpChildren : function (n)
24800     {
24801         if (!n.childNodes.length) {
24802             return;
24803         }
24804         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24805            this.cleanUpChild(n.childNodes[i]);
24806         }
24807     },
24808     
24809     
24810         
24811     
24812     cleanUpChild : function (node)
24813     {
24814         var ed = this;
24815         //console.log(node);
24816         if (node.nodeName == "#text") {
24817             // clean up silly Windows -- stuff?
24818             return; 
24819         }
24820         if (node.nodeName == "#comment") {
24821             node.parentNode.removeChild(node);
24822             // clean up silly Windows -- stuff?
24823             return; 
24824         }
24825         var lcname = node.tagName.toLowerCase();
24826         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24827         // whitelist of tags..
24828         
24829         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24830             // remove node.
24831             node.parentNode.removeChild(node);
24832             return;
24833             
24834         }
24835         
24836         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24837         
24838         // spans with no attributes - just remove them..
24839         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24840             remove_keep_children = true;
24841         }
24842         
24843         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24844         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24845         
24846         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24847         //    remove_keep_children = true;
24848         //}
24849         
24850         if (remove_keep_children) {
24851             this.cleanUpChildren(node);
24852             // inserts everything just before this node...
24853             while (node.childNodes.length) {
24854                 var cn = node.childNodes[0];
24855                 node.removeChild(cn);
24856                 node.parentNode.insertBefore(cn, node);
24857             }
24858             node.parentNode.removeChild(node);
24859             return;
24860         }
24861         
24862         if (!node.attributes || !node.attributes.length) {
24863             
24864           
24865             
24866             
24867             this.cleanUpChildren(node);
24868             return;
24869         }
24870         
24871         function cleanAttr(n,v)
24872         {
24873             
24874             if (v.match(/^\./) || v.match(/^\//)) {
24875                 return;
24876             }
24877             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24878                 return;
24879             }
24880             if (v.match(/^#/)) {
24881                 return;
24882             }
24883             if (v.match(/^\{/)) { // allow template editing.
24884                 return;
24885             }
24886 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24887             node.removeAttribute(n);
24888             
24889         }
24890         
24891         var cwhite = this.cwhite;
24892         var cblack = this.cblack;
24893             
24894         function cleanStyle(n,v)
24895         {
24896             if (v.match(/expression/)) { //XSS?? should we even bother..
24897                 node.removeAttribute(n);
24898                 return;
24899             }
24900             
24901             var parts = v.split(/;/);
24902             var clean = [];
24903             
24904             Roo.each(parts, function(p) {
24905                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24906                 if (!p.length) {
24907                     return true;
24908                 }
24909                 var l = p.split(':').shift().replace(/\s+/g,'');
24910                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24911                 
24912                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24913 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24914                     //node.removeAttribute(n);
24915                     return true;
24916                 }
24917                 //Roo.log()
24918                 // only allow 'c whitelisted system attributes'
24919                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24920 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24921                     //node.removeAttribute(n);
24922                     return true;
24923                 }
24924                 
24925                 
24926                  
24927                 
24928                 clean.push(p);
24929                 return true;
24930             });
24931             if (clean.length) { 
24932                 node.setAttribute(n, clean.join(';'));
24933             } else {
24934                 node.removeAttribute(n);
24935             }
24936             
24937         }
24938         
24939         
24940         for (var i = node.attributes.length-1; i > -1 ; i--) {
24941             var a = node.attributes[i];
24942             //console.log(a);
24943             
24944             if (a.name.toLowerCase().substr(0,2)=='on')  {
24945                 node.removeAttribute(a.name);
24946                 continue;
24947             }
24948             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24949                 node.removeAttribute(a.name);
24950                 continue;
24951             }
24952             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24953                 cleanAttr(a.name,a.value); // fixme..
24954                 continue;
24955             }
24956             if (a.name == 'style') {
24957                 cleanStyle(a.name,a.value);
24958                 continue;
24959             }
24960             /// clean up MS crap..
24961             // tecnically this should be a list of valid class'es..
24962             
24963             
24964             if (a.name == 'class') {
24965                 if (a.value.match(/^Mso/)) {
24966                     node.removeAttribute('class');
24967                 }
24968                 
24969                 if (a.value.match(/^body$/)) {
24970                     node.removeAttribute('class');
24971                 }
24972                 continue;
24973             }
24974             
24975             // style cleanup!?
24976             // class cleanup?
24977             
24978         }
24979         
24980         
24981         this.cleanUpChildren(node);
24982         
24983         
24984     },
24985     
24986     /**
24987      * Clean up MS wordisms...
24988      */
24989     cleanWord : function(node)
24990     {
24991         if (!node) {
24992             this.cleanWord(this.doc.body);
24993             return;
24994         }
24995         
24996         if(
24997                 node.nodeName == 'SPAN' &&
24998                 !node.hasAttributes() &&
24999                 node.childNodes.length == 1 &&
25000                 node.firstChild.nodeName == "#text"  
25001         ) {
25002             var textNode = node.firstChild;
25003             node.removeChild(textNode);
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.insertBefore(textNode, node);
25008             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25009                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25010             }
25011             node.parentNode.removeChild(node);
25012         }
25013         
25014         if (node.nodeName == "#text") {
25015             // clean up silly Windows -- stuff?
25016             return; 
25017         }
25018         if (node.nodeName == "#comment") {
25019             node.parentNode.removeChild(node);
25020             // clean up silly Windows -- stuff?
25021             return; 
25022         }
25023         
25024         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25025             node.parentNode.removeChild(node);
25026             return;
25027         }
25028         //Roo.log(node.tagName);
25029         // remove - but keep children..
25030         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25031             //Roo.log('-- removed');
25032             while (node.childNodes.length) {
25033                 var cn = node.childNodes[0];
25034                 node.removeChild(cn);
25035                 node.parentNode.insertBefore(cn, node);
25036                 // move node to parent - and clean it..
25037                 this.cleanWord(cn);
25038             }
25039             node.parentNode.removeChild(node);
25040             /// no need to iterate chidlren = it's got none..
25041             //this.iterateChildren(node, this.cleanWord);
25042             return;
25043         }
25044         // clean styles
25045         if (node.className.length) {
25046             
25047             var cn = node.className.split(/\W+/);
25048             var cna = [];
25049             Roo.each(cn, function(cls) {
25050                 if (cls.match(/Mso[a-zA-Z]+/)) {
25051                     return;
25052                 }
25053                 cna.push(cls);
25054             });
25055             node.className = cna.length ? cna.join(' ') : '';
25056             if (!cna.length) {
25057                 node.removeAttribute("class");
25058             }
25059         }
25060         
25061         if (node.hasAttribute("lang")) {
25062             node.removeAttribute("lang");
25063         }
25064         
25065         if (node.hasAttribute("style")) {
25066             
25067             var styles = node.getAttribute("style").split(";");
25068             var nstyle = [];
25069             Roo.each(styles, function(s) {
25070                 if (!s.match(/:/)) {
25071                     return;
25072                 }
25073                 var kv = s.split(":");
25074                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25075                     return;
25076                 }
25077                 // what ever is left... we allow.
25078                 nstyle.push(s);
25079             });
25080             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25081             if (!nstyle.length) {
25082                 node.removeAttribute('style');
25083             }
25084         }
25085         this.iterateChildren(node, this.cleanWord);
25086         
25087         
25088         
25089     },
25090     /**
25091      * iterateChildren of a Node, calling fn each time, using this as the scole..
25092      * @param {DomNode} node node to iterate children of.
25093      * @param {Function} fn method of this class to call on each item.
25094      */
25095     iterateChildren : function(node, fn)
25096     {
25097         if (!node.childNodes.length) {
25098                 return;
25099         }
25100         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25101            fn.call(this, node.childNodes[i])
25102         }
25103     },
25104     
25105     
25106     /**
25107      * cleanTableWidths.
25108      *
25109      * Quite often pasting from word etc.. results in tables with column and widths.
25110      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25111      *
25112      */
25113     cleanTableWidths : function(node)
25114     {
25115          
25116          
25117         if (!node) {
25118             this.cleanTableWidths(this.doc.body);
25119             return;
25120         }
25121         
25122         // ignore list...
25123         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25124             return; 
25125         }
25126         Roo.log(node.tagName);
25127         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25128             this.iterateChildren(node, this.cleanTableWidths);
25129             return;
25130         }
25131         if (node.hasAttribute('width')) {
25132             node.removeAttribute('width');
25133         }
25134         
25135          
25136         if (node.hasAttribute("style")) {
25137             // pretty basic...
25138             
25139             var styles = node.getAttribute("style").split(";");
25140             var nstyle = [];
25141             Roo.each(styles, function(s) {
25142                 if (!s.match(/:/)) {
25143                     return;
25144                 }
25145                 var kv = s.split(":");
25146                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25147                     return;
25148                 }
25149                 // what ever is left... we allow.
25150                 nstyle.push(s);
25151             });
25152             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25153             if (!nstyle.length) {
25154                 node.removeAttribute('style');
25155             }
25156         }
25157         
25158         this.iterateChildren(node, this.cleanTableWidths);
25159         
25160         
25161     },
25162     
25163     
25164     
25165     
25166     domToHTML : function(currentElement, depth, nopadtext) {
25167         
25168         depth = depth || 0;
25169         nopadtext = nopadtext || false;
25170     
25171         if (!currentElement) {
25172             return this.domToHTML(this.doc.body);
25173         }
25174         
25175         //Roo.log(currentElement);
25176         var j;
25177         var allText = false;
25178         var nodeName = currentElement.nodeName;
25179         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25180         
25181         if  (nodeName == '#text') {
25182             
25183             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25184         }
25185         
25186         
25187         var ret = '';
25188         if (nodeName != 'BODY') {
25189              
25190             var i = 0;
25191             // Prints the node tagName, such as <A>, <IMG>, etc
25192             if (tagName) {
25193                 var attr = [];
25194                 for(i = 0; i < currentElement.attributes.length;i++) {
25195                     // quoting?
25196                     var aname = currentElement.attributes.item(i).name;
25197                     if (!currentElement.attributes.item(i).value.length) {
25198                         continue;
25199                     }
25200                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25201                 }
25202                 
25203                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25204             } 
25205             else {
25206                 
25207                 // eack
25208             }
25209         } else {
25210             tagName = false;
25211         }
25212         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25213             return ret;
25214         }
25215         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25216             nopadtext = true;
25217         }
25218         
25219         
25220         // Traverse the tree
25221         i = 0;
25222         var currentElementChild = currentElement.childNodes.item(i);
25223         var allText = true;
25224         var innerHTML  = '';
25225         lastnode = '';
25226         while (currentElementChild) {
25227             // Formatting code (indent the tree so it looks nice on the screen)
25228             var nopad = nopadtext;
25229             if (lastnode == 'SPAN') {
25230                 nopad  = true;
25231             }
25232             // text
25233             if  (currentElementChild.nodeName == '#text') {
25234                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25235                 toadd = nopadtext ? toadd : toadd.trim();
25236                 if (!nopad && toadd.length > 80) {
25237                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25238                 }
25239                 innerHTML  += toadd;
25240                 
25241                 i++;
25242                 currentElementChild = currentElement.childNodes.item(i);
25243                 lastNode = '';
25244                 continue;
25245             }
25246             allText = false;
25247             
25248             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25249                 
25250             // Recursively traverse the tree structure of the child node
25251             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25252             lastnode = currentElementChild.nodeName;
25253             i++;
25254             currentElementChild=currentElement.childNodes.item(i);
25255         }
25256         
25257         ret += innerHTML;
25258         
25259         if (!allText) {
25260                 // The remaining code is mostly for formatting the tree
25261             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25262         }
25263         
25264         
25265         if (tagName) {
25266             ret+= "</"+tagName+">";
25267         }
25268         return ret;
25269         
25270     },
25271         
25272     applyBlacklists : function()
25273     {
25274         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25275         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25276         
25277         this.white = [];
25278         this.black = [];
25279         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25280             if (b.indexOf(tag) > -1) {
25281                 return;
25282             }
25283             this.white.push(tag);
25284             
25285         }, this);
25286         
25287         Roo.each(w, function(tag) {
25288             if (b.indexOf(tag) > -1) {
25289                 return;
25290             }
25291             if (this.white.indexOf(tag) > -1) {
25292                 return;
25293             }
25294             this.white.push(tag);
25295             
25296         }, this);
25297         
25298         
25299         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25300             if (w.indexOf(tag) > -1) {
25301                 return;
25302             }
25303             this.black.push(tag);
25304             
25305         }, this);
25306         
25307         Roo.each(b, function(tag) {
25308             if (w.indexOf(tag) > -1) {
25309                 return;
25310             }
25311             if (this.black.indexOf(tag) > -1) {
25312                 return;
25313             }
25314             this.black.push(tag);
25315             
25316         }, this);
25317         
25318         
25319         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25320         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25321         
25322         this.cwhite = [];
25323         this.cblack = [];
25324         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25325             if (b.indexOf(tag) > -1) {
25326                 return;
25327             }
25328             this.cwhite.push(tag);
25329             
25330         }, this);
25331         
25332         Roo.each(w, function(tag) {
25333             if (b.indexOf(tag) > -1) {
25334                 return;
25335             }
25336             if (this.cwhite.indexOf(tag) > -1) {
25337                 return;
25338             }
25339             this.cwhite.push(tag);
25340             
25341         }, this);
25342         
25343         
25344         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25345             if (w.indexOf(tag) > -1) {
25346                 return;
25347             }
25348             this.cblack.push(tag);
25349             
25350         }, this);
25351         
25352         Roo.each(b, function(tag) {
25353             if (w.indexOf(tag) > -1) {
25354                 return;
25355             }
25356             if (this.cblack.indexOf(tag) > -1) {
25357                 return;
25358             }
25359             this.cblack.push(tag);
25360             
25361         }, this);
25362     },
25363     
25364     setStylesheets : function(stylesheets)
25365     {
25366         if(typeof(stylesheets) == 'string'){
25367             Roo.get(this.iframe.contentDocument.head).createChild({
25368                 tag : 'link',
25369                 rel : 'stylesheet',
25370                 type : 'text/css',
25371                 href : stylesheets
25372             });
25373             
25374             return;
25375         }
25376         var _this = this;
25377      
25378         Roo.each(stylesheets, function(s) {
25379             if(!s.length){
25380                 return;
25381             }
25382             
25383             Roo.get(_this.iframe.contentDocument.head).createChild({
25384                 tag : 'link',
25385                 rel : 'stylesheet',
25386                 type : 'text/css',
25387                 href : s
25388             });
25389         });
25390
25391         
25392     },
25393     
25394     removeStylesheets : function()
25395     {
25396         var _this = this;
25397         
25398         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25399             s.remove();
25400         });
25401     },
25402     
25403     setStyle : function(style)
25404     {
25405         Roo.get(this.iframe.contentDocument.head).createChild({
25406             tag : 'style',
25407             type : 'text/css',
25408             html : style
25409         });
25410
25411         return;
25412     }
25413     
25414     // hide stuff that is not compatible
25415     /**
25416      * @event blur
25417      * @hide
25418      */
25419     /**
25420      * @event change
25421      * @hide
25422      */
25423     /**
25424      * @event focus
25425      * @hide
25426      */
25427     /**
25428      * @event specialkey
25429      * @hide
25430      */
25431     /**
25432      * @cfg {String} fieldClass @hide
25433      */
25434     /**
25435      * @cfg {String} focusClass @hide
25436      */
25437     /**
25438      * @cfg {String} autoCreate @hide
25439      */
25440     /**
25441      * @cfg {String} inputType @hide
25442      */
25443     /**
25444      * @cfg {String} invalidClass @hide
25445      */
25446     /**
25447      * @cfg {String} invalidText @hide
25448      */
25449     /**
25450      * @cfg {String} msgFx @hide
25451      */
25452     /**
25453      * @cfg {String} validateOnBlur @hide
25454      */
25455 });
25456
25457 Roo.HtmlEditorCore.white = [
25458         'area', 'br', 'img', 'input', 'hr', 'wbr',
25459         
25460        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25461        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25462        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25463        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25464        'table',   'ul',         'xmp', 
25465        
25466        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25467       'thead',   'tr', 
25468      
25469       'dir', 'menu', 'ol', 'ul', 'dl',
25470        
25471       'embed',  'object'
25472 ];
25473
25474
25475 Roo.HtmlEditorCore.black = [
25476     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25477         'applet', // 
25478         'base',   'basefont', 'bgsound', 'blink',  'body', 
25479         'frame',  'frameset', 'head',    'html',   'ilayer', 
25480         'iframe', 'layer',  'link',     'meta',    'object',   
25481         'script', 'style' ,'title',  'xml' // clean later..
25482 ];
25483 Roo.HtmlEditorCore.clean = [
25484     'script', 'style', 'title', 'xml'
25485 ];
25486 Roo.HtmlEditorCore.remove = [
25487     'font'
25488 ];
25489 // attributes..
25490
25491 Roo.HtmlEditorCore.ablack = [
25492     'on'
25493 ];
25494     
25495 Roo.HtmlEditorCore.aclean = [ 
25496     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25497 ];
25498
25499 // protocols..
25500 Roo.HtmlEditorCore.pwhite= [
25501         'http',  'https',  'mailto'
25502 ];
25503
25504 // white listed style attributes.
25505 Roo.HtmlEditorCore.cwhite= [
25506       //  'text-align', /// default is to allow most things..
25507       
25508          
25509 //        'font-size'//??
25510 ];
25511
25512 // black listed style attributes.
25513 Roo.HtmlEditorCore.cblack= [
25514       //  'font-size' -- this can be set by the project 
25515 ];
25516
25517
25518 Roo.HtmlEditorCore.swapCodes   =[ 
25519     [    8211, "--" ], 
25520     [    8212, "--" ], 
25521     [    8216,  "'" ],  
25522     [    8217, "'" ],  
25523     [    8220, '"' ],  
25524     [    8221, '"' ],  
25525     [    8226, "*" ],  
25526     [    8230, "..." ]
25527 ]; 
25528
25529     /*
25530  * - LGPL
25531  *
25532  * HtmlEditor
25533  * 
25534  */
25535
25536 /**
25537  * @class Roo.bootstrap.HtmlEditor
25538  * @extends Roo.bootstrap.TextArea
25539  * Bootstrap HtmlEditor class
25540
25541  * @constructor
25542  * Create a new HtmlEditor
25543  * @param {Object} config The config object
25544  */
25545
25546 Roo.bootstrap.HtmlEditor = function(config){
25547     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25548     if (!this.toolbars) {
25549         this.toolbars = [];
25550     }
25551     
25552     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25553     this.addEvents({
25554             /**
25555              * @event initialize
25556              * Fires when the editor is fully initialized (including the iframe)
25557              * @param {HtmlEditor} this
25558              */
25559             initialize: true,
25560             /**
25561              * @event activate
25562              * Fires when the editor is first receives the focus. Any insertion must wait
25563              * until after this event.
25564              * @param {HtmlEditor} this
25565              */
25566             activate: true,
25567              /**
25568              * @event beforesync
25569              * Fires before the textarea is updated with content from the editor iframe. Return false
25570              * to cancel the sync.
25571              * @param {HtmlEditor} this
25572              * @param {String} html
25573              */
25574             beforesync: true,
25575              /**
25576              * @event beforepush
25577              * Fires before the iframe editor is updated with content from the textarea. Return false
25578              * to cancel the push.
25579              * @param {HtmlEditor} this
25580              * @param {String} html
25581              */
25582             beforepush: true,
25583              /**
25584              * @event sync
25585              * Fires when the textarea is updated with content from the editor iframe.
25586              * @param {HtmlEditor} this
25587              * @param {String} html
25588              */
25589             sync: true,
25590              /**
25591              * @event push
25592              * Fires when the iframe editor is updated with content from the textarea.
25593              * @param {HtmlEditor} this
25594              * @param {String} html
25595              */
25596             push: true,
25597              /**
25598              * @event editmodechange
25599              * Fires when the editor switches edit modes
25600              * @param {HtmlEditor} this
25601              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25602              */
25603             editmodechange: true,
25604             /**
25605              * @event editorevent
25606              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25607              * @param {HtmlEditor} this
25608              */
25609             editorevent: true,
25610             /**
25611              * @event firstfocus
25612              * Fires when on first focus - needed by toolbars..
25613              * @param {HtmlEditor} this
25614              */
25615             firstfocus: true,
25616             /**
25617              * @event autosave
25618              * Auto save the htmlEditor value as a file into Events
25619              * @param {HtmlEditor} this
25620              */
25621             autosave: true,
25622             /**
25623              * @event savedpreview
25624              * preview the saved version of htmlEditor
25625              * @param {HtmlEditor} this
25626              */
25627             savedpreview: true
25628         });
25629 };
25630
25631
25632 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25633     
25634     
25635       /**
25636      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25637      */
25638     toolbars : false,
25639     
25640      /**
25641     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25642     */
25643     btns : [],
25644    
25645      /**
25646      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25647      *                        Roo.resizable.
25648      */
25649     resizable : false,
25650      /**
25651      * @cfg {Number} height (in pixels)
25652      */   
25653     height: 300,
25654    /**
25655      * @cfg {Number} width (in pixels)
25656      */   
25657     width: false,
25658     
25659     /**
25660      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25661      * 
25662      */
25663     stylesheets: false,
25664     
25665     // id of frame..
25666     frameId: false,
25667     
25668     // private properties
25669     validationEvent : false,
25670     deferHeight: true,
25671     initialized : false,
25672     activated : false,
25673     
25674     onFocus : Roo.emptyFn,
25675     iframePad:3,
25676     hideMode:'offsets',
25677     
25678     tbContainer : false,
25679     
25680     bodyCls : '',
25681     
25682     toolbarContainer :function() {
25683         return this.wrap.select('.x-html-editor-tb',true).first();
25684     },
25685
25686     /**
25687      * Protected method that will not generally be called directly. It
25688      * is called when the editor creates its toolbar. Override this method if you need to
25689      * add custom toolbar buttons.
25690      * @param {HtmlEditor} editor
25691      */
25692     createToolbar : function(){
25693         Roo.log('renewing');
25694         Roo.log("create toolbars");
25695         
25696         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25697         this.toolbars[0].render(this.toolbarContainer());
25698         
25699         return;
25700         
25701 //        if (!editor.toolbars || !editor.toolbars.length) {
25702 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25703 //        }
25704 //        
25705 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25706 //            editor.toolbars[i] = Roo.factory(
25707 //                    typeof(editor.toolbars[i]) == 'string' ?
25708 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25709 //                Roo.bootstrap.HtmlEditor);
25710 //            editor.toolbars[i].init(editor);
25711 //        }
25712     },
25713
25714      
25715     // private
25716     onRender : function(ct, position)
25717     {
25718        // Roo.log("Call onRender: " + this.xtype);
25719         var _t = this;
25720         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25721       
25722         this.wrap = this.inputEl().wrap({
25723             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25724         });
25725         
25726         this.editorcore.onRender(ct, position);
25727          
25728         if (this.resizable) {
25729             this.resizeEl = new Roo.Resizable(this.wrap, {
25730                 pinned : true,
25731                 wrap: true,
25732                 dynamic : true,
25733                 minHeight : this.height,
25734                 height: this.height,
25735                 handles : this.resizable,
25736                 width: this.width,
25737                 listeners : {
25738                     resize : function(r, w, h) {
25739                         _t.onResize(w,h); // -something
25740                     }
25741                 }
25742             });
25743             
25744         }
25745         this.createToolbar(this);
25746        
25747         
25748         if(!this.width && this.resizable){
25749             this.setSize(this.wrap.getSize());
25750         }
25751         if (this.resizeEl) {
25752             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25753             // should trigger onReize..
25754         }
25755         
25756     },
25757
25758     // private
25759     onResize : function(w, h)
25760     {
25761         Roo.log('resize: ' +w + ',' + h );
25762         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25763         var ew = false;
25764         var eh = false;
25765         
25766         if(this.inputEl() ){
25767             if(typeof w == 'number'){
25768                 var aw = w - this.wrap.getFrameWidth('lr');
25769                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25770                 ew = aw;
25771             }
25772             if(typeof h == 'number'){
25773                  var tbh = -11;  // fixme it needs to tool bar size!
25774                 for (var i =0; i < this.toolbars.length;i++) {
25775                     // fixme - ask toolbars for heights?
25776                     tbh += this.toolbars[i].el.getHeight();
25777                     //if (this.toolbars[i].footer) {
25778                     //    tbh += this.toolbars[i].footer.el.getHeight();
25779                     //}
25780                 }
25781               
25782                 
25783                 
25784                 
25785                 
25786                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25787                 ah -= 5; // knock a few pixes off for look..
25788                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25789                 var eh = ah;
25790             }
25791         }
25792         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25793         this.editorcore.onResize(ew,eh);
25794         
25795     },
25796
25797     /**
25798      * Toggles the editor between standard and source edit mode.
25799      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25800      */
25801     toggleSourceEdit : function(sourceEditMode)
25802     {
25803         this.editorcore.toggleSourceEdit(sourceEditMode);
25804         
25805         if(this.editorcore.sourceEditMode){
25806             Roo.log('editor - showing textarea');
25807             
25808 //            Roo.log('in');
25809 //            Roo.log(this.syncValue());
25810             this.syncValue();
25811             this.inputEl().removeClass(['hide', 'x-hidden']);
25812             this.inputEl().dom.removeAttribute('tabIndex');
25813             this.inputEl().focus();
25814         }else{
25815             Roo.log('editor - hiding textarea');
25816 //            Roo.log('out')
25817 //            Roo.log(this.pushValue()); 
25818             this.pushValue();
25819             
25820             this.inputEl().addClass(['hide', 'x-hidden']);
25821             this.inputEl().dom.setAttribute('tabIndex', -1);
25822             //this.deferFocus();
25823         }
25824          
25825         if(this.resizable){
25826             this.setSize(this.wrap.getSize());
25827         }
25828         
25829         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25830     },
25831  
25832     // private (for BoxComponent)
25833     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25834
25835     // private (for BoxComponent)
25836     getResizeEl : function(){
25837         return this.wrap;
25838     },
25839
25840     // private (for BoxComponent)
25841     getPositionEl : function(){
25842         return this.wrap;
25843     },
25844
25845     // private
25846     initEvents : function(){
25847         this.originalValue = this.getValue();
25848     },
25849
25850 //    /**
25851 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25852 //     * @method
25853 //     */
25854 //    markInvalid : Roo.emptyFn,
25855 //    /**
25856 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25857 //     * @method
25858 //     */
25859 //    clearInvalid : Roo.emptyFn,
25860
25861     setValue : function(v){
25862         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25863         this.editorcore.pushValue();
25864     },
25865
25866      
25867     // private
25868     deferFocus : function(){
25869         this.focus.defer(10, this);
25870     },
25871
25872     // doc'ed in Field
25873     focus : function(){
25874         this.editorcore.focus();
25875         
25876     },
25877       
25878
25879     // private
25880     onDestroy : function(){
25881         
25882         
25883         
25884         if(this.rendered){
25885             
25886             for (var i =0; i < this.toolbars.length;i++) {
25887                 // fixme - ask toolbars for heights?
25888                 this.toolbars[i].onDestroy();
25889             }
25890             
25891             this.wrap.dom.innerHTML = '';
25892             this.wrap.remove();
25893         }
25894     },
25895
25896     // private
25897     onFirstFocus : function(){
25898         //Roo.log("onFirstFocus");
25899         this.editorcore.onFirstFocus();
25900          for (var i =0; i < this.toolbars.length;i++) {
25901             this.toolbars[i].onFirstFocus();
25902         }
25903         
25904     },
25905     
25906     // private
25907     syncValue : function()
25908     {   
25909         this.editorcore.syncValue();
25910     },
25911     
25912     pushValue : function()
25913     {   
25914         this.editorcore.pushValue();
25915     }
25916      
25917     
25918     // hide stuff that is not compatible
25919     /**
25920      * @event blur
25921      * @hide
25922      */
25923     /**
25924      * @event change
25925      * @hide
25926      */
25927     /**
25928      * @event focus
25929      * @hide
25930      */
25931     /**
25932      * @event specialkey
25933      * @hide
25934      */
25935     /**
25936      * @cfg {String} fieldClass @hide
25937      */
25938     /**
25939      * @cfg {String} focusClass @hide
25940      */
25941     /**
25942      * @cfg {String} autoCreate @hide
25943      */
25944     /**
25945      * @cfg {String} inputType @hide
25946      */
25947      
25948     /**
25949      * @cfg {String} invalidText @hide
25950      */
25951     /**
25952      * @cfg {String} msgFx @hide
25953      */
25954     /**
25955      * @cfg {String} validateOnBlur @hide
25956      */
25957 });
25958  
25959     
25960    
25961    
25962    
25963       
25964 Roo.namespace('Roo.bootstrap.htmleditor');
25965 /**
25966  * @class Roo.bootstrap.HtmlEditorToolbar1
25967  * Basic Toolbar
25968  * 
25969  * @example
25970  * Usage:
25971  *
25972  new Roo.bootstrap.HtmlEditor({
25973     ....
25974     toolbars : [
25975         new Roo.bootstrap.HtmlEditorToolbar1({
25976             disable : { fonts: 1 , format: 1, ..., ... , ...],
25977             btns : [ .... ]
25978         })
25979     }
25980      
25981  * 
25982  * @cfg {Object} disable List of elements to disable..
25983  * @cfg {Array} btns List of additional buttons.
25984  * 
25985  * 
25986  * NEEDS Extra CSS? 
25987  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25988  */
25989  
25990 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25991 {
25992     
25993     Roo.apply(this, config);
25994     
25995     // default disabled, based on 'good practice'..
25996     this.disable = this.disable || {};
25997     Roo.applyIf(this.disable, {
25998         fontSize : true,
25999         colors : true,
26000         specialElements : true
26001     });
26002     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26003     
26004     this.editor = config.editor;
26005     this.editorcore = config.editor.editorcore;
26006     
26007     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26008     
26009     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26010     // dont call parent... till later.
26011 }
26012 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26013      
26014     bar : true,
26015     
26016     editor : false,
26017     editorcore : false,
26018     
26019     
26020     formats : [
26021         "p" ,  
26022         "h1","h2","h3","h4","h5","h6", 
26023         "pre", "code", 
26024         "abbr", "acronym", "address", "cite", "samp", "var",
26025         'div','span'
26026     ],
26027     
26028     onRender : function(ct, position)
26029     {
26030        // Roo.log("Call onRender: " + this.xtype);
26031         
26032        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26033        Roo.log(this.el);
26034        this.el.dom.style.marginBottom = '0';
26035        var _this = this;
26036        var editorcore = this.editorcore;
26037        var editor= this.editor;
26038        
26039        var children = [];
26040        var btn = function(id,cmd , toggle, handler, html){
26041        
26042             var  event = toggle ? 'toggle' : 'click';
26043        
26044             var a = {
26045                 size : 'sm',
26046                 xtype: 'Button',
26047                 xns: Roo.bootstrap,
26048                 //glyphicon : id,
26049                 fa: id,
26050                 cmd : id || cmd,
26051                 enableToggle:toggle !== false,
26052                 html : html || '',
26053                 pressed : toggle ? false : null,
26054                 listeners : {}
26055             };
26056             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26057                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26058             };
26059             children.push(a);
26060             return a;
26061        }
26062        
26063     //    var cb_box = function...
26064         
26065         var style = {
26066                 xtype: 'Button',
26067                 size : 'sm',
26068                 xns: Roo.bootstrap,
26069                 fa : 'font',
26070                 //html : 'submit'
26071                 menu : {
26072                     xtype: 'Menu',
26073                     xns: Roo.bootstrap,
26074                     items:  []
26075                 }
26076         };
26077         Roo.each(this.formats, function(f) {
26078             style.menu.items.push({
26079                 xtype :'MenuItem',
26080                 xns: Roo.bootstrap,
26081                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26082                 tagname : f,
26083                 listeners : {
26084                     click : function()
26085                     {
26086                         editorcore.insertTag(this.tagname);
26087                         editor.focus();
26088                     }
26089                 }
26090                 
26091             });
26092         });
26093         children.push(style);   
26094         
26095         btn('bold',false,true);
26096         btn('italic',false,true);
26097         btn('align-left', 'justifyleft',true);
26098         btn('align-center', 'justifycenter',true);
26099         btn('align-right' , 'justifyright',true);
26100         btn('link', false, false, function(btn) {
26101             //Roo.log("create link?");
26102             var url = prompt(this.createLinkText, this.defaultLinkValue);
26103             if(url && url != 'http:/'+'/'){
26104                 this.editorcore.relayCmd('createlink', url);
26105             }
26106         }),
26107         btn('list','insertunorderedlist',true);
26108         btn('pencil', false,true, function(btn){
26109                 Roo.log(this);
26110                 this.toggleSourceEdit(btn.pressed);
26111         });
26112         
26113         if (this.editor.btns.length > 0) {
26114             for (var i = 0; i<this.editor.btns.length; i++) {
26115                 children.push(this.editor.btns[i]);
26116             }
26117         }
26118         
26119         /*
26120         var cog = {
26121                 xtype: 'Button',
26122                 size : 'sm',
26123                 xns: Roo.bootstrap,
26124                 glyphicon : 'cog',
26125                 //html : 'submit'
26126                 menu : {
26127                     xtype: 'Menu',
26128                     xns: Roo.bootstrap,
26129                     items:  []
26130                 }
26131         };
26132         
26133         cog.menu.items.push({
26134             xtype :'MenuItem',
26135             xns: Roo.bootstrap,
26136             html : Clean styles,
26137             tagname : f,
26138             listeners : {
26139                 click : function()
26140                 {
26141                     editorcore.insertTag(this.tagname);
26142                     editor.focus();
26143                 }
26144             }
26145             
26146         });
26147        */
26148         
26149          
26150        this.xtype = 'NavSimplebar';
26151         
26152         for(var i=0;i< children.length;i++) {
26153             
26154             this.buttons.add(this.addxtypeChild(children[i]));
26155             
26156         }
26157         
26158         editor.on('editorevent', this.updateToolbar, this);
26159     },
26160     onBtnClick : function(id)
26161     {
26162        this.editorcore.relayCmd(id);
26163        this.editorcore.focus();
26164     },
26165     
26166     /**
26167      * Protected method that will not generally be called directly. It triggers
26168      * a toolbar update by reading the markup state of the current selection in the editor.
26169      */
26170     updateToolbar: function(){
26171
26172         if(!this.editorcore.activated){
26173             this.editor.onFirstFocus(); // is this neeed?
26174             return;
26175         }
26176
26177         var btns = this.buttons; 
26178         var doc = this.editorcore.doc;
26179         btns.get('bold').setActive(doc.queryCommandState('bold'));
26180         btns.get('italic').setActive(doc.queryCommandState('italic'));
26181         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26182         
26183         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26184         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26185         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26186         
26187         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26188         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26189          /*
26190         
26191         var ans = this.editorcore.getAllAncestors();
26192         if (this.formatCombo) {
26193             
26194             
26195             var store = this.formatCombo.store;
26196             this.formatCombo.setValue("");
26197             for (var i =0; i < ans.length;i++) {
26198                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26199                     // select it..
26200                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26201                     break;
26202                 }
26203             }
26204         }
26205         
26206         
26207         
26208         // hides menus... - so this cant be on a menu...
26209         Roo.bootstrap.MenuMgr.hideAll();
26210         */
26211         Roo.bootstrap.MenuMgr.hideAll();
26212         //this.editorsyncValue();
26213     },
26214     onFirstFocus: function() {
26215         this.buttons.each(function(item){
26216            item.enable();
26217         });
26218     },
26219     toggleSourceEdit : function(sourceEditMode){
26220         
26221           
26222         if(sourceEditMode){
26223             Roo.log("disabling buttons");
26224            this.buttons.each( function(item){
26225                 if(item.cmd != 'pencil'){
26226                     item.disable();
26227                 }
26228             });
26229           
26230         }else{
26231             Roo.log("enabling buttons");
26232             if(this.editorcore.initialized){
26233                 this.buttons.each( function(item){
26234                     item.enable();
26235                 });
26236             }
26237             
26238         }
26239         Roo.log("calling toggole on editor");
26240         // tell the editor that it's been pressed..
26241         this.editor.toggleSourceEdit(sourceEditMode);
26242        
26243     }
26244 });
26245
26246
26247
26248
26249  
26250 /*
26251  * - LGPL
26252  */
26253
26254 /**
26255  * @class Roo.bootstrap.Markdown
26256  * @extends Roo.bootstrap.TextArea
26257  * Bootstrap Showdown editable area
26258  * @cfg {string} content
26259  * 
26260  * @constructor
26261  * Create a new Showdown
26262  */
26263
26264 Roo.bootstrap.Markdown = function(config){
26265     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26266    
26267 };
26268
26269 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26270     
26271     editing :false,
26272     
26273     initEvents : function()
26274     {
26275         
26276         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26277         this.markdownEl = this.el.createChild({
26278             cls : 'roo-markdown-area'
26279         });
26280         this.inputEl().addClass('d-none');
26281         if (this.getValue() == '') {
26282             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26283             
26284         } else {
26285             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26286         }
26287         this.markdownEl.on('click', this.toggleTextEdit, this);
26288         this.on('blur', this.toggleTextEdit, this);
26289         this.on('specialkey', this.resizeTextArea, this);
26290     },
26291     
26292     toggleTextEdit : function()
26293     {
26294         var sh = this.markdownEl.getHeight();
26295         this.inputEl().addClass('d-none');
26296         this.markdownEl.addClass('d-none');
26297         if (!this.editing) {
26298             // show editor?
26299             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26300             this.inputEl().removeClass('d-none');
26301             this.inputEl().focus();
26302             this.editing = true;
26303             return;
26304         }
26305         // show showdown...
26306         this.updateMarkdown();
26307         this.markdownEl.removeClass('d-none');
26308         this.editing = false;
26309         return;
26310     },
26311     updateMarkdown : function()
26312     {
26313         if (this.getValue() == '') {
26314             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26315             return;
26316         }
26317  
26318         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26319     },
26320     
26321     resizeTextArea: function () {
26322         
26323         var sh = 100;
26324         Roo.log([sh, this.getValue().split("\n").length * 30]);
26325         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26326     },
26327     setValue : function(val)
26328     {
26329         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26330         if (!this.editing) {
26331             this.updateMarkdown();
26332         }
26333         
26334     },
26335     focus : function()
26336     {
26337         if (!this.editing) {
26338             this.toggleTextEdit();
26339         }
26340         
26341     }
26342
26343
26344 });
26345 /**
26346  * @class Roo.bootstrap.Table.AbstractSelectionModel
26347  * @extends Roo.util.Observable
26348  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26349  * implemented by descendant classes.  This class should not be directly instantiated.
26350  * @constructor
26351  */
26352 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26353     this.locked = false;
26354     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26355 };
26356
26357
26358 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26359     /** @ignore Called by the grid automatically. Do not call directly. */
26360     init : function(grid){
26361         this.grid = grid;
26362         this.initEvents();
26363     },
26364
26365     /**
26366      * Locks the selections.
26367      */
26368     lock : function(){
26369         this.locked = true;
26370     },
26371
26372     /**
26373      * Unlocks the selections.
26374      */
26375     unlock : function(){
26376         this.locked = false;
26377     },
26378
26379     /**
26380      * Returns true if the selections are locked.
26381      * @return {Boolean}
26382      */
26383     isLocked : function(){
26384         return this.locked;
26385     },
26386     
26387     
26388     initEvents : function ()
26389     {
26390         
26391     }
26392 });
26393 /**
26394  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26395  * @class Roo.bootstrap.Table.RowSelectionModel
26396  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26397  * It supports multiple selections and keyboard selection/navigation. 
26398  * @constructor
26399  * @param {Object} config
26400  */
26401
26402 Roo.bootstrap.Table.RowSelectionModel = function(config){
26403     Roo.apply(this, config);
26404     this.selections = new Roo.util.MixedCollection(false, function(o){
26405         return o.id;
26406     });
26407
26408     this.last = false;
26409     this.lastActive = false;
26410
26411     this.addEvents({
26412         /**
26413              * @event selectionchange
26414              * Fires when the selection changes
26415              * @param {SelectionModel} this
26416              */
26417             "selectionchange" : true,
26418         /**
26419              * @event afterselectionchange
26420              * Fires after the selection changes (eg. by key press or clicking)
26421              * @param {SelectionModel} this
26422              */
26423             "afterselectionchange" : true,
26424         /**
26425              * @event beforerowselect
26426              * Fires when a row is selected being selected, return false to cancel.
26427              * @param {SelectionModel} this
26428              * @param {Number} rowIndex The selected index
26429              * @param {Boolean} keepExisting False if other selections will be cleared
26430              */
26431             "beforerowselect" : true,
26432         /**
26433              * @event rowselect
26434              * Fires when a row is selected.
26435              * @param {SelectionModel} this
26436              * @param {Number} rowIndex The selected index
26437              * @param {Roo.data.Record} r The record
26438              */
26439             "rowselect" : true,
26440         /**
26441              * @event rowdeselect
26442              * Fires when a row is deselected.
26443              * @param {SelectionModel} this
26444              * @param {Number} rowIndex The selected index
26445              */
26446         "rowdeselect" : true
26447     });
26448     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26449     this.locked = false;
26450  };
26451
26452 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26453     /**
26454      * @cfg {Boolean} singleSelect
26455      * True to allow selection of only one row at a time (defaults to false)
26456      */
26457     singleSelect : false,
26458
26459     // private
26460     initEvents : function()
26461     {
26462
26463         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26464         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26465         //}else{ // allow click to work like normal
26466          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26467         //}
26468         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26469         this.grid.on("rowclick", this.handleMouseDown, this);
26470         
26471         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26472             "up" : function(e){
26473                 if(!e.shiftKey){
26474                     this.selectPrevious(e.shiftKey);
26475                 }else if(this.last !== false && this.lastActive !== false){
26476                     var last = this.last;
26477                     this.selectRange(this.last,  this.lastActive-1);
26478                     this.grid.getView().focusRow(this.lastActive);
26479                     if(last !== false){
26480                         this.last = last;
26481                     }
26482                 }else{
26483                     this.selectFirstRow();
26484                 }
26485                 this.fireEvent("afterselectionchange", this);
26486             },
26487             "down" : function(e){
26488                 if(!e.shiftKey){
26489                     this.selectNext(e.shiftKey);
26490                 }else if(this.last !== false && this.lastActive !== false){
26491                     var last = this.last;
26492                     this.selectRange(this.last,  this.lastActive+1);
26493                     this.grid.getView().focusRow(this.lastActive);
26494                     if(last !== false){
26495                         this.last = last;
26496                     }
26497                 }else{
26498                     this.selectFirstRow();
26499                 }
26500                 this.fireEvent("afterselectionchange", this);
26501             },
26502             scope: this
26503         });
26504         this.grid.store.on('load', function(){
26505             this.selections.clear();
26506         },this);
26507         /*
26508         var view = this.grid.view;
26509         view.on("refresh", this.onRefresh, this);
26510         view.on("rowupdated", this.onRowUpdated, this);
26511         view.on("rowremoved", this.onRemove, this);
26512         */
26513     },
26514
26515     // private
26516     onRefresh : function()
26517     {
26518         var ds = this.grid.store, i, v = this.grid.view;
26519         var s = this.selections;
26520         s.each(function(r){
26521             if((i = ds.indexOfId(r.id)) != -1){
26522                 v.onRowSelect(i);
26523             }else{
26524                 s.remove(r);
26525             }
26526         });
26527     },
26528
26529     // private
26530     onRemove : function(v, index, r){
26531         this.selections.remove(r);
26532     },
26533
26534     // private
26535     onRowUpdated : function(v, index, r){
26536         if(this.isSelected(r)){
26537             v.onRowSelect(index);
26538         }
26539     },
26540
26541     /**
26542      * Select records.
26543      * @param {Array} records The records to select
26544      * @param {Boolean} keepExisting (optional) True to keep existing selections
26545      */
26546     selectRecords : function(records, keepExisting)
26547     {
26548         if(!keepExisting){
26549             this.clearSelections();
26550         }
26551             var ds = this.grid.store;
26552         for(var i = 0, len = records.length; i < len; i++){
26553             this.selectRow(ds.indexOf(records[i]), true);
26554         }
26555     },
26556
26557     /**
26558      * Gets the number of selected rows.
26559      * @return {Number}
26560      */
26561     getCount : function(){
26562         return this.selections.length;
26563     },
26564
26565     /**
26566      * Selects the first row in the grid.
26567      */
26568     selectFirstRow : function(){
26569         this.selectRow(0);
26570     },
26571
26572     /**
26573      * Select the last row.
26574      * @param {Boolean} keepExisting (optional) True to keep existing selections
26575      */
26576     selectLastRow : function(keepExisting){
26577         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26578         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26579     },
26580
26581     /**
26582      * Selects the row immediately following the last selected row.
26583      * @param {Boolean} keepExisting (optional) True to keep existing selections
26584      */
26585     selectNext : function(keepExisting)
26586     {
26587             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26588             this.selectRow(this.last+1, keepExisting);
26589             this.grid.getView().focusRow(this.last);
26590         }
26591     },
26592
26593     /**
26594      * Selects the row that precedes the last selected row.
26595      * @param {Boolean} keepExisting (optional) True to keep existing selections
26596      */
26597     selectPrevious : function(keepExisting){
26598         if(this.last){
26599             this.selectRow(this.last-1, keepExisting);
26600             this.grid.getView().focusRow(this.last);
26601         }
26602     },
26603
26604     /**
26605      * Returns the selected records
26606      * @return {Array} Array of selected records
26607      */
26608     getSelections : function(){
26609         return [].concat(this.selections.items);
26610     },
26611
26612     /**
26613      * Returns the first selected record.
26614      * @return {Record}
26615      */
26616     getSelected : function(){
26617         return this.selections.itemAt(0);
26618     },
26619
26620
26621     /**
26622      * Clears all selections.
26623      */
26624     clearSelections : function(fast)
26625     {
26626         if(this.locked) {
26627             return;
26628         }
26629         if(fast !== true){
26630                 var ds = this.grid.store;
26631             var s = this.selections;
26632             s.each(function(r){
26633                 this.deselectRow(ds.indexOfId(r.id));
26634             }, this);
26635             s.clear();
26636         }else{
26637             this.selections.clear();
26638         }
26639         this.last = false;
26640     },
26641
26642
26643     /**
26644      * Selects all rows.
26645      */
26646     selectAll : function(){
26647         if(this.locked) {
26648             return;
26649         }
26650         this.selections.clear();
26651         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26652             this.selectRow(i, true);
26653         }
26654     },
26655
26656     /**
26657      * Returns True if there is a selection.
26658      * @return {Boolean}
26659      */
26660     hasSelection : function(){
26661         return this.selections.length > 0;
26662     },
26663
26664     /**
26665      * Returns True if the specified row is selected.
26666      * @param {Number/Record} record The record or index of the record to check
26667      * @return {Boolean}
26668      */
26669     isSelected : function(index){
26670             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26671         return (r && this.selections.key(r.id) ? true : false);
26672     },
26673
26674     /**
26675      * Returns True if the specified record id is selected.
26676      * @param {String} id The id of record to check
26677      * @return {Boolean}
26678      */
26679     isIdSelected : function(id){
26680         return (this.selections.key(id) ? true : false);
26681     },
26682
26683
26684     // private
26685     handleMouseDBClick : function(e, t){
26686         
26687     },
26688     // private
26689     handleMouseDown : function(e, t)
26690     {
26691             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26692         if(this.isLocked() || rowIndex < 0 ){
26693             return;
26694         };
26695         if(e.shiftKey && this.last !== false){
26696             var last = this.last;
26697             this.selectRange(last, rowIndex, e.ctrlKey);
26698             this.last = last; // reset the last
26699             t.focus();
26700     
26701         }else{
26702             var isSelected = this.isSelected(rowIndex);
26703             //Roo.log("select row:" + rowIndex);
26704             if(isSelected){
26705                 this.deselectRow(rowIndex);
26706             } else {
26707                         this.selectRow(rowIndex, true);
26708             }
26709     
26710             /*
26711                 if(e.button !== 0 && isSelected){
26712                 alert('rowIndex 2: ' + rowIndex);
26713                     view.focusRow(rowIndex);
26714                 }else if(e.ctrlKey && isSelected){
26715                     this.deselectRow(rowIndex);
26716                 }else if(!isSelected){
26717                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26718                     view.focusRow(rowIndex);
26719                 }
26720             */
26721         }
26722         this.fireEvent("afterselectionchange", this);
26723     },
26724     // private
26725     handleDragableRowClick :  function(grid, rowIndex, e) 
26726     {
26727         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26728             this.selectRow(rowIndex, false);
26729             grid.view.focusRow(rowIndex);
26730              this.fireEvent("afterselectionchange", this);
26731         }
26732     },
26733     
26734     /**
26735      * Selects multiple rows.
26736      * @param {Array} rows Array of the indexes of the row to select
26737      * @param {Boolean} keepExisting (optional) True to keep existing selections
26738      */
26739     selectRows : function(rows, keepExisting){
26740         if(!keepExisting){
26741             this.clearSelections();
26742         }
26743         for(var i = 0, len = rows.length; i < len; i++){
26744             this.selectRow(rows[i], true);
26745         }
26746     },
26747
26748     /**
26749      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26750      * @param {Number} startRow The index of the first row in the range
26751      * @param {Number} endRow The index of the last row in the range
26752      * @param {Boolean} keepExisting (optional) True to retain existing selections
26753      */
26754     selectRange : function(startRow, endRow, keepExisting){
26755         if(this.locked) {
26756             return;
26757         }
26758         if(!keepExisting){
26759             this.clearSelections();
26760         }
26761         if(startRow <= endRow){
26762             for(var i = startRow; i <= endRow; i++){
26763                 this.selectRow(i, true);
26764             }
26765         }else{
26766             for(var i = startRow; i >= endRow; i--){
26767                 this.selectRow(i, true);
26768             }
26769         }
26770     },
26771
26772     /**
26773      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26774      * @param {Number} startRow The index of the first row in the range
26775      * @param {Number} endRow The index of the last row in the range
26776      */
26777     deselectRange : function(startRow, endRow, preventViewNotify){
26778         if(this.locked) {
26779             return;
26780         }
26781         for(var i = startRow; i <= endRow; i++){
26782             this.deselectRow(i, preventViewNotify);
26783         }
26784     },
26785
26786     /**
26787      * Selects a row.
26788      * @param {Number} row The index of the row to select
26789      * @param {Boolean} keepExisting (optional) True to keep existing selections
26790      */
26791     selectRow : function(index, keepExisting, preventViewNotify)
26792     {
26793             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26794             return;
26795         }
26796         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26797             if(!keepExisting || this.singleSelect){
26798                 this.clearSelections();
26799             }
26800             
26801             var r = this.grid.store.getAt(index);
26802             //console.log('selectRow - record id :' + r.id);
26803             
26804             this.selections.add(r);
26805             this.last = this.lastActive = index;
26806             if(!preventViewNotify){
26807                 var proxy = new Roo.Element(
26808                                 this.grid.getRowDom(index)
26809                 );
26810                 proxy.addClass('bg-info info');
26811             }
26812             this.fireEvent("rowselect", this, index, r);
26813             this.fireEvent("selectionchange", this);
26814         }
26815     },
26816
26817     /**
26818      * Deselects a row.
26819      * @param {Number} row The index of the row to deselect
26820      */
26821     deselectRow : function(index, preventViewNotify)
26822     {
26823         if(this.locked) {
26824             return;
26825         }
26826         if(this.last == index){
26827             this.last = false;
26828         }
26829         if(this.lastActive == index){
26830             this.lastActive = false;
26831         }
26832         
26833         var r = this.grid.store.getAt(index);
26834         if (!r) {
26835             return;
26836         }
26837         
26838         this.selections.remove(r);
26839         //.console.log('deselectRow - record id :' + r.id);
26840         if(!preventViewNotify){
26841         
26842             var proxy = new Roo.Element(
26843                 this.grid.getRowDom(index)
26844             );
26845             proxy.removeClass('bg-info info');
26846         }
26847         this.fireEvent("rowdeselect", this, index);
26848         this.fireEvent("selectionchange", this);
26849     },
26850
26851     // private
26852     restoreLast : function(){
26853         if(this._last){
26854             this.last = this._last;
26855         }
26856     },
26857
26858     // private
26859     acceptsNav : function(row, col, cm){
26860         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26861     },
26862
26863     // private
26864     onEditorKey : function(field, e){
26865         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26866         if(k == e.TAB){
26867             e.stopEvent();
26868             ed.completeEdit();
26869             if(e.shiftKey){
26870                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26871             }else{
26872                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26873             }
26874         }else if(k == e.ENTER && !e.ctrlKey){
26875             e.stopEvent();
26876             ed.completeEdit();
26877             if(e.shiftKey){
26878                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26879             }else{
26880                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26881             }
26882         }else if(k == e.ESC){
26883             ed.cancelEdit();
26884         }
26885         if(newCell){
26886             g.startEditing(newCell[0], newCell[1]);
26887         }
26888     }
26889 });
26890 /*
26891  * Based on:
26892  * Ext JS Library 1.1.1
26893  * Copyright(c) 2006-2007, Ext JS, LLC.
26894  *
26895  * Originally Released Under LGPL - original licence link has changed is not relivant.
26896  *
26897  * Fork - LGPL
26898  * <script type="text/javascript">
26899  */
26900  
26901 /**
26902  * @class Roo.bootstrap.PagingToolbar
26903  * @extends Roo.bootstrap.NavSimplebar
26904  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26905  * @constructor
26906  * Create a new PagingToolbar
26907  * @param {Object} config The config object
26908  * @param {Roo.data.Store} store
26909  */
26910 Roo.bootstrap.PagingToolbar = function(config)
26911 {
26912     // old args format still supported... - xtype is prefered..
26913         // created from xtype...
26914     
26915     this.ds = config.dataSource;
26916     
26917     if (config.store && !this.ds) {
26918         this.store= Roo.factory(config.store, Roo.data);
26919         this.ds = this.store;
26920         this.ds.xmodule = this.xmodule || false;
26921     }
26922     
26923     this.toolbarItems = [];
26924     if (config.items) {
26925         this.toolbarItems = config.items;
26926     }
26927     
26928     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26929     
26930     this.cursor = 0;
26931     
26932     if (this.ds) { 
26933         this.bind(this.ds);
26934     }
26935     
26936     if (Roo.bootstrap.version == 4) {
26937         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26938     } else {
26939         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26940     }
26941     
26942 };
26943
26944 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26945     /**
26946      * @cfg {Roo.data.Store} dataSource
26947      * The underlying data store providing the paged data
26948      */
26949     /**
26950      * @cfg {String/HTMLElement/Element} container
26951      * container The id or element that will contain the toolbar
26952      */
26953     /**
26954      * @cfg {Boolean} displayInfo
26955      * True to display the displayMsg (defaults to false)
26956      */
26957     /**
26958      * @cfg {Number} pageSize
26959      * The number of records to display per page (defaults to 20)
26960      */
26961     pageSize: 20,
26962     /**
26963      * @cfg {String} displayMsg
26964      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26965      */
26966     displayMsg : 'Displaying {0} - {1} of {2}',
26967     /**
26968      * @cfg {String} emptyMsg
26969      * The message to display when no records are found (defaults to "No data to display")
26970      */
26971     emptyMsg : 'No data to display',
26972     /**
26973      * Customizable piece of the default paging text (defaults to "Page")
26974      * @type String
26975      */
26976     beforePageText : "Page",
26977     /**
26978      * Customizable piece of the default paging text (defaults to "of %0")
26979      * @type String
26980      */
26981     afterPageText : "of {0}",
26982     /**
26983      * Customizable piece of the default paging text (defaults to "First Page")
26984      * @type String
26985      */
26986     firstText : "First Page",
26987     /**
26988      * Customizable piece of the default paging text (defaults to "Previous Page")
26989      * @type String
26990      */
26991     prevText : "Previous Page",
26992     /**
26993      * Customizable piece of the default paging text (defaults to "Next Page")
26994      * @type String
26995      */
26996     nextText : "Next Page",
26997     /**
26998      * Customizable piece of the default paging text (defaults to "Last Page")
26999      * @type String
27000      */
27001     lastText : "Last Page",
27002     /**
27003      * Customizable piece of the default paging text (defaults to "Refresh")
27004      * @type String
27005      */
27006     refreshText : "Refresh",
27007
27008     buttons : false,
27009     // private
27010     onRender : function(ct, position) 
27011     {
27012         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27013         this.navgroup.parentId = this.id;
27014         this.navgroup.onRender(this.el, null);
27015         // add the buttons to the navgroup
27016         
27017         if(this.displayInfo){
27018             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27019             this.displayEl = this.el.select('.x-paging-info', true).first();
27020 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27021 //            this.displayEl = navel.el.select('span',true).first();
27022         }
27023         
27024         var _this = this;
27025         
27026         if(this.buttons){
27027             Roo.each(_this.buttons, function(e){ // this might need to use render????
27028                Roo.factory(e).render(_this.el);
27029             });
27030         }
27031             
27032         Roo.each(_this.toolbarItems, function(e) {
27033             _this.navgroup.addItem(e);
27034         });
27035         
27036         
27037         this.first = this.navgroup.addItem({
27038             tooltip: this.firstText,
27039             cls: "prev btn-outline-secondary",
27040             html : ' <i class="fa fa-step-backward"></i>',
27041             disabled: true,
27042             preventDefault: true,
27043             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27044         });
27045         
27046         this.prev =  this.navgroup.addItem({
27047             tooltip: this.prevText,
27048             cls: "prev btn-outline-secondary",
27049             html : ' <i class="fa fa-backward"></i>',
27050             disabled: true,
27051             preventDefault: true,
27052             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27053         });
27054     //this.addSeparator();
27055         
27056         
27057         var field = this.navgroup.addItem( {
27058             tagtype : 'span',
27059             cls : 'x-paging-position  btn-outline-secondary',
27060              disabled: true,
27061             html : this.beforePageText  +
27062                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27063                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27064          } ); //?? escaped?
27065         
27066         this.field = field.el.select('input', true).first();
27067         this.field.on("keydown", this.onPagingKeydown, this);
27068         this.field.on("focus", function(){this.dom.select();});
27069     
27070     
27071         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27072         //this.field.setHeight(18);
27073         //this.addSeparator();
27074         this.next = this.navgroup.addItem({
27075             tooltip: this.nextText,
27076             cls: "next btn-outline-secondary",
27077             html : ' <i class="fa fa-forward"></i>',
27078             disabled: true,
27079             preventDefault: true,
27080             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27081         });
27082         this.last = this.navgroup.addItem({
27083             tooltip: this.lastText,
27084             html : ' <i class="fa fa-step-forward"></i>',
27085             cls: "next btn-outline-secondary",
27086             disabled: true,
27087             preventDefault: true,
27088             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27089         });
27090     //this.addSeparator();
27091         this.loading = this.navgroup.addItem({
27092             tooltip: this.refreshText,
27093             cls: "btn-outline-secondary",
27094             html : ' <i class="fa fa-refresh"></i>',
27095             preventDefault: true,
27096             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27097         });
27098         
27099     },
27100
27101     // private
27102     updateInfo : function(){
27103         if(this.displayEl){
27104             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27105             var msg = count == 0 ?
27106                 this.emptyMsg :
27107                 String.format(
27108                     this.displayMsg,
27109                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27110                 );
27111             this.displayEl.update(msg);
27112         }
27113     },
27114
27115     // private
27116     onLoad : function(ds, r, o)
27117     {
27118         this.cursor = o.params.start ? o.params.start : 0;
27119         
27120         var d = this.getPageData(),
27121             ap = d.activePage,
27122             ps = d.pages;
27123         
27124         
27125         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27126         this.field.dom.value = ap;
27127         this.first.setDisabled(ap == 1);
27128         this.prev.setDisabled(ap == 1);
27129         this.next.setDisabled(ap == ps);
27130         this.last.setDisabled(ap == ps);
27131         this.loading.enable();
27132         this.updateInfo();
27133     },
27134
27135     // private
27136     getPageData : function(){
27137         var total = this.ds.getTotalCount();
27138         return {
27139             total : total,
27140             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27141             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27142         };
27143     },
27144
27145     // private
27146     onLoadError : function(){
27147         this.loading.enable();
27148     },
27149
27150     // private
27151     onPagingKeydown : function(e){
27152         var k = e.getKey();
27153         var d = this.getPageData();
27154         if(k == e.RETURN){
27155             var v = this.field.dom.value, pageNum;
27156             if(!v || isNaN(pageNum = parseInt(v, 10))){
27157                 this.field.dom.value = d.activePage;
27158                 return;
27159             }
27160             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27161             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27162             e.stopEvent();
27163         }
27164         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))
27165         {
27166           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27167           this.field.dom.value = pageNum;
27168           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27169           e.stopEvent();
27170         }
27171         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27172         {
27173           var v = this.field.dom.value, pageNum; 
27174           var increment = (e.shiftKey) ? 10 : 1;
27175           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27176                 increment *= -1;
27177           }
27178           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27179             this.field.dom.value = d.activePage;
27180             return;
27181           }
27182           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27183           {
27184             this.field.dom.value = parseInt(v, 10) + increment;
27185             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27186             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27187           }
27188           e.stopEvent();
27189         }
27190     },
27191
27192     // private
27193     beforeLoad : function(){
27194         if(this.loading){
27195             this.loading.disable();
27196         }
27197     },
27198
27199     // private
27200     onClick : function(which){
27201         
27202         var ds = this.ds;
27203         if (!ds) {
27204             return;
27205         }
27206         
27207         switch(which){
27208             case "first":
27209                 ds.load({params:{start: 0, limit: this.pageSize}});
27210             break;
27211             case "prev":
27212                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27213             break;
27214             case "next":
27215                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27216             break;
27217             case "last":
27218                 var total = ds.getTotalCount();
27219                 var extra = total % this.pageSize;
27220                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27221                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27222             break;
27223             case "refresh":
27224                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27225             break;
27226         }
27227     },
27228
27229     /**
27230      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27231      * @param {Roo.data.Store} store The data store to unbind
27232      */
27233     unbind : function(ds){
27234         ds.un("beforeload", this.beforeLoad, this);
27235         ds.un("load", this.onLoad, this);
27236         ds.un("loadexception", this.onLoadError, this);
27237         ds.un("remove", this.updateInfo, this);
27238         ds.un("add", this.updateInfo, this);
27239         this.ds = undefined;
27240     },
27241
27242     /**
27243      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27244      * @param {Roo.data.Store} store The data store to bind
27245      */
27246     bind : function(ds){
27247         ds.on("beforeload", this.beforeLoad, this);
27248         ds.on("load", this.onLoad, this);
27249         ds.on("loadexception", this.onLoadError, this);
27250         ds.on("remove", this.updateInfo, this);
27251         ds.on("add", this.updateInfo, this);
27252         this.ds = ds;
27253     }
27254 });/*
27255  * - LGPL
27256  *
27257  * element
27258  * 
27259  */
27260
27261 /**
27262  * @class Roo.bootstrap.MessageBar
27263  * @extends Roo.bootstrap.Component
27264  * Bootstrap MessageBar class
27265  * @cfg {String} html contents of the MessageBar
27266  * @cfg {String} weight (info | success | warning | danger) default info
27267  * @cfg {String} beforeClass insert the bar before the given class
27268  * @cfg {Boolean} closable (true | false) default false
27269  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27270  * 
27271  * @constructor
27272  * Create a new Element
27273  * @param {Object} config The config object
27274  */
27275
27276 Roo.bootstrap.MessageBar = function(config){
27277     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27278 };
27279
27280 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27281     
27282     html: '',
27283     weight: 'info',
27284     closable: false,
27285     fixed: false,
27286     beforeClass: 'bootstrap-sticky-wrap',
27287     
27288     getAutoCreate : function(){
27289         
27290         var cfg = {
27291             tag: 'div',
27292             cls: 'alert alert-dismissable alert-' + this.weight,
27293             cn: [
27294                 {
27295                     tag: 'span',
27296                     cls: 'message',
27297                     html: this.html || ''
27298                 }
27299             ]
27300         };
27301         
27302         if(this.fixed){
27303             cfg.cls += ' alert-messages-fixed';
27304         }
27305         
27306         if(this.closable){
27307             cfg.cn.push({
27308                 tag: 'button',
27309                 cls: 'close',
27310                 html: 'x'
27311             });
27312         }
27313         
27314         return cfg;
27315     },
27316     
27317     onRender : function(ct, position)
27318     {
27319         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27320         
27321         if(!this.el){
27322             var cfg = Roo.apply({},  this.getAutoCreate());
27323             cfg.id = Roo.id();
27324             
27325             if (this.cls) {
27326                 cfg.cls += ' ' + this.cls;
27327             }
27328             if (this.style) {
27329                 cfg.style = this.style;
27330             }
27331             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27332             
27333             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27334         }
27335         
27336         this.el.select('>button.close').on('click', this.hide, this);
27337         
27338     },
27339     
27340     show : function()
27341     {
27342         if (!this.rendered) {
27343             this.render();
27344         }
27345         
27346         this.el.show();
27347         
27348         this.fireEvent('show', this);
27349         
27350     },
27351     
27352     hide : function()
27353     {
27354         if (!this.rendered) {
27355             this.render();
27356         }
27357         
27358         this.el.hide();
27359         
27360         this.fireEvent('hide', this);
27361     },
27362     
27363     update : function()
27364     {
27365 //        var e = this.el.dom.firstChild;
27366 //        
27367 //        if(this.closable){
27368 //            e = e.nextSibling;
27369 //        }
27370 //        
27371 //        e.data = this.html || '';
27372
27373         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27374     }
27375    
27376 });
27377
27378  
27379
27380      /*
27381  * - LGPL
27382  *
27383  * Graph
27384  * 
27385  */
27386
27387
27388 /**
27389  * @class Roo.bootstrap.Graph
27390  * @extends Roo.bootstrap.Component
27391  * Bootstrap Graph class
27392 > Prameters
27393  -sm {number} sm 4
27394  -md {number} md 5
27395  @cfg {String} graphtype  bar | vbar | pie
27396  @cfg {number} g_x coodinator | centre x (pie)
27397  @cfg {number} g_y coodinator | centre y (pie)
27398  @cfg {number} g_r radius (pie)
27399  @cfg {number} g_height height of the chart (respected by all elements in the set)
27400  @cfg {number} g_width width of the chart (respected by all elements in the set)
27401  @cfg {Object} title The title of the chart
27402     
27403  -{Array}  values
27404  -opts (object) options for the chart 
27405      o {
27406      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27407      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27408      o vgutter (number)
27409      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.
27410      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27411      o to
27412      o stretch (boolean)
27413      o }
27414  -opts (object) options for the pie
27415      o{
27416      o cut
27417      o startAngle (number)
27418      o endAngle (number)
27419      } 
27420  *
27421  * @constructor
27422  * Create a new Input
27423  * @param {Object} config The config object
27424  */
27425
27426 Roo.bootstrap.Graph = function(config){
27427     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27428     
27429     this.addEvents({
27430         // img events
27431         /**
27432          * @event click
27433          * The img click event for the img.
27434          * @param {Roo.EventObject} e
27435          */
27436         "click" : true
27437     });
27438 };
27439
27440 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27441     
27442     sm: 4,
27443     md: 5,
27444     graphtype: 'bar',
27445     g_height: 250,
27446     g_width: 400,
27447     g_x: 50,
27448     g_y: 50,
27449     g_r: 30,
27450     opts:{
27451         //g_colors: this.colors,
27452         g_type: 'soft',
27453         g_gutter: '20%'
27454
27455     },
27456     title : false,
27457
27458     getAutoCreate : function(){
27459         
27460         var cfg = {
27461             tag: 'div',
27462             html : null
27463         };
27464         
27465         
27466         return  cfg;
27467     },
27468
27469     onRender : function(ct,position){
27470         
27471         
27472         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27473         
27474         if (typeof(Raphael) == 'undefined') {
27475             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27476             return;
27477         }
27478         
27479         this.raphael = Raphael(this.el.dom);
27480         
27481                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27482                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27483                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27484                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27485                 /*
27486                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27487                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27488                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27489                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27490                 
27491                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27492                 r.barchart(330, 10, 300, 220, data1);
27493                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27494                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27495                 */
27496                 
27497                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27498                 // r.barchart(30, 30, 560, 250,  xdata, {
27499                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27500                 //     axis : "0 0 1 1",
27501                 //     axisxlabels :  xdata
27502                 //     //yvalues : cols,
27503                    
27504                 // });
27505 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27506 //        
27507 //        this.load(null,xdata,{
27508 //                axis : "0 0 1 1",
27509 //                axisxlabels :  xdata
27510 //                });
27511
27512     },
27513
27514     load : function(graphtype,xdata,opts)
27515     {
27516         this.raphael.clear();
27517         if(!graphtype) {
27518             graphtype = this.graphtype;
27519         }
27520         if(!opts){
27521             opts = this.opts;
27522         }
27523         var r = this.raphael,
27524             fin = function () {
27525                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27526             },
27527             fout = function () {
27528                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27529             },
27530             pfin = function() {
27531                 this.sector.stop();
27532                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27533
27534                 if (this.label) {
27535                     this.label[0].stop();
27536                     this.label[0].attr({ r: 7.5 });
27537                     this.label[1].attr({ "font-weight": 800 });
27538                 }
27539             },
27540             pfout = function() {
27541                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27542
27543                 if (this.label) {
27544                     this.label[0].animate({ r: 5 }, 500, "bounce");
27545                     this.label[1].attr({ "font-weight": 400 });
27546                 }
27547             };
27548
27549         switch(graphtype){
27550             case 'bar':
27551                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27552                 break;
27553             case 'hbar':
27554                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27555                 break;
27556             case 'pie':
27557 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27558 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27559 //            
27560                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27561                 
27562                 break;
27563
27564         }
27565         
27566         if(this.title){
27567             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27568         }
27569         
27570     },
27571     
27572     setTitle: function(o)
27573     {
27574         this.title = o;
27575     },
27576     
27577     initEvents: function() {
27578         
27579         if(!this.href){
27580             this.el.on('click', this.onClick, this);
27581         }
27582     },
27583     
27584     onClick : function(e)
27585     {
27586         Roo.log('img onclick');
27587         this.fireEvent('click', this, e);
27588     }
27589    
27590 });
27591
27592  
27593 /*
27594  * - LGPL
27595  *
27596  * numberBox
27597  * 
27598  */
27599 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27600
27601 /**
27602  * @class Roo.bootstrap.dash.NumberBox
27603  * @extends Roo.bootstrap.Component
27604  * Bootstrap NumberBox class
27605  * @cfg {String} headline Box headline
27606  * @cfg {String} content Box content
27607  * @cfg {String} icon Box icon
27608  * @cfg {String} footer Footer text
27609  * @cfg {String} fhref Footer href
27610  * 
27611  * @constructor
27612  * Create a new NumberBox
27613  * @param {Object} config The config object
27614  */
27615
27616
27617 Roo.bootstrap.dash.NumberBox = function(config){
27618     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27619     
27620 };
27621
27622 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27623     
27624     headline : '',
27625     content : '',
27626     icon : '',
27627     footer : '',
27628     fhref : '',
27629     ficon : '',
27630     
27631     getAutoCreate : function(){
27632         
27633         var cfg = {
27634             tag : 'div',
27635             cls : 'small-box ',
27636             cn : [
27637                 {
27638                     tag : 'div',
27639                     cls : 'inner',
27640                     cn :[
27641                         {
27642                             tag : 'h3',
27643                             cls : 'roo-headline',
27644                             html : this.headline
27645                         },
27646                         {
27647                             tag : 'p',
27648                             cls : 'roo-content',
27649                             html : this.content
27650                         }
27651                     ]
27652                 }
27653             ]
27654         };
27655         
27656         if(this.icon){
27657             cfg.cn.push({
27658                 tag : 'div',
27659                 cls : 'icon',
27660                 cn :[
27661                     {
27662                         tag : 'i',
27663                         cls : 'ion ' + this.icon
27664                     }
27665                 ]
27666             });
27667         }
27668         
27669         if(this.footer){
27670             var footer = {
27671                 tag : 'a',
27672                 cls : 'small-box-footer',
27673                 href : this.fhref || '#',
27674                 html : this.footer
27675             };
27676             
27677             cfg.cn.push(footer);
27678             
27679         }
27680         
27681         return  cfg;
27682     },
27683
27684     onRender : function(ct,position){
27685         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27686
27687
27688        
27689                 
27690     },
27691
27692     setHeadline: function (value)
27693     {
27694         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27695     },
27696     
27697     setFooter: function (value, href)
27698     {
27699         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27700         
27701         if(href){
27702             this.el.select('a.small-box-footer',true).first().attr('href', href);
27703         }
27704         
27705     },
27706
27707     setContent: function (value)
27708     {
27709         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27710     },
27711
27712     initEvents: function() 
27713     {   
27714         
27715     }
27716     
27717 });
27718
27719  
27720 /*
27721  * - LGPL
27722  *
27723  * TabBox
27724  * 
27725  */
27726 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27727
27728 /**
27729  * @class Roo.bootstrap.dash.TabBox
27730  * @extends Roo.bootstrap.Component
27731  * Bootstrap TabBox class
27732  * @cfg {String} title Title of the TabBox
27733  * @cfg {String} icon Icon of the TabBox
27734  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27735  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27736  * 
27737  * @constructor
27738  * Create a new TabBox
27739  * @param {Object} config The config object
27740  */
27741
27742
27743 Roo.bootstrap.dash.TabBox = function(config){
27744     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27745     this.addEvents({
27746         // raw events
27747         /**
27748          * @event addpane
27749          * When a pane is added
27750          * @param {Roo.bootstrap.dash.TabPane} pane
27751          */
27752         "addpane" : true,
27753         /**
27754          * @event activatepane
27755          * When a pane is activated
27756          * @param {Roo.bootstrap.dash.TabPane} pane
27757          */
27758         "activatepane" : true
27759         
27760          
27761     });
27762     
27763     this.panes = [];
27764 };
27765
27766 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27767
27768     title : '',
27769     icon : false,
27770     showtabs : true,
27771     tabScrollable : false,
27772     
27773     getChildContainer : function()
27774     {
27775         return this.el.select('.tab-content', true).first();
27776     },
27777     
27778     getAutoCreate : function(){
27779         
27780         var header = {
27781             tag: 'li',
27782             cls: 'pull-left header',
27783             html: this.title,
27784             cn : []
27785         };
27786         
27787         if(this.icon){
27788             header.cn.push({
27789                 tag: 'i',
27790                 cls: 'fa ' + this.icon
27791             });
27792         }
27793         
27794         var h = {
27795             tag: 'ul',
27796             cls: 'nav nav-tabs pull-right',
27797             cn: [
27798                 header
27799             ]
27800         };
27801         
27802         if(this.tabScrollable){
27803             h = {
27804                 tag: 'div',
27805                 cls: 'tab-header',
27806                 cn: [
27807                     {
27808                         tag: 'ul',
27809                         cls: 'nav nav-tabs pull-right',
27810                         cn: [
27811                             header
27812                         ]
27813                     }
27814                 ]
27815             };
27816         }
27817         
27818         var cfg = {
27819             tag: 'div',
27820             cls: 'nav-tabs-custom',
27821             cn: [
27822                 h,
27823                 {
27824                     tag: 'div',
27825                     cls: 'tab-content no-padding',
27826                     cn: []
27827                 }
27828             ]
27829         };
27830
27831         return  cfg;
27832     },
27833     initEvents : function()
27834     {
27835         //Roo.log('add add pane handler');
27836         this.on('addpane', this.onAddPane, this);
27837     },
27838      /**
27839      * Updates the box title
27840      * @param {String} html to set the title to.
27841      */
27842     setTitle : function(value)
27843     {
27844         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27845     },
27846     onAddPane : function(pane)
27847     {
27848         this.panes.push(pane);
27849         //Roo.log('addpane');
27850         //Roo.log(pane);
27851         // tabs are rendere left to right..
27852         if(!this.showtabs){
27853             return;
27854         }
27855         
27856         var ctr = this.el.select('.nav-tabs', true).first();
27857          
27858          
27859         var existing = ctr.select('.nav-tab',true);
27860         var qty = existing.getCount();;
27861         
27862         
27863         var tab = ctr.createChild({
27864             tag : 'li',
27865             cls : 'nav-tab' + (qty ? '' : ' active'),
27866             cn : [
27867                 {
27868                     tag : 'a',
27869                     href:'#',
27870                     html : pane.title
27871                 }
27872             ]
27873         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27874         pane.tab = tab;
27875         
27876         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27877         if (!qty) {
27878             pane.el.addClass('active');
27879         }
27880         
27881                 
27882     },
27883     onTabClick : function(ev,un,ob,pane)
27884     {
27885         //Roo.log('tab - prev default');
27886         ev.preventDefault();
27887         
27888         
27889         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27890         pane.tab.addClass('active');
27891         //Roo.log(pane.title);
27892         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27893         // technically we should have a deactivate event.. but maybe add later.
27894         // and it should not de-activate the selected tab...
27895         this.fireEvent('activatepane', pane);
27896         pane.el.addClass('active');
27897         pane.fireEvent('activate');
27898         
27899         
27900     },
27901     
27902     getActivePane : function()
27903     {
27904         var r = false;
27905         Roo.each(this.panes, function(p) {
27906             if(p.el.hasClass('active')){
27907                 r = p;
27908                 return false;
27909             }
27910             
27911             return;
27912         });
27913         
27914         return r;
27915     }
27916     
27917     
27918 });
27919
27920  
27921 /*
27922  * - LGPL
27923  *
27924  * Tab pane
27925  * 
27926  */
27927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27928 /**
27929  * @class Roo.bootstrap.TabPane
27930  * @extends Roo.bootstrap.Component
27931  * Bootstrap TabPane class
27932  * @cfg {Boolean} active (false | true) Default false
27933  * @cfg {String} title title of panel
27934
27935  * 
27936  * @constructor
27937  * Create a new TabPane
27938  * @param {Object} config The config object
27939  */
27940
27941 Roo.bootstrap.dash.TabPane = function(config){
27942     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27943     
27944     this.addEvents({
27945         // raw events
27946         /**
27947          * @event activate
27948          * When a pane is activated
27949          * @param {Roo.bootstrap.dash.TabPane} pane
27950          */
27951         "activate" : true
27952          
27953     });
27954 };
27955
27956 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27957     
27958     active : false,
27959     title : '',
27960     
27961     // the tabBox that this is attached to.
27962     tab : false,
27963      
27964     getAutoCreate : function() 
27965     {
27966         var cfg = {
27967             tag: 'div',
27968             cls: 'tab-pane'
27969         };
27970         
27971         if(this.active){
27972             cfg.cls += ' active';
27973         }
27974         
27975         return cfg;
27976     },
27977     initEvents  : function()
27978     {
27979         //Roo.log('trigger add pane handler');
27980         this.parent().fireEvent('addpane', this)
27981     },
27982     
27983      /**
27984      * Updates the tab title 
27985      * @param {String} html to set the title to.
27986      */
27987     setTitle: function(str)
27988     {
27989         if (!this.tab) {
27990             return;
27991         }
27992         this.title = str;
27993         this.tab.select('a', true).first().dom.innerHTML = str;
27994         
27995     }
27996     
27997     
27998     
27999 });
28000
28001  
28002
28003
28004  /*
28005  * - LGPL
28006  *
28007  * menu
28008  * 
28009  */
28010 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28011
28012 /**
28013  * @class Roo.bootstrap.menu.Menu
28014  * @extends Roo.bootstrap.Component
28015  * Bootstrap Menu class - container for Menu
28016  * @cfg {String} html Text of the menu
28017  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28018  * @cfg {String} icon Font awesome icon
28019  * @cfg {String} pos Menu align to (top | bottom) default bottom
28020  * 
28021  * 
28022  * @constructor
28023  * Create a new Menu
28024  * @param {Object} config The config object
28025  */
28026
28027
28028 Roo.bootstrap.menu.Menu = function(config){
28029     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28030     
28031     this.addEvents({
28032         /**
28033          * @event beforeshow
28034          * Fires before this menu is displayed
28035          * @param {Roo.bootstrap.menu.Menu} this
28036          */
28037         beforeshow : true,
28038         /**
28039          * @event beforehide
28040          * Fires before this menu is hidden
28041          * @param {Roo.bootstrap.menu.Menu} this
28042          */
28043         beforehide : true,
28044         /**
28045          * @event show
28046          * Fires after this menu is displayed
28047          * @param {Roo.bootstrap.menu.Menu} this
28048          */
28049         show : true,
28050         /**
28051          * @event hide
28052          * Fires after this menu is hidden
28053          * @param {Roo.bootstrap.menu.Menu} this
28054          */
28055         hide : true,
28056         /**
28057          * @event click
28058          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28059          * @param {Roo.bootstrap.menu.Menu} this
28060          * @param {Roo.EventObject} e
28061          */
28062         click : true
28063     });
28064     
28065 };
28066
28067 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28068     
28069     submenu : false,
28070     html : '',
28071     weight : 'default',
28072     icon : false,
28073     pos : 'bottom',
28074     
28075     
28076     getChildContainer : function() {
28077         if(this.isSubMenu){
28078             return this.el;
28079         }
28080         
28081         return this.el.select('ul.dropdown-menu', true).first();  
28082     },
28083     
28084     getAutoCreate : function()
28085     {
28086         var text = [
28087             {
28088                 tag : 'span',
28089                 cls : 'roo-menu-text',
28090                 html : this.html
28091             }
28092         ];
28093         
28094         if(this.icon){
28095             text.unshift({
28096                 tag : 'i',
28097                 cls : 'fa ' + this.icon
28098             })
28099         }
28100         
28101         
28102         var cfg = {
28103             tag : 'div',
28104             cls : 'btn-group',
28105             cn : [
28106                 {
28107                     tag : 'button',
28108                     cls : 'dropdown-button btn btn-' + this.weight,
28109                     cn : text
28110                 },
28111                 {
28112                     tag : 'button',
28113                     cls : 'dropdown-toggle btn btn-' + this.weight,
28114                     cn : [
28115                         {
28116                             tag : 'span',
28117                             cls : 'caret'
28118                         }
28119                     ]
28120                 },
28121                 {
28122                     tag : 'ul',
28123                     cls : 'dropdown-menu'
28124                 }
28125             ]
28126             
28127         };
28128         
28129         if(this.pos == 'top'){
28130             cfg.cls += ' dropup';
28131         }
28132         
28133         if(this.isSubMenu){
28134             cfg = {
28135                 tag : 'ul',
28136                 cls : 'dropdown-menu'
28137             }
28138         }
28139         
28140         return cfg;
28141     },
28142     
28143     onRender : function(ct, position)
28144     {
28145         this.isSubMenu = ct.hasClass('dropdown-submenu');
28146         
28147         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28148     },
28149     
28150     initEvents : function() 
28151     {
28152         if(this.isSubMenu){
28153             return;
28154         }
28155         
28156         this.hidden = true;
28157         
28158         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28159         this.triggerEl.on('click', this.onTriggerPress, this);
28160         
28161         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28162         this.buttonEl.on('click', this.onClick, this);
28163         
28164     },
28165     
28166     list : function()
28167     {
28168         if(this.isSubMenu){
28169             return this.el;
28170         }
28171         
28172         return this.el.select('ul.dropdown-menu', true).first();
28173     },
28174     
28175     onClick : function(e)
28176     {
28177         this.fireEvent("click", this, e);
28178     },
28179     
28180     onTriggerPress  : function(e)
28181     {   
28182         if (this.isVisible()) {
28183             this.hide();
28184         } else {
28185             this.show();
28186         }
28187     },
28188     
28189     isVisible : function(){
28190         return !this.hidden;
28191     },
28192     
28193     show : function()
28194     {
28195         this.fireEvent("beforeshow", this);
28196         
28197         this.hidden = false;
28198         this.el.addClass('open');
28199         
28200         Roo.get(document).on("mouseup", this.onMouseUp, this);
28201         
28202         this.fireEvent("show", this);
28203         
28204         
28205     },
28206     
28207     hide : function()
28208     {
28209         this.fireEvent("beforehide", this);
28210         
28211         this.hidden = true;
28212         this.el.removeClass('open');
28213         
28214         Roo.get(document).un("mouseup", this.onMouseUp);
28215         
28216         this.fireEvent("hide", this);
28217     },
28218     
28219     onMouseUp : function()
28220     {
28221         this.hide();
28222     }
28223     
28224 });
28225
28226  
28227  /*
28228  * - LGPL
28229  *
28230  * menu item
28231  * 
28232  */
28233 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28234
28235 /**
28236  * @class Roo.bootstrap.menu.Item
28237  * @extends Roo.bootstrap.Component
28238  * Bootstrap MenuItem class
28239  * @cfg {Boolean} submenu (true | false) default false
28240  * @cfg {String} html text of the item
28241  * @cfg {String} href the link
28242  * @cfg {Boolean} disable (true | false) default false
28243  * @cfg {Boolean} preventDefault (true | false) default true
28244  * @cfg {String} icon Font awesome icon
28245  * @cfg {String} pos Submenu align to (left | right) default right 
28246  * 
28247  * 
28248  * @constructor
28249  * Create a new Item
28250  * @param {Object} config The config object
28251  */
28252
28253
28254 Roo.bootstrap.menu.Item = function(config){
28255     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28256     this.addEvents({
28257         /**
28258          * @event mouseover
28259          * Fires when the mouse is hovering over this menu
28260          * @param {Roo.bootstrap.menu.Item} this
28261          * @param {Roo.EventObject} e
28262          */
28263         mouseover : true,
28264         /**
28265          * @event mouseout
28266          * Fires when the mouse exits this menu
28267          * @param {Roo.bootstrap.menu.Item} this
28268          * @param {Roo.EventObject} e
28269          */
28270         mouseout : true,
28271         // raw events
28272         /**
28273          * @event click
28274          * The raw click event for the entire grid.
28275          * @param {Roo.EventObject} e
28276          */
28277         click : true
28278     });
28279 };
28280
28281 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28282     
28283     submenu : false,
28284     href : '',
28285     html : '',
28286     preventDefault: true,
28287     disable : false,
28288     icon : false,
28289     pos : 'right',
28290     
28291     getAutoCreate : function()
28292     {
28293         var text = [
28294             {
28295                 tag : 'span',
28296                 cls : 'roo-menu-item-text',
28297                 html : this.html
28298             }
28299         ];
28300         
28301         if(this.icon){
28302             text.unshift({
28303                 tag : 'i',
28304                 cls : 'fa ' + this.icon
28305             })
28306         }
28307         
28308         var cfg = {
28309             tag : 'li',
28310             cn : [
28311                 {
28312                     tag : 'a',
28313                     href : this.href || '#',
28314                     cn : text
28315                 }
28316             ]
28317         };
28318         
28319         if(this.disable){
28320             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28321         }
28322         
28323         if(this.submenu){
28324             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28325             
28326             if(this.pos == 'left'){
28327                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28328             }
28329         }
28330         
28331         return cfg;
28332     },
28333     
28334     initEvents : function() 
28335     {
28336         this.el.on('mouseover', this.onMouseOver, this);
28337         this.el.on('mouseout', this.onMouseOut, this);
28338         
28339         this.el.select('a', true).first().on('click', this.onClick, this);
28340         
28341     },
28342     
28343     onClick : function(e)
28344     {
28345         if(this.preventDefault){
28346             e.preventDefault();
28347         }
28348         
28349         this.fireEvent("click", this, e);
28350     },
28351     
28352     onMouseOver : function(e)
28353     {
28354         if(this.submenu && this.pos == 'left'){
28355             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28356         }
28357         
28358         this.fireEvent("mouseover", this, e);
28359     },
28360     
28361     onMouseOut : function(e)
28362     {
28363         this.fireEvent("mouseout", this, e);
28364     }
28365 });
28366
28367  
28368
28369  /*
28370  * - LGPL
28371  *
28372  * menu separator
28373  * 
28374  */
28375 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28376
28377 /**
28378  * @class Roo.bootstrap.menu.Separator
28379  * @extends Roo.bootstrap.Component
28380  * Bootstrap Separator class
28381  * 
28382  * @constructor
28383  * Create a new Separator
28384  * @param {Object} config The config object
28385  */
28386
28387
28388 Roo.bootstrap.menu.Separator = function(config){
28389     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28390 };
28391
28392 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28393     
28394     getAutoCreate : function(){
28395         var cfg = {
28396             tag : 'li',
28397             cls: 'divider'
28398         };
28399         
28400         return cfg;
28401     }
28402    
28403 });
28404
28405  
28406
28407  /*
28408  * - LGPL
28409  *
28410  * Tooltip
28411  * 
28412  */
28413
28414 /**
28415  * @class Roo.bootstrap.Tooltip
28416  * Bootstrap Tooltip class
28417  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28418  * to determine which dom element triggers the tooltip.
28419  * 
28420  * It needs to add support for additional attributes like tooltip-position
28421  * 
28422  * @constructor
28423  * Create a new Toolti
28424  * @param {Object} config The config object
28425  */
28426
28427 Roo.bootstrap.Tooltip = function(config){
28428     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28429     
28430     this.alignment = Roo.bootstrap.Tooltip.alignment;
28431     
28432     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28433         this.alignment = config.alignment;
28434     }
28435     
28436 };
28437
28438 Roo.apply(Roo.bootstrap.Tooltip, {
28439     /**
28440      * @function init initialize tooltip monitoring.
28441      * @static
28442      */
28443     currentEl : false,
28444     currentTip : false,
28445     currentRegion : false,
28446     
28447     //  init : delay?
28448     
28449     init : function()
28450     {
28451         Roo.get(document).on('mouseover', this.enter ,this);
28452         Roo.get(document).on('mouseout', this.leave, this);
28453          
28454         
28455         this.currentTip = new Roo.bootstrap.Tooltip();
28456     },
28457     
28458     enter : function(ev)
28459     {
28460         var dom = ev.getTarget();
28461         
28462         //Roo.log(['enter',dom]);
28463         var el = Roo.fly(dom);
28464         if (this.currentEl) {
28465             //Roo.log(dom);
28466             //Roo.log(this.currentEl);
28467             //Roo.log(this.currentEl.contains(dom));
28468             if (this.currentEl == el) {
28469                 return;
28470             }
28471             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28472                 return;
28473             }
28474
28475         }
28476         
28477         if (this.currentTip.el) {
28478             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28479         }    
28480         //Roo.log(ev);
28481         
28482         if(!el || el.dom == document){
28483             return;
28484         }
28485         
28486         var bindEl = el;
28487         
28488         // you can not look for children, as if el is the body.. then everythign is the child..
28489         if (!el.attr('tooltip')) { //
28490             if (!el.select("[tooltip]").elements.length) {
28491                 return;
28492             }
28493             // is the mouse over this child...?
28494             bindEl = el.select("[tooltip]").first();
28495             var xy = ev.getXY();
28496             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28497                 //Roo.log("not in region.");
28498                 return;
28499             }
28500             //Roo.log("child element over..");
28501             
28502         }
28503         this.currentEl = bindEl;
28504         this.currentTip.bind(bindEl);
28505         this.currentRegion = Roo.lib.Region.getRegion(dom);
28506         this.currentTip.enter();
28507         
28508     },
28509     leave : function(ev)
28510     {
28511         var dom = ev.getTarget();
28512         //Roo.log(['leave',dom]);
28513         if (!this.currentEl) {
28514             return;
28515         }
28516         
28517         
28518         if (dom != this.currentEl.dom) {
28519             return;
28520         }
28521         var xy = ev.getXY();
28522         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28523             return;
28524         }
28525         // only activate leave if mouse cursor is outside... bounding box..
28526         
28527         
28528         
28529         
28530         if (this.currentTip) {
28531             this.currentTip.leave();
28532         }
28533         //Roo.log('clear currentEl');
28534         this.currentEl = false;
28535         
28536         
28537     },
28538     alignment : {
28539         'left' : ['r-l', [-2,0], 'right'],
28540         'right' : ['l-r', [2,0], 'left'],
28541         'bottom' : ['t-b', [0,2], 'top'],
28542         'top' : [ 'b-t', [0,-2], 'bottom']
28543     }
28544     
28545 });
28546
28547
28548 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28549     
28550     
28551     bindEl : false,
28552     
28553     delay : null, // can be { show : 300 , hide: 500}
28554     
28555     timeout : null,
28556     
28557     hoverState : null, //???
28558     
28559     placement : 'bottom', 
28560     
28561     alignment : false,
28562     
28563     getAutoCreate : function(){
28564     
28565         var cfg = {
28566            cls : 'tooltip',   
28567            role : 'tooltip',
28568            cn : [
28569                 {
28570                     cls : 'tooltip-arrow arrow'
28571                 },
28572                 {
28573                     cls : 'tooltip-inner'
28574                 }
28575            ]
28576         };
28577         
28578         return cfg;
28579     },
28580     bind : function(el)
28581     {
28582         this.bindEl = el;
28583     },
28584     
28585     initEvents : function()
28586     {
28587         this.arrowEl = this.el.select('.arrow', true).first();
28588         this.innerEl = this.el.select('.tooltip-inner', true).first();
28589     },
28590     
28591     enter : function () {
28592        
28593         if (this.timeout != null) {
28594             clearTimeout(this.timeout);
28595         }
28596         
28597         this.hoverState = 'in';
28598          //Roo.log("enter - show");
28599         if (!this.delay || !this.delay.show) {
28600             this.show();
28601             return;
28602         }
28603         var _t = this;
28604         this.timeout = setTimeout(function () {
28605             if (_t.hoverState == 'in') {
28606                 _t.show();
28607             }
28608         }, this.delay.show);
28609     },
28610     leave : function()
28611     {
28612         clearTimeout(this.timeout);
28613     
28614         this.hoverState = 'out';
28615          if (!this.delay || !this.delay.hide) {
28616             this.hide();
28617             return;
28618         }
28619        
28620         var _t = this;
28621         this.timeout = setTimeout(function () {
28622             //Roo.log("leave - timeout");
28623             
28624             if (_t.hoverState == 'out') {
28625                 _t.hide();
28626                 Roo.bootstrap.Tooltip.currentEl = false;
28627             }
28628         }, delay);
28629     },
28630     
28631     show : function (msg)
28632     {
28633         if (!this.el) {
28634             this.render(document.body);
28635         }
28636         // set content.
28637         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28638         
28639         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28640         
28641         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28642         
28643         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28644                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28645         
28646         var placement = typeof this.placement == 'function' ?
28647             this.placement.call(this, this.el, on_el) :
28648             this.placement;
28649             
28650         var autoToken = /\s?auto?\s?/i;
28651         var autoPlace = autoToken.test(placement);
28652         if (autoPlace) {
28653             placement = placement.replace(autoToken, '') || 'top';
28654         }
28655         
28656         //this.el.detach()
28657         //this.el.setXY([0,0]);
28658         this.el.show();
28659         //this.el.dom.style.display='block';
28660         
28661         //this.el.appendTo(on_el);
28662         
28663         var p = this.getPosition();
28664         var box = this.el.getBox();
28665         
28666         if (autoPlace) {
28667             // fixme..
28668         }
28669         
28670         var align = this.alignment[placement];
28671         
28672         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28673         
28674         if(placement == 'top' || placement == 'bottom'){
28675             if(xy[0] < 0){
28676                 placement = 'right';
28677             }
28678             
28679             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28680                 placement = 'left';
28681             }
28682             
28683             var scroll = Roo.select('body', true).first().getScroll();
28684             
28685             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28686                 placement = 'top';
28687             }
28688             
28689             align = this.alignment[placement];
28690             
28691             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28692             
28693         }
28694         
28695         this.el.alignTo(this.bindEl, align[0],align[1]);
28696         //var arrow = this.el.select('.arrow',true).first();
28697         //arrow.set(align[2], 
28698         
28699         this.el.addClass(placement);
28700         this.el.addClass("bs-tooltip-"+ placement);
28701         
28702         this.el.addClass('in fade show');
28703         
28704         this.hoverState = null;
28705         
28706         if (this.el.hasClass('fade')) {
28707             // fade it?
28708         }
28709         
28710         
28711         
28712         
28713         
28714     },
28715     hide : function()
28716     {
28717          
28718         if (!this.el) {
28719             return;
28720         }
28721         //this.el.setXY([0,0]);
28722         this.el.removeClass(['show', 'in']);
28723         //this.el.hide();
28724         
28725     }
28726     
28727 });
28728  
28729
28730  /*
28731  * - LGPL
28732  *
28733  * Location Picker
28734  * 
28735  */
28736
28737 /**
28738  * @class Roo.bootstrap.LocationPicker
28739  * @extends Roo.bootstrap.Component
28740  * Bootstrap LocationPicker class
28741  * @cfg {Number} latitude Position when init default 0
28742  * @cfg {Number} longitude Position when init default 0
28743  * @cfg {Number} zoom default 15
28744  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28745  * @cfg {Boolean} mapTypeControl default false
28746  * @cfg {Boolean} disableDoubleClickZoom default false
28747  * @cfg {Boolean} scrollwheel default true
28748  * @cfg {Boolean} streetViewControl default false
28749  * @cfg {Number} radius default 0
28750  * @cfg {String} locationName
28751  * @cfg {Boolean} draggable default true
28752  * @cfg {Boolean} enableAutocomplete default false
28753  * @cfg {Boolean} enableReverseGeocode default true
28754  * @cfg {String} markerTitle
28755  * 
28756  * @constructor
28757  * Create a new LocationPicker
28758  * @param {Object} config The config object
28759  */
28760
28761
28762 Roo.bootstrap.LocationPicker = function(config){
28763     
28764     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28765     
28766     this.addEvents({
28767         /**
28768          * @event initial
28769          * Fires when the picker initialized.
28770          * @param {Roo.bootstrap.LocationPicker} this
28771          * @param {Google Location} location
28772          */
28773         initial : true,
28774         /**
28775          * @event positionchanged
28776          * Fires when the picker position changed.
28777          * @param {Roo.bootstrap.LocationPicker} this
28778          * @param {Google Location} location
28779          */
28780         positionchanged : true,
28781         /**
28782          * @event resize
28783          * Fires when the map resize.
28784          * @param {Roo.bootstrap.LocationPicker} this
28785          */
28786         resize : true,
28787         /**
28788          * @event show
28789          * Fires when the map show.
28790          * @param {Roo.bootstrap.LocationPicker} this
28791          */
28792         show : true,
28793         /**
28794          * @event hide
28795          * Fires when the map hide.
28796          * @param {Roo.bootstrap.LocationPicker} this
28797          */
28798         hide : true,
28799         /**
28800          * @event mapClick
28801          * Fires when click the map.
28802          * @param {Roo.bootstrap.LocationPicker} this
28803          * @param {Map event} e
28804          */
28805         mapClick : true,
28806         /**
28807          * @event mapRightClick
28808          * Fires when right click the map.
28809          * @param {Roo.bootstrap.LocationPicker} this
28810          * @param {Map event} e
28811          */
28812         mapRightClick : true,
28813         /**
28814          * @event markerClick
28815          * Fires when click the marker.
28816          * @param {Roo.bootstrap.LocationPicker} this
28817          * @param {Map event} e
28818          */
28819         markerClick : true,
28820         /**
28821          * @event markerRightClick
28822          * Fires when right click the marker.
28823          * @param {Roo.bootstrap.LocationPicker} this
28824          * @param {Map event} e
28825          */
28826         markerRightClick : true,
28827         /**
28828          * @event OverlayViewDraw
28829          * Fires when OverlayView Draw
28830          * @param {Roo.bootstrap.LocationPicker} this
28831          */
28832         OverlayViewDraw : true,
28833         /**
28834          * @event OverlayViewOnAdd
28835          * Fires when OverlayView Draw
28836          * @param {Roo.bootstrap.LocationPicker} this
28837          */
28838         OverlayViewOnAdd : true,
28839         /**
28840          * @event OverlayViewOnRemove
28841          * Fires when OverlayView Draw
28842          * @param {Roo.bootstrap.LocationPicker} this
28843          */
28844         OverlayViewOnRemove : true,
28845         /**
28846          * @event OverlayViewShow
28847          * Fires when OverlayView Draw
28848          * @param {Roo.bootstrap.LocationPicker} this
28849          * @param {Pixel} cpx
28850          */
28851         OverlayViewShow : true,
28852         /**
28853          * @event OverlayViewHide
28854          * Fires when OverlayView Draw
28855          * @param {Roo.bootstrap.LocationPicker} this
28856          */
28857         OverlayViewHide : true,
28858         /**
28859          * @event loadexception
28860          * Fires when load google lib failed.
28861          * @param {Roo.bootstrap.LocationPicker} this
28862          */
28863         loadexception : true
28864     });
28865         
28866 };
28867
28868 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28869     
28870     gMapContext: false,
28871     
28872     latitude: 0,
28873     longitude: 0,
28874     zoom: 15,
28875     mapTypeId: false,
28876     mapTypeControl: false,
28877     disableDoubleClickZoom: false,
28878     scrollwheel: true,
28879     streetViewControl: false,
28880     radius: 0,
28881     locationName: '',
28882     draggable: true,
28883     enableAutocomplete: false,
28884     enableReverseGeocode: true,
28885     markerTitle: '',
28886     
28887     getAutoCreate: function()
28888     {
28889
28890         var cfg = {
28891             tag: 'div',
28892             cls: 'roo-location-picker'
28893         };
28894         
28895         return cfg
28896     },
28897     
28898     initEvents: function(ct, position)
28899     {       
28900         if(!this.el.getWidth() || this.isApplied()){
28901             return;
28902         }
28903         
28904         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28905         
28906         this.initial();
28907     },
28908     
28909     initial: function()
28910     {
28911         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28912             this.fireEvent('loadexception', this);
28913             return;
28914         }
28915         
28916         if(!this.mapTypeId){
28917             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28918         }
28919         
28920         this.gMapContext = this.GMapContext();
28921         
28922         this.initOverlayView();
28923         
28924         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28925         
28926         var _this = this;
28927                 
28928         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28929             _this.setPosition(_this.gMapContext.marker.position);
28930         });
28931         
28932         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28933             _this.fireEvent('mapClick', this, event);
28934             
28935         });
28936
28937         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28938             _this.fireEvent('mapRightClick', this, event);
28939             
28940         });
28941         
28942         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28943             _this.fireEvent('markerClick', this, event);
28944             
28945         });
28946
28947         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28948             _this.fireEvent('markerRightClick', this, event);
28949             
28950         });
28951         
28952         this.setPosition(this.gMapContext.location);
28953         
28954         this.fireEvent('initial', this, this.gMapContext.location);
28955     },
28956     
28957     initOverlayView: function()
28958     {
28959         var _this = this;
28960         
28961         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28962             
28963             draw: function()
28964             {
28965                 _this.fireEvent('OverlayViewDraw', _this);
28966             },
28967             
28968             onAdd: function()
28969             {
28970                 _this.fireEvent('OverlayViewOnAdd', _this);
28971             },
28972             
28973             onRemove: function()
28974             {
28975                 _this.fireEvent('OverlayViewOnRemove', _this);
28976             },
28977             
28978             show: function(cpx)
28979             {
28980                 _this.fireEvent('OverlayViewShow', _this, cpx);
28981             },
28982             
28983             hide: function()
28984             {
28985                 _this.fireEvent('OverlayViewHide', _this);
28986             }
28987             
28988         });
28989     },
28990     
28991     fromLatLngToContainerPixel: function(event)
28992     {
28993         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28994     },
28995     
28996     isApplied: function() 
28997     {
28998         return this.getGmapContext() == false ? false : true;
28999     },
29000     
29001     getGmapContext: function() 
29002     {
29003         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29004     },
29005     
29006     GMapContext: function() 
29007     {
29008         var position = new google.maps.LatLng(this.latitude, this.longitude);
29009         
29010         var _map = new google.maps.Map(this.el.dom, {
29011             center: position,
29012             zoom: this.zoom,
29013             mapTypeId: this.mapTypeId,
29014             mapTypeControl: this.mapTypeControl,
29015             disableDoubleClickZoom: this.disableDoubleClickZoom,
29016             scrollwheel: this.scrollwheel,
29017             streetViewControl: this.streetViewControl,
29018             locationName: this.locationName,
29019             draggable: this.draggable,
29020             enableAutocomplete: this.enableAutocomplete,
29021             enableReverseGeocode: this.enableReverseGeocode
29022         });
29023         
29024         var _marker = new google.maps.Marker({
29025             position: position,
29026             map: _map,
29027             title: this.markerTitle,
29028             draggable: this.draggable
29029         });
29030         
29031         return {
29032             map: _map,
29033             marker: _marker,
29034             circle: null,
29035             location: position,
29036             radius: this.radius,
29037             locationName: this.locationName,
29038             addressComponents: {
29039                 formatted_address: null,
29040                 addressLine1: null,
29041                 addressLine2: null,
29042                 streetName: null,
29043                 streetNumber: null,
29044                 city: null,
29045                 district: null,
29046                 state: null,
29047                 stateOrProvince: null
29048             },
29049             settings: this,
29050             domContainer: this.el.dom,
29051             geodecoder: new google.maps.Geocoder()
29052         };
29053     },
29054     
29055     drawCircle: function(center, radius, options) 
29056     {
29057         if (this.gMapContext.circle != null) {
29058             this.gMapContext.circle.setMap(null);
29059         }
29060         if (radius > 0) {
29061             radius *= 1;
29062             options = Roo.apply({}, options, {
29063                 strokeColor: "#0000FF",
29064                 strokeOpacity: .35,
29065                 strokeWeight: 2,
29066                 fillColor: "#0000FF",
29067                 fillOpacity: .2
29068             });
29069             
29070             options.map = this.gMapContext.map;
29071             options.radius = radius;
29072             options.center = center;
29073             this.gMapContext.circle = new google.maps.Circle(options);
29074             return this.gMapContext.circle;
29075         }
29076         
29077         return null;
29078     },
29079     
29080     setPosition: function(location) 
29081     {
29082         this.gMapContext.location = location;
29083         this.gMapContext.marker.setPosition(location);
29084         this.gMapContext.map.panTo(location);
29085         this.drawCircle(location, this.gMapContext.radius, {});
29086         
29087         var _this = this;
29088         
29089         if (this.gMapContext.settings.enableReverseGeocode) {
29090             this.gMapContext.geodecoder.geocode({
29091                 latLng: this.gMapContext.location
29092             }, function(results, status) {
29093                 
29094                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29095                     _this.gMapContext.locationName = results[0].formatted_address;
29096                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29097                     
29098                     _this.fireEvent('positionchanged', this, location);
29099                 }
29100             });
29101             
29102             return;
29103         }
29104         
29105         this.fireEvent('positionchanged', this, location);
29106     },
29107     
29108     resize: function()
29109     {
29110         google.maps.event.trigger(this.gMapContext.map, "resize");
29111         
29112         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29113         
29114         this.fireEvent('resize', this);
29115     },
29116     
29117     setPositionByLatLng: function(latitude, longitude)
29118     {
29119         this.setPosition(new google.maps.LatLng(latitude, longitude));
29120     },
29121     
29122     getCurrentPosition: function() 
29123     {
29124         return {
29125             latitude: this.gMapContext.location.lat(),
29126             longitude: this.gMapContext.location.lng()
29127         };
29128     },
29129     
29130     getAddressName: function() 
29131     {
29132         return this.gMapContext.locationName;
29133     },
29134     
29135     getAddressComponents: function() 
29136     {
29137         return this.gMapContext.addressComponents;
29138     },
29139     
29140     address_component_from_google_geocode: function(address_components) 
29141     {
29142         var result = {};
29143         
29144         for (var i = 0; i < address_components.length; i++) {
29145             var component = address_components[i];
29146             if (component.types.indexOf("postal_code") >= 0) {
29147                 result.postalCode = component.short_name;
29148             } else if (component.types.indexOf("street_number") >= 0) {
29149                 result.streetNumber = component.short_name;
29150             } else if (component.types.indexOf("route") >= 0) {
29151                 result.streetName = component.short_name;
29152             } else if (component.types.indexOf("neighborhood") >= 0) {
29153                 result.city = component.short_name;
29154             } else if (component.types.indexOf("locality") >= 0) {
29155                 result.city = component.short_name;
29156             } else if (component.types.indexOf("sublocality") >= 0) {
29157                 result.district = component.short_name;
29158             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29159                 result.stateOrProvince = component.short_name;
29160             } else if (component.types.indexOf("country") >= 0) {
29161                 result.country = component.short_name;
29162             }
29163         }
29164         
29165         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29166         result.addressLine2 = "";
29167         return result;
29168     },
29169     
29170     setZoomLevel: function(zoom)
29171     {
29172         this.gMapContext.map.setZoom(zoom);
29173     },
29174     
29175     show: function()
29176     {
29177         if(!this.el){
29178             return;
29179         }
29180         
29181         this.el.show();
29182         
29183         this.resize();
29184         
29185         this.fireEvent('show', this);
29186     },
29187     
29188     hide: function()
29189     {
29190         if(!this.el){
29191             return;
29192         }
29193         
29194         this.el.hide();
29195         
29196         this.fireEvent('hide', this);
29197     }
29198     
29199 });
29200
29201 Roo.apply(Roo.bootstrap.LocationPicker, {
29202     
29203     OverlayView : function(map, options)
29204     {
29205         options = options || {};
29206         
29207         this.setMap(map);
29208     }
29209     
29210     
29211 });/**
29212  * @class Roo.bootstrap.Alert
29213  * @extends Roo.bootstrap.Component
29214  * Bootstrap Alert class - shows an alert area box
29215  * eg
29216  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29217   Enter a valid email address
29218 </div>
29219  * @licence LGPL
29220  * @cfg {String} title The title of alert
29221  * @cfg {String} html The content of alert
29222  * @cfg {String} weight (  success | info | warning | danger )
29223  * @cfg {String} faicon font-awesomeicon
29224  * 
29225  * @constructor
29226  * Create a new alert
29227  * @param {Object} config The config object
29228  */
29229
29230
29231 Roo.bootstrap.Alert = function(config){
29232     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29233     
29234 };
29235
29236 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29237     
29238     title: '',
29239     html: '',
29240     weight: false,
29241     faicon: false,
29242     
29243     getAutoCreate : function()
29244     {
29245         
29246         var cfg = {
29247             tag : 'div',
29248             cls : 'alert',
29249             cn : [
29250                 {
29251                     tag : 'i',
29252                     cls : 'roo-alert-icon'
29253                     
29254                 },
29255                 {
29256                     tag : 'b',
29257                     cls : 'roo-alert-title',
29258                     html : this.title
29259                 },
29260                 {
29261                     tag : 'span',
29262                     cls : 'roo-alert-text',
29263                     html : this.html
29264                 }
29265             ]
29266         };
29267         
29268         if(this.faicon){
29269             cfg.cn[0].cls += ' fa ' + this.faicon;
29270         }
29271         
29272         if(this.weight){
29273             cfg.cls += ' alert-' + this.weight;
29274         }
29275         
29276         return cfg;
29277     },
29278     
29279     initEvents: function() 
29280     {
29281         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29282     },
29283     
29284     setTitle : function(str)
29285     {
29286         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29287     },
29288     
29289     setText : function(str)
29290     {
29291         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29292     },
29293     
29294     setWeight : function(weight)
29295     {
29296         if(this.weight){
29297             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29298         }
29299         
29300         this.weight = weight;
29301         
29302         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29303     },
29304     
29305     setIcon : function(icon)
29306     {
29307         if(this.faicon){
29308             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29309         }
29310         
29311         this.faicon = icon;
29312         
29313         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29314     },
29315     
29316     hide: function() 
29317     {
29318         this.el.hide();   
29319     },
29320     
29321     show: function() 
29322     {  
29323         this.el.show();   
29324     }
29325     
29326 });
29327
29328  
29329 /*
29330 * Licence: LGPL
29331 */
29332
29333 /**
29334  * @class Roo.bootstrap.UploadCropbox
29335  * @extends Roo.bootstrap.Component
29336  * Bootstrap UploadCropbox class
29337  * @cfg {String} emptyText show when image has been loaded
29338  * @cfg {String} rotateNotify show when image too small to rotate
29339  * @cfg {Number} errorTimeout default 3000
29340  * @cfg {Number} minWidth default 300
29341  * @cfg {Number} minHeight default 300
29342  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29343  * @cfg {Boolean} isDocument (true|false) default false
29344  * @cfg {String} url action url
29345  * @cfg {String} paramName default 'imageUpload'
29346  * @cfg {String} method default POST
29347  * @cfg {Boolean} loadMask (true|false) default true
29348  * @cfg {Boolean} loadingText default 'Loading...'
29349  * 
29350  * @constructor
29351  * Create a new UploadCropbox
29352  * @param {Object} config The config object
29353  */
29354
29355 Roo.bootstrap.UploadCropbox = function(config){
29356     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29357     
29358     this.addEvents({
29359         /**
29360          * @event beforeselectfile
29361          * Fire before select file
29362          * @param {Roo.bootstrap.UploadCropbox} this
29363          */
29364         "beforeselectfile" : true,
29365         /**
29366          * @event initial
29367          * Fire after initEvent
29368          * @param {Roo.bootstrap.UploadCropbox} this
29369          */
29370         "initial" : true,
29371         /**
29372          * @event crop
29373          * Fire after initEvent
29374          * @param {Roo.bootstrap.UploadCropbox} this
29375          * @param {String} data
29376          */
29377         "crop" : true,
29378         /**
29379          * @event prepare
29380          * Fire when preparing the file data
29381          * @param {Roo.bootstrap.UploadCropbox} this
29382          * @param {Object} file
29383          */
29384         "prepare" : true,
29385         /**
29386          * @event exception
29387          * Fire when get exception
29388          * @param {Roo.bootstrap.UploadCropbox} this
29389          * @param {XMLHttpRequest} xhr
29390          */
29391         "exception" : true,
29392         /**
29393          * @event beforeloadcanvas
29394          * Fire before load the canvas
29395          * @param {Roo.bootstrap.UploadCropbox} this
29396          * @param {String} src
29397          */
29398         "beforeloadcanvas" : true,
29399         /**
29400          * @event trash
29401          * Fire when trash image
29402          * @param {Roo.bootstrap.UploadCropbox} this
29403          */
29404         "trash" : true,
29405         /**
29406          * @event download
29407          * Fire when download the image
29408          * @param {Roo.bootstrap.UploadCropbox} this
29409          */
29410         "download" : true,
29411         /**
29412          * @event footerbuttonclick
29413          * Fire when footerbuttonclick
29414          * @param {Roo.bootstrap.UploadCropbox} this
29415          * @param {String} type
29416          */
29417         "footerbuttonclick" : true,
29418         /**
29419          * @event resize
29420          * Fire when resize
29421          * @param {Roo.bootstrap.UploadCropbox} this
29422          */
29423         "resize" : true,
29424         /**
29425          * @event rotate
29426          * Fire when rotate the image
29427          * @param {Roo.bootstrap.UploadCropbox} this
29428          * @param {String} pos
29429          */
29430         "rotate" : true,
29431         /**
29432          * @event inspect
29433          * Fire when inspect the file
29434          * @param {Roo.bootstrap.UploadCropbox} this
29435          * @param {Object} file
29436          */
29437         "inspect" : true,
29438         /**
29439          * @event upload
29440          * Fire when xhr upload the file
29441          * @param {Roo.bootstrap.UploadCropbox} this
29442          * @param {Object} data
29443          */
29444         "upload" : true,
29445         /**
29446          * @event arrange
29447          * Fire when arrange the file data
29448          * @param {Roo.bootstrap.UploadCropbox} this
29449          * @param {Object} formData
29450          */
29451         "arrange" : true
29452     });
29453     
29454     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29455 };
29456
29457 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29458     
29459     emptyText : 'Click to upload image',
29460     rotateNotify : 'Image is too small to rotate',
29461     errorTimeout : 3000,
29462     scale : 0,
29463     baseScale : 1,
29464     rotate : 0,
29465     dragable : false,
29466     pinching : false,
29467     mouseX : 0,
29468     mouseY : 0,
29469     cropData : false,
29470     minWidth : 300,
29471     minHeight : 300,
29472     file : false,
29473     exif : {},
29474     baseRotate : 1,
29475     cropType : 'image/jpeg',
29476     buttons : false,
29477     canvasLoaded : false,
29478     isDocument : false,
29479     method : 'POST',
29480     paramName : 'imageUpload',
29481     loadMask : true,
29482     loadingText : 'Loading...',
29483     maskEl : false,
29484     
29485     getAutoCreate : function()
29486     {
29487         var cfg = {
29488             tag : 'div',
29489             cls : 'roo-upload-cropbox',
29490             cn : [
29491                 {
29492                     tag : 'input',
29493                     cls : 'roo-upload-cropbox-selector',
29494                     type : 'file'
29495                 },
29496                 {
29497                     tag : 'div',
29498                     cls : 'roo-upload-cropbox-body',
29499                     style : 'cursor:pointer',
29500                     cn : [
29501                         {
29502                             tag : 'div',
29503                             cls : 'roo-upload-cropbox-preview'
29504                         },
29505                         {
29506                             tag : 'div',
29507                             cls : 'roo-upload-cropbox-thumb'
29508                         },
29509                         {
29510                             tag : 'div',
29511                             cls : 'roo-upload-cropbox-empty-notify',
29512                             html : this.emptyText
29513                         },
29514                         {
29515                             tag : 'div',
29516                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29517                             html : this.rotateNotify
29518                         }
29519                     ]
29520                 },
29521                 {
29522                     tag : 'div',
29523                     cls : 'roo-upload-cropbox-footer',
29524                     cn : {
29525                         tag : 'div',
29526                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29527                         cn : []
29528                     }
29529                 }
29530             ]
29531         };
29532         
29533         return cfg;
29534     },
29535     
29536     onRender : function(ct, position)
29537     {
29538         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29539         
29540         if (this.buttons.length) {
29541             
29542             Roo.each(this.buttons, function(bb) {
29543                 
29544                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29545                 
29546                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29547                 
29548             }, this);
29549         }
29550         
29551         if(this.loadMask){
29552             this.maskEl = this.el;
29553         }
29554     },
29555     
29556     initEvents : function()
29557     {
29558         this.urlAPI = (window.createObjectURL && window) || 
29559                                 (window.URL && URL.revokeObjectURL && URL) || 
29560                                 (window.webkitURL && webkitURL);
29561                         
29562         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29563         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29564         
29565         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29566         this.selectorEl.hide();
29567         
29568         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29569         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29570         
29571         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29572         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29573         this.thumbEl.hide();
29574         
29575         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29576         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29577         
29578         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29579         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29580         this.errorEl.hide();
29581         
29582         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29583         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29584         this.footerEl.hide();
29585         
29586         this.setThumbBoxSize();
29587         
29588         this.bind();
29589         
29590         this.resize();
29591         
29592         this.fireEvent('initial', this);
29593     },
29594
29595     bind : function()
29596     {
29597         var _this = this;
29598         
29599         window.addEventListener("resize", function() { _this.resize(); } );
29600         
29601         this.bodyEl.on('click', this.beforeSelectFile, this);
29602         
29603         if(Roo.isTouch){
29604             this.bodyEl.on('touchstart', this.onTouchStart, this);
29605             this.bodyEl.on('touchmove', this.onTouchMove, this);
29606             this.bodyEl.on('touchend', this.onTouchEnd, this);
29607         }
29608         
29609         if(!Roo.isTouch){
29610             this.bodyEl.on('mousedown', this.onMouseDown, this);
29611             this.bodyEl.on('mousemove', this.onMouseMove, this);
29612             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29613             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29614             Roo.get(document).on('mouseup', this.onMouseUp, this);
29615         }
29616         
29617         this.selectorEl.on('change', this.onFileSelected, this);
29618     },
29619     
29620     reset : function()
29621     {    
29622         this.scale = 0;
29623         this.baseScale = 1;
29624         this.rotate = 0;
29625         this.baseRotate = 1;
29626         this.dragable = false;
29627         this.pinching = false;
29628         this.mouseX = 0;
29629         this.mouseY = 0;
29630         this.cropData = false;
29631         this.notifyEl.dom.innerHTML = this.emptyText;
29632         
29633         this.selectorEl.dom.value = '';
29634         
29635     },
29636     
29637     resize : function()
29638     {
29639         if(this.fireEvent('resize', this) != false){
29640             this.setThumbBoxPosition();
29641             this.setCanvasPosition();
29642         }
29643     },
29644     
29645     onFooterButtonClick : function(e, el, o, type)
29646     {
29647         switch (type) {
29648             case 'rotate-left' :
29649                 this.onRotateLeft(e);
29650                 break;
29651             case 'rotate-right' :
29652                 this.onRotateRight(e);
29653                 break;
29654             case 'picture' :
29655                 this.beforeSelectFile(e);
29656                 break;
29657             case 'trash' :
29658                 this.trash(e);
29659                 break;
29660             case 'crop' :
29661                 this.crop(e);
29662                 break;
29663             case 'download' :
29664                 this.download(e);
29665                 break;
29666             default :
29667                 break;
29668         }
29669         
29670         this.fireEvent('footerbuttonclick', this, type);
29671     },
29672     
29673     beforeSelectFile : function(e)
29674     {
29675         e.preventDefault();
29676         
29677         if(this.fireEvent('beforeselectfile', this) != false){
29678             this.selectorEl.dom.click();
29679         }
29680     },
29681     
29682     onFileSelected : function(e)
29683     {
29684         e.preventDefault();
29685         
29686         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29687             return;
29688         }
29689         
29690         var file = this.selectorEl.dom.files[0];
29691         
29692         if(this.fireEvent('inspect', this, file) != false){
29693             this.prepare(file);
29694         }
29695         
29696     },
29697     
29698     trash : function(e)
29699     {
29700         this.fireEvent('trash', this);
29701     },
29702     
29703     download : function(e)
29704     {
29705         this.fireEvent('download', this);
29706     },
29707     
29708     loadCanvas : function(src)
29709     {   
29710         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29711             
29712             this.reset();
29713             
29714             this.imageEl = document.createElement('img');
29715             
29716             var _this = this;
29717             
29718             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29719             
29720             this.imageEl.src = src;
29721         }
29722     },
29723     
29724     onLoadCanvas : function()
29725     {   
29726         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29727         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29728         
29729         this.bodyEl.un('click', this.beforeSelectFile, this);
29730         
29731         this.notifyEl.hide();
29732         this.thumbEl.show();
29733         this.footerEl.show();
29734         
29735         this.baseRotateLevel();
29736         
29737         if(this.isDocument){
29738             this.setThumbBoxSize();
29739         }
29740         
29741         this.setThumbBoxPosition();
29742         
29743         this.baseScaleLevel();
29744         
29745         this.draw();
29746         
29747         this.resize();
29748         
29749         this.canvasLoaded = true;
29750         
29751         if(this.loadMask){
29752             this.maskEl.unmask();
29753         }
29754         
29755     },
29756     
29757     setCanvasPosition : function()
29758     {   
29759         if(!this.canvasEl){
29760             return;
29761         }
29762         
29763         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29764         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29765         
29766         this.previewEl.setLeft(pw);
29767         this.previewEl.setTop(ph);
29768         
29769     },
29770     
29771     onMouseDown : function(e)
29772     {   
29773         e.stopEvent();
29774         
29775         this.dragable = true;
29776         this.pinching = false;
29777         
29778         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29779             this.dragable = false;
29780             return;
29781         }
29782         
29783         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29784         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29785         
29786     },
29787     
29788     onMouseMove : function(e)
29789     {   
29790         e.stopEvent();
29791         
29792         if(!this.canvasLoaded){
29793             return;
29794         }
29795         
29796         if (!this.dragable){
29797             return;
29798         }
29799         
29800         var minX = Math.ceil(this.thumbEl.getLeft(true));
29801         var minY = Math.ceil(this.thumbEl.getTop(true));
29802         
29803         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29804         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29805         
29806         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29807         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29808         
29809         x = x - this.mouseX;
29810         y = y - this.mouseY;
29811         
29812         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29813         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29814         
29815         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29816         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29817         
29818         this.previewEl.setLeft(bgX);
29819         this.previewEl.setTop(bgY);
29820         
29821         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29822         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29823     },
29824     
29825     onMouseUp : function(e)
29826     {   
29827         e.stopEvent();
29828         
29829         this.dragable = false;
29830     },
29831     
29832     onMouseWheel : function(e)
29833     {   
29834         e.stopEvent();
29835         
29836         this.startScale = this.scale;
29837         
29838         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29839         
29840         if(!this.zoomable()){
29841             this.scale = this.startScale;
29842             return;
29843         }
29844         
29845         this.draw();
29846         
29847         return;
29848     },
29849     
29850     zoomable : function()
29851     {
29852         var minScale = this.thumbEl.getWidth() / this.minWidth;
29853         
29854         if(this.minWidth < this.minHeight){
29855             minScale = this.thumbEl.getHeight() / this.minHeight;
29856         }
29857         
29858         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29859         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29860         
29861         if(
29862                 this.isDocument &&
29863                 (this.rotate == 0 || this.rotate == 180) && 
29864                 (
29865                     width > this.imageEl.OriginWidth || 
29866                     height > this.imageEl.OriginHeight ||
29867                     (width < this.minWidth && height < this.minHeight)
29868                 )
29869         ){
29870             return false;
29871         }
29872         
29873         if(
29874                 this.isDocument &&
29875                 (this.rotate == 90 || this.rotate == 270) && 
29876                 (
29877                     width > this.imageEl.OriginWidth || 
29878                     height > this.imageEl.OriginHeight ||
29879                     (width < this.minHeight && height < this.minWidth)
29880                 )
29881         ){
29882             return false;
29883         }
29884         
29885         if(
29886                 !this.isDocument &&
29887                 (this.rotate == 0 || this.rotate == 180) && 
29888                 (
29889                     width < this.minWidth || 
29890                     width > this.imageEl.OriginWidth || 
29891                     height < this.minHeight || 
29892                     height > this.imageEl.OriginHeight
29893                 )
29894         ){
29895             return false;
29896         }
29897         
29898         if(
29899                 !this.isDocument &&
29900                 (this.rotate == 90 || this.rotate == 270) && 
29901                 (
29902                     width < this.minHeight || 
29903                     width > this.imageEl.OriginWidth || 
29904                     height < this.minWidth || 
29905                     height > this.imageEl.OriginHeight
29906                 )
29907         ){
29908             return false;
29909         }
29910         
29911         return true;
29912         
29913     },
29914     
29915     onRotateLeft : function(e)
29916     {   
29917         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29918             
29919             var minScale = this.thumbEl.getWidth() / this.minWidth;
29920             
29921             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29922             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29923             
29924             this.startScale = this.scale;
29925             
29926             while (this.getScaleLevel() < minScale){
29927             
29928                 this.scale = this.scale + 1;
29929                 
29930                 if(!this.zoomable()){
29931                     break;
29932                 }
29933                 
29934                 if(
29935                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29936                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29937                 ){
29938                     continue;
29939                 }
29940                 
29941                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29942
29943                 this.draw();
29944                 
29945                 return;
29946             }
29947             
29948             this.scale = this.startScale;
29949             
29950             this.onRotateFail();
29951             
29952             return false;
29953         }
29954         
29955         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29956
29957         if(this.isDocument){
29958             this.setThumbBoxSize();
29959             this.setThumbBoxPosition();
29960             this.setCanvasPosition();
29961         }
29962         
29963         this.draw();
29964         
29965         this.fireEvent('rotate', this, 'left');
29966         
29967     },
29968     
29969     onRotateRight : function(e)
29970     {
29971         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29972             
29973             var minScale = this.thumbEl.getWidth() / this.minWidth;
29974         
29975             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29976             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29977             
29978             this.startScale = this.scale;
29979             
29980             while (this.getScaleLevel() < minScale){
29981             
29982                 this.scale = this.scale + 1;
29983                 
29984                 if(!this.zoomable()){
29985                     break;
29986                 }
29987                 
29988                 if(
29989                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29990                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29991                 ){
29992                     continue;
29993                 }
29994                 
29995                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29996
29997                 this.draw();
29998                 
29999                 return;
30000             }
30001             
30002             this.scale = this.startScale;
30003             
30004             this.onRotateFail();
30005             
30006             return false;
30007         }
30008         
30009         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30010
30011         if(this.isDocument){
30012             this.setThumbBoxSize();
30013             this.setThumbBoxPosition();
30014             this.setCanvasPosition();
30015         }
30016         
30017         this.draw();
30018         
30019         this.fireEvent('rotate', this, 'right');
30020     },
30021     
30022     onRotateFail : function()
30023     {
30024         this.errorEl.show(true);
30025         
30026         var _this = this;
30027         
30028         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30029     },
30030     
30031     draw : function()
30032     {
30033         this.previewEl.dom.innerHTML = '';
30034         
30035         var canvasEl = document.createElement("canvas");
30036         
30037         var contextEl = canvasEl.getContext("2d");
30038         
30039         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30040         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30041         var center = this.imageEl.OriginWidth / 2;
30042         
30043         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30044             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30045             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30046             center = this.imageEl.OriginHeight / 2;
30047         }
30048         
30049         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30050         
30051         contextEl.translate(center, center);
30052         contextEl.rotate(this.rotate * Math.PI / 180);
30053
30054         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30055         
30056         this.canvasEl = document.createElement("canvas");
30057         
30058         this.contextEl = this.canvasEl.getContext("2d");
30059         
30060         switch (this.rotate) {
30061             case 0 :
30062                 
30063                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30064                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30065                 
30066                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30067                 
30068                 break;
30069             case 90 : 
30070                 
30071                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30072                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30073                 
30074                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30075                     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);
30076                     break;
30077                 }
30078                 
30079                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30080                 
30081                 break;
30082             case 180 :
30083                 
30084                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30085                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30086                 
30087                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30088                     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);
30089                     break;
30090                 }
30091                 
30092                 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);
30093                 
30094                 break;
30095             case 270 :
30096                 
30097                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30098                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30099         
30100                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30101                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30102                     break;
30103                 }
30104                 
30105                 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);
30106                 
30107                 break;
30108             default : 
30109                 break;
30110         }
30111         
30112         this.previewEl.appendChild(this.canvasEl);
30113         
30114         this.setCanvasPosition();
30115     },
30116     
30117     crop : function()
30118     {
30119         if(!this.canvasLoaded){
30120             return;
30121         }
30122         
30123         var imageCanvas = document.createElement("canvas");
30124         
30125         var imageContext = imageCanvas.getContext("2d");
30126         
30127         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30128         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30129         
30130         var center = imageCanvas.width / 2;
30131         
30132         imageContext.translate(center, center);
30133         
30134         imageContext.rotate(this.rotate * Math.PI / 180);
30135         
30136         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30137         
30138         var canvas = document.createElement("canvas");
30139         
30140         var context = canvas.getContext("2d");
30141                 
30142         canvas.width = this.minWidth;
30143         canvas.height = this.minHeight;
30144
30145         switch (this.rotate) {
30146             case 0 :
30147                 
30148                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30149                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30150                 
30151                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30152                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30153                 
30154                 var targetWidth = this.minWidth - 2 * x;
30155                 var targetHeight = this.minHeight - 2 * y;
30156                 
30157                 var scale = 1;
30158                 
30159                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30160                     scale = targetWidth / width;
30161                 }
30162                 
30163                 if(x > 0 && y == 0){
30164                     scale = targetHeight / height;
30165                 }
30166                 
30167                 if(x > 0 && y > 0){
30168                     scale = targetWidth / width;
30169                     
30170                     if(width < height){
30171                         scale = targetHeight / height;
30172                     }
30173                 }
30174                 
30175                 context.scale(scale, scale);
30176                 
30177                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30178                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30179
30180                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30181                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30182
30183                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30184                 
30185                 break;
30186             case 90 : 
30187                 
30188                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30189                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30190                 
30191                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30192                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30193                 
30194                 var targetWidth = this.minWidth - 2 * x;
30195                 var targetHeight = this.minHeight - 2 * y;
30196                 
30197                 var scale = 1;
30198                 
30199                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30200                     scale = targetWidth / width;
30201                 }
30202                 
30203                 if(x > 0 && y == 0){
30204                     scale = targetHeight / height;
30205                 }
30206                 
30207                 if(x > 0 && y > 0){
30208                     scale = targetWidth / width;
30209                     
30210                     if(width < height){
30211                         scale = targetHeight / height;
30212                     }
30213                 }
30214                 
30215                 context.scale(scale, scale);
30216                 
30217                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30218                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30219
30220                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30221                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30222                 
30223                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30224                 
30225                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30226                 
30227                 break;
30228             case 180 :
30229                 
30230                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30231                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30232                 
30233                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30234                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30235                 
30236                 var targetWidth = this.minWidth - 2 * x;
30237                 var targetHeight = this.minHeight - 2 * y;
30238                 
30239                 var scale = 1;
30240                 
30241                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30242                     scale = targetWidth / width;
30243                 }
30244                 
30245                 if(x > 0 && y == 0){
30246                     scale = targetHeight / height;
30247                 }
30248                 
30249                 if(x > 0 && y > 0){
30250                     scale = targetWidth / width;
30251                     
30252                     if(width < height){
30253                         scale = targetHeight / height;
30254                     }
30255                 }
30256                 
30257                 context.scale(scale, scale);
30258                 
30259                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30260                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30261
30262                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30263                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30264
30265                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30266                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30267                 
30268                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30269                 
30270                 break;
30271             case 270 :
30272                 
30273                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30274                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30275                 
30276                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30277                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30278                 
30279                 var targetWidth = this.minWidth - 2 * x;
30280                 var targetHeight = this.minHeight - 2 * y;
30281                 
30282                 var scale = 1;
30283                 
30284                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30285                     scale = targetWidth / width;
30286                 }
30287                 
30288                 if(x > 0 && y == 0){
30289                     scale = targetHeight / height;
30290                 }
30291                 
30292                 if(x > 0 && y > 0){
30293                     scale = targetWidth / width;
30294                     
30295                     if(width < height){
30296                         scale = targetHeight / height;
30297                     }
30298                 }
30299                 
30300                 context.scale(scale, scale);
30301                 
30302                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30303                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30304
30305                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30306                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30307                 
30308                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30309                 
30310                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30311                 
30312                 break;
30313             default : 
30314                 break;
30315         }
30316         
30317         this.cropData = canvas.toDataURL(this.cropType);
30318         
30319         if(this.fireEvent('crop', this, this.cropData) !== false){
30320             this.process(this.file, this.cropData);
30321         }
30322         
30323         return;
30324         
30325     },
30326     
30327     setThumbBoxSize : function()
30328     {
30329         var width, height;
30330         
30331         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30332             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30333             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30334             
30335             this.minWidth = width;
30336             this.minHeight = height;
30337             
30338             if(this.rotate == 90 || this.rotate == 270){
30339                 this.minWidth = height;
30340                 this.minHeight = width;
30341             }
30342         }
30343         
30344         height = 300;
30345         width = Math.ceil(this.minWidth * height / this.minHeight);
30346         
30347         if(this.minWidth > this.minHeight){
30348             width = 300;
30349             height = Math.ceil(this.minHeight * width / this.minWidth);
30350         }
30351         
30352         this.thumbEl.setStyle({
30353             width : width + 'px',
30354             height : height + 'px'
30355         });
30356
30357         return;
30358             
30359     },
30360     
30361     setThumbBoxPosition : function()
30362     {
30363         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30364         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30365         
30366         this.thumbEl.setLeft(x);
30367         this.thumbEl.setTop(y);
30368         
30369     },
30370     
30371     baseRotateLevel : function()
30372     {
30373         this.baseRotate = 1;
30374         
30375         if(
30376                 typeof(this.exif) != 'undefined' &&
30377                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30378                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30379         ){
30380             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30381         }
30382         
30383         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30384         
30385     },
30386     
30387     baseScaleLevel : function()
30388     {
30389         var width, height;
30390         
30391         if(this.isDocument){
30392             
30393             if(this.baseRotate == 6 || this.baseRotate == 8){
30394             
30395                 height = this.thumbEl.getHeight();
30396                 this.baseScale = height / this.imageEl.OriginWidth;
30397
30398                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30399                     width = this.thumbEl.getWidth();
30400                     this.baseScale = width / this.imageEl.OriginHeight;
30401                 }
30402
30403                 return;
30404             }
30405
30406             height = this.thumbEl.getHeight();
30407             this.baseScale = height / this.imageEl.OriginHeight;
30408
30409             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30410                 width = this.thumbEl.getWidth();
30411                 this.baseScale = width / this.imageEl.OriginWidth;
30412             }
30413
30414             return;
30415         }
30416         
30417         if(this.baseRotate == 6 || this.baseRotate == 8){
30418             
30419             width = this.thumbEl.getHeight();
30420             this.baseScale = width / this.imageEl.OriginHeight;
30421             
30422             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30423                 height = this.thumbEl.getWidth();
30424                 this.baseScale = height / this.imageEl.OriginHeight;
30425             }
30426             
30427             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30428                 height = this.thumbEl.getWidth();
30429                 this.baseScale = height / this.imageEl.OriginHeight;
30430                 
30431                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30432                     width = this.thumbEl.getHeight();
30433                     this.baseScale = width / this.imageEl.OriginWidth;
30434                 }
30435             }
30436             
30437             return;
30438         }
30439         
30440         width = this.thumbEl.getWidth();
30441         this.baseScale = width / this.imageEl.OriginWidth;
30442         
30443         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30444             height = this.thumbEl.getHeight();
30445             this.baseScale = height / this.imageEl.OriginHeight;
30446         }
30447         
30448         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30449             
30450             height = this.thumbEl.getHeight();
30451             this.baseScale = height / this.imageEl.OriginHeight;
30452             
30453             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30454                 width = this.thumbEl.getWidth();
30455                 this.baseScale = width / this.imageEl.OriginWidth;
30456             }
30457             
30458         }
30459         
30460         return;
30461     },
30462     
30463     getScaleLevel : function()
30464     {
30465         return this.baseScale * Math.pow(1.1, this.scale);
30466     },
30467     
30468     onTouchStart : function(e)
30469     {
30470         if(!this.canvasLoaded){
30471             this.beforeSelectFile(e);
30472             return;
30473         }
30474         
30475         var touches = e.browserEvent.touches;
30476         
30477         if(!touches){
30478             return;
30479         }
30480         
30481         if(touches.length == 1){
30482             this.onMouseDown(e);
30483             return;
30484         }
30485         
30486         if(touches.length != 2){
30487             return;
30488         }
30489         
30490         var coords = [];
30491         
30492         for(var i = 0, finger; finger = touches[i]; i++){
30493             coords.push(finger.pageX, finger.pageY);
30494         }
30495         
30496         var x = Math.pow(coords[0] - coords[2], 2);
30497         var y = Math.pow(coords[1] - coords[3], 2);
30498         
30499         this.startDistance = Math.sqrt(x + y);
30500         
30501         this.startScale = this.scale;
30502         
30503         this.pinching = true;
30504         this.dragable = false;
30505         
30506     },
30507     
30508     onTouchMove : function(e)
30509     {
30510         if(!this.pinching && !this.dragable){
30511             return;
30512         }
30513         
30514         var touches = e.browserEvent.touches;
30515         
30516         if(!touches){
30517             return;
30518         }
30519         
30520         if(this.dragable){
30521             this.onMouseMove(e);
30522             return;
30523         }
30524         
30525         var coords = [];
30526         
30527         for(var i = 0, finger; finger = touches[i]; i++){
30528             coords.push(finger.pageX, finger.pageY);
30529         }
30530         
30531         var x = Math.pow(coords[0] - coords[2], 2);
30532         var y = Math.pow(coords[1] - coords[3], 2);
30533         
30534         this.endDistance = Math.sqrt(x + y);
30535         
30536         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30537         
30538         if(!this.zoomable()){
30539             this.scale = this.startScale;
30540             return;
30541         }
30542         
30543         this.draw();
30544         
30545     },
30546     
30547     onTouchEnd : function(e)
30548     {
30549         this.pinching = false;
30550         this.dragable = false;
30551         
30552     },
30553     
30554     process : function(file, crop)
30555     {
30556         if(this.loadMask){
30557             this.maskEl.mask(this.loadingText);
30558         }
30559         
30560         this.xhr = new XMLHttpRequest();
30561         
30562         file.xhr = this.xhr;
30563
30564         this.xhr.open(this.method, this.url, true);
30565         
30566         var headers = {
30567             "Accept": "application/json",
30568             "Cache-Control": "no-cache",
30569             "X-Requested-With": "XMLHttpRequest"
30570         };
30571         
30572         for (var headerName in headers) {
30573             var headerValue = headers[headerName];
30574             if (headerValue) {
30575                 this.xhr.setRequestHeader(headerName, headerValue);
30576             }
30577         }
30578         
30579         var _this = this;
30580         
30581         this.xhr.onload = function()
30582         {
30583             _this.xhrOnLoad(_this.xhr);
30584         }
30585         
30586         this.xhr.onerror = function()
30587         {
30588             _this.xhrOnError(_this.xhr);
30589         }
30590         
30591         var formData = new FormData();
30592
30593         formData.append('returnHTML', 'NO');
30594         
30595         if(crop){
30596             formData.append('crop', crop);
30597         }
30598         
30599         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30600             formData.append(this.paramName, file, file.name);
30601         }
30602         
30603         if(typeof(file.filename) != 'undefined'){
30604             formData.append('filename', file.filename);
30605         }
30606         
30607         if(typeof(file.mimetype) != 'undefined'){
30608             formData.append('mimetype', file.mimetype);
30609         }
30610         
30611         if(this.fireEvent('arrange', this, formData) != false){
30612             this.xhr.send(formData);
30613         };
30614     },
30615     
30616     xhrOnLoad : function(xhr)
30617     {
30618         if(this.loadMask){
30619             this.maskEl.unmask();
30620         }
30621         
30622         if (xhr.readyState !== 4) {
30623             this.fireEvent('exception', this, xhr);
30624             return;
30625         }
30626
30627         var response = Roo.decode(xhr.responseText);
30628         
30629         if(!response.success){
30630             this.fireEvent('exception', this, xhr);
30631             return;
30632         }
30633         
30634         var response = Roo.decode(xhr.responseText);
30635         
30636         this.fireEvent('upload', this, response);
30637         
30638     },
30639     
30640     xhrOnError : function()
30641     {
30642         if(this.loadMask){
30643             this.maskEl.unmask();
30644         }
30645         
30646         Roo.log('xhr on error');
30647         
30648         var response = Roo.decode(xhr.responseText);
30649           
30650         Roo.log(response);
30651         
30652     },
30653     
30654     prepare : function(file)
30655     {   
30656         if(this.loadMask){
30657             this.maskEl.mask(this.loadingText);
30658         }
30659         
30660         this.file = false;
30661         this.exif = {};
30662         
30663         if(typeof(file) === 'string'){
30664             this.loadCanvas(file);
30665             return;
30666         }
30667         
30668         if(!file || !this.urlAPI){
30669             return;
30670         }
30671         
30672         this.file = file;
30673         this.cropType = file.type;
30674         
30675         var _this = this;
30676         
30677         if(this.fireEvent('prepare', this, this.file) != false){
30678             
30679             var reader = new FileReader();
30680             
30681             reader.onload = function (e) {
30682                 if (e.target.error) {
30683                     Roo.log(e.target.error);
30684                     return;
30685                 }
30686                 
30687                 var buffer = e.target.result,
30688                     dataView = new DataView(buffer),
30689                     offset = 2,
30690                     maxOffset = dataView.byteLength - 4,
30691                     markerBytes,
30692                     markerLength;
30693                 
30694                 if (dataView.getUint16(0) === 0xffd8) {
30695                     while (offset < maxOffset) {
30696                         markerBytes = dataView.getUint16(offset);
30697                         
30698                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30699                             markerLength = dataView.getUint16(offset + 2) + 2;
30700                             if (offset + markerLength > dataView.byteLength) {
30701                                 Roo.log('Invalid meta data: Invalid segment size.');
30702                                 break;
30703                             }
30704                             
30705                             if(markerBytes == 0xffe1){
30706                                 _this.parseExifData(
30707                                     dataView,
30708                                     offset,
30709                                     markerLength
30710                                 );
30711                             }
30712                             
30713                             offset += markerLength;
30714                             
30715                             continue;
30716                         }
30717                         
30718                         break;
30719                     }
30720                     
30721                 }
30722                 
30723                 var url = _this.urlAPI.createObjectURL(_this.file);
30724                 
30725                 _this.loadCanvas(url);
30726                 
30727                 return;
30728             }
30729             
30730             reader.readAsArrayBuffer(this.file);
30731             
30732         }
30733         
30734     },
30735     
30736     parseExifData : function(dataView, offset, length)
30737     {
30738         var tiffOffset = offset + 10,
30739             littleEndian,
30740             dirOffset;
30741     
30742         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30743             // No Exif data, might be XMP data instead
30744             return;
30745         }
30746         
30747         // Check for the ASCII code for "Exif" (0x45786966):
30748         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30749             // No Exif data, might be XMP data instead
30750             return;
30751         }
30752         if (tiffOffset + 8 > dataView.byteLength) {
30753             Roo.log('Invalid Exif data: Invalid segment size.');
30754             return;
30755         }
30756         // Check for the two null bytes:
30757         if (dataView.getUint16(offset + 8) !== 0x0000) {
30758             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30759             return;
30760         }
30761         // Check the byte alignment:
30762         switch (dataView.getUint16(tiffOffset)) {
30763         case 0x4949:
30764             littleEndian = true;
30765             break;
30766         case 0x4D4D:
30767             littleEndian = false;
30768             break;
30769         default:
30770             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30771             return;
30772         }
30773         // Check for the TIFF tag marker (0x002A):
30774         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30775             Roo.log('Invalid Exif data: Missing TIFF marker.');
30776             return;
30777         }
30778         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30779         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30780         
30781         this.parseExifTags(
30782             dataView,
30783             tiffOffset,
30784             tiffOffset + dirOffset,
30785             littleEndian
30786         );
30787     },
30788     
30789     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30790     {
30791         var tagsNumber,
30792             dirEndOffset,
30793             i;
30794         if (dirOffset + 6 > dataView.byteLength) {
30795             Roo.log('Invalid Exif data: Invalid directory offset.');
30796             return;
30797         }
30798         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30799         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30800         if (dirEndOffset + 4 > dataView.byteLength) {
30801             Roo.log('Invalid Exif data: Invalid directory size.');
30802             return;
30803         }
30804         for (i = 0; i < tagsNumber; i += 1) {
30805             this.parseExifTag(
30806                 dataView,
30807                 tiffOffset,
30808                 dirOffset + 2 + 12 * i, // tag offset
30809                 littleEndian
30810             );
30811         }
30812         // Return the offset to the next directory:
30813         return dataView.getUint32(dirEndOffset, littleEndian);
30814     },
30815     
30816     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30817     {
30818         var tag = dataView.getUint16(offset, littleEndian);
30819         
30820         this.exif[tag] = this.getExifValue(
30821             dataView,
30822             tiffOffset,
30823             offset,
30824             dataView.getUint16(offset + 2, littleEndian), // tag type
30825             dataView.getUint32(offset + 4, littleEndian), // tag length
30826             littleEndian
30827         );
30828     },
30829     
30830     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30831     {
30832         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30833             tagSize,
30834             dataOffset,
30835             values,
30836             i,
30837             str,
30838             c;
30839     
30840         if (!tagType) {
30841             Roo.log('Invalid Exif data: Invalid tag type.');
30842             return;
30843         }
30844         
30845         tagSize = tagType.size * length;
30846         // Determine if the value is contained in the dataOffset bytes,
30847         // or if the value at the dataOffset is a pointer to the actual data:
30848         dataOffset = tagSize > 4 ?
30849                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30850         if (dataOffset + tagSize > dataView.byteLength) {
30851             Roo.log('Invalid Exif data: Invalid data offset.');
30852             return;
30853         }
30854         if (length === 1) {
30855             return tagType.getValue(dataView, dataOffset, littleEndian);
30856         }
30857         values = [];
30858         for (i = 0; i < length; i += 1) {
30859             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30860         }
30861         
30862         if (tagType.ascii) {
30863             str = '';
30864             // Concatenate the chars:
30865             for (i = 0; i < values.length; i += 1) {
30866                 c = values[i];
30867                 // Ignore the terminating NULL byte(s):
30868                 if (c === '\u0000') {
30869                     break;
30870                 }
30871                 str += c;
30872             }
30873             return str;
30874         }
30875         return values;
30876     }
30877     
30878 });
30879
30880 Roo.apply(Roo.bootstrap.UploadCropbox, {
30881     tags : {
30882         'Orientation': 0x0112
30883     },
30884     
30885     Orientation: {
30886             1: 0, //'top-left',
30887 //            2: 'top-right',
30888             3: 180, //'bottom-right',
30889 //            4: 'bottom-left',
30890 //            5: 'left-top',
30891             6: 90, //'right-top',
30892 //            7: 'right-bottom',
30893             8: 270 //'left-bottom'
30894     },
30895     
30896     exifTagTypes : {
30897         // byte, 8-bit unsigned int:
30898         1: {
30899             getValue: function (dataView, dataOffset) {
30900                 return dataView.getUint8(dataOffset);
30901             },
30902             size: 1
30903         },
30904         // ascii, 8-bit byte:
30905         2: {
30906             getValue: function (dataView, dataOffset) {
30907                 return String.fromCharCode(dataView.getUint8(dataOffset));
30908             },
30909             size: 1,
30910             ascii: true
30911         },
30912         // short, 16 bit int:
30913         3: {
30914             getValue: function (dataView, dataOffset, littleEndian) {
30915                 return dataView.getUint16(dataOffset, littleEndian);
30916             },
30917             size: 2
30918         },
30919         // long, 32 bit int:
30920         4: {
30921             getValue: function (dataView, dataOffset, littleEndian) {
30922                 return dataView.getUint32(dataOffset, littleEndian);
30923             },
30924             size: 4
30925         },
30926         // rational = two long values, first is numerator, second is denominator:
30927         5: {
30928             getValue: function (dataView, dataOffset, littleEndian) {
30929                 return dataView.getUint32(dataOffset, littleEndian) /
30930                     dataView.getUint32(dataOffset + 4, littleEndian);
30931             },
30932             size: 8
30933         },
30934         // slong, 32 bit signed int:
30935         9: {
30936             getValue: function (dataView, dataOffset, littleEndian) {
30937                 return dataView.getInt32(dataOffset, littleEndian);
30938             },
30939             size: 4
30940         },
30941         // srational, two slongs, first is numerator, second is denominator:
30942         10: {
30943             getValue: function (dataView, dataOffset, littleEndian) {
30944                 return dataView.getInt32(dataOffset, littleEndian) /
30945                     dataView.getInt32(dataOffset + 4, littleEndian);
30946             },
30947             size: 8
30948         }
30949     },
30950     
30951     footer : {
30952         STANDARD : [
30953             {
30954                 tag : 'div',
30955                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30956                 action : 'rotate-left',
30957                 cn : [
30958                     {
30959                         tag : 'button',
30960                         cls : 'btn btn-default',
30961                         html : '<i class="fa fa-undo"></i>'
30962                     }
30963                 ]
30964             },
30965             {
30966                 tag : 'div',
30967                 cls : 'btn-group roo-upload-cropbox-picture',
30968                 action : 'picture',
30969                 cn : [
30970                     {
30971                         tag : 'button',
30972                         cls : 'btn btn-default',
30973                         html : '<i class="fa fa-picture-o"></i>'
30974                     }
30975                 ]
30976             },
30977             {
30978                 tag : 'div',
30979                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30980                 action : 'rotate-right',
30981                 cn : [
30982                     {
30983                         tag : 'button',
30984                         cls : 'btn btn-default',
30985                         html : '<i class="fa fa-repeat"></i>'
30986                     }
30987                 ]
30988             }
30989         ],
30990         DOCUMENT : [
30991             {
30992                 tag : 'div',
30993                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30994                 action : 'rotate-left',
30995                 cn : [
30996                     {
30997                         tag : 'button',
30998                         cls : 'btn btn-default',
30999                         html : '<i class="fa fa-undo"></i>'
31000                     }
31001                 ]
31002             },
31003             {
31004                 tag : 'div',
31005                 cls : 'btn-group roo-upload-cropbox-download',
31006                 action : 'download',
31007                 cn : [
31008                     {
31009                         tag : 'button',
31010                         cls : 'btn btn-default',
31011                         html : '<i class="fa fa-download"></i>'
31012                     }
31013                 ]
31014             },
31015             {
31016                 tag : 'div',
31017                 cls : 'btn-group roo-upload-cropbox-crop',
31018                 action : 'crop',
31019                 cn : [
31020                     {
31021                         tag : 'button',
31022                         cls : 'btn btn-default',
31023                         html : '<i class="fa fa-crop"></i>'
31024                     }
31025                 ]
31026             },
31027             {
31028                 tag : 'div',
31029                 cls : 'btn-group roo-upload-cropbox-trash',
31030                 action : 'trash',
31031                 cn : [
31032                     {
31033                         tag : 'button',
31034                         cls : 'btn btn-default',
31035                         html : '<i class="fa fa-trash"></i>'
31036                     }
31037                 ]
31038             },
31039             {
31040                 tag : 'div',
31041                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31042                 action : 'rotate-right',
31043                 cn : [
31044                     {
31045                         tag : 'button',
31046                         cls : 'btn btn-default',
31047                         html : '<i class="fa fa-repeat"></i>'
31048                     }
31049                 ]
31050             }
31051         ],
31052         ROTATOR : [
31053             {
31054                 tag : 'div',
31055                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31056                 action : 'rotate-left',
31057                 cn : [
31058                     {
31059                         tag : 'button',
31060                         cls : 'btn btn-default',
31061                         html : '<i class="fa fa-undo"></i>'
31062                     }
31063                 ]
31064             },
31065             {
31066                 tag : 'div',
31067                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31068                 action : 'rotate-right',
31069                 cn : [
31070                     {
31071                         tag : 'button',
31072                         cls : 'btn btn-default',
31073                         html : '<i class="fa fa-repeat"></i>'
31074                     }
31075                 ]
31076             }
31077         ]
31078     }
31079 });
31080
31081 /*
31082 * Licence: LGPL
31083 */
31084
31085 /**
31086  * @class Roo.bootstrap.DocumentManager
31087  * @extends Roo.bootstrap.Component
31088  * Bootstrap DocumentManager class
31089  * @cfg {String} paramName default 'imageUpload'
31090  * @cfg {String} toolTipName default 'filename'
31091  * @cfg {String} method default POST
31092  * @cfg {String} url action url
31093  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31094  * @cfg {Boolean} multiple multiple upload default true
31095  * @cfg {Number} thumbSize default 300
31096  * @cfg {String} fieldLabel
31097  * @cfg {Number} labelWidth default 4
31098  * @cfg {String} labelAlign (left|top) default left
31099  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31100 * @cfg {Number} labellg set the width of label (1-12)
31101  * @cfg {Number} labelmd set the width of label (1-12)
31102  * @cfg {Number} labelsm set the width of label (1-12)
31103  * @cfg {Number} labelxs set the width of label (1-12)
31104  * 
31105  * @constructor
31106  * Create a new DocumentManager
31107  * @param {Object} config The config object
31108  */
31109
31110 Roo.bootstrap.DocumentManager = function(config){
31111     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31112     
31113     this.files = [];
31114     this.delegates = [];
31115     
31116     this.addEvents({
31117         /**
31118          * @event initial
31119          * Fire when initial the DocumentManager
31120          * @param {Roo.bootstrap.DocumentManager} this
31121          */
31122         "initial" : true,
31123         /**
31124          * @event inspect
31125          * inspect selected file
31126          * @param {Roo.bootstrap.DocumentManager} this
31127          * @param {File} file
31128          */
31129         "inspect" : true,
31130         /**
31131          * @event exception
31132          * Fire when xhr load exception
31133          * @param {Roo.bootstrap.DocumentManager} this
31134          * @param {XMLHttpRequest} xhr
31135          */
31136         "exception" : true,
31137         /**
31138          * @event afterupload
31139          * Fire when xhr load exception
31140          * @param {Roo.bootstrap.DocumentManager} this
31141          * @param {XMLHttpRequest} xhr
31142          */
31143         "afterupload" : true,
31144         /**
31145          * @event prepare
31146          * prepare the form data
31147          * @param {Roo.bootstrap.DocumentManager} this
31148          * @param {Object} formData
31149          */
31150         "prepare" : true,
31151         /**
31152          * @event remove
31153          * Fire when remove the file
31154          * @param {Roo.bootstrap.DocumentManager} this
31155          * @param {Object} file
31156          */
31157         "remove" : true,
31158         /**
31159          * @event refresh
31160          * Fire after refresh the file
31161          * @param {Roo.bootstrap.DocumentManager} this
31162          */
31163         "refresh" : true,
31164         /**
31165          * @event click
31166          * Fire after click the image
31167          * @param {Roo.bootstrap.DocumentManager} this
31168          * @param {Object} file
31169          */
31170         "click" : true,
31171         /**
31172          * @event edit
31173          * Fire when upload a image and editable set to true
31174          * @param {Roo.bootstrap.DocumentManager} this
31175          * @param {Object} file
31176          */
31177         "edit" : true,
31178         /**
31179          * @event beforeselectfile
31180          * Fire before select file
31181          * @param {Roo.bootstrap.DocumentManager} this
31182          */
31183         "beforeselectfile" : true,
31184         /**
31185          * @event process
31186          * Fire before process file
31187          * @param {Roo.bootstrap.DocumentManager} this
31188          * @param {Object} file
31189          */
31190         "process" : true,
31191         /**
31192          * @event previewrendered
31193          * Fire when preview rendered
31194          * @param {Roo.bootstrap.DocumentManager} this
31195          * @param {Object} file
31196          */
31197         "previewrendered" : true,
31198         /**
31199          */
31200         "previewResize" : true
31201         
31202     });
31203 };
31204
31205 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31206     
31207     boxes : 0,
31208     inputName : '',
31209     thumbSize : 300,
31210     multiple : true,
31211     files : false,
31212     method : 'POST',
31213     url : '',
31214     paramName : 'imageUpload',
31215     toolTipName : 'filename',
31216     fieldLabel : '',
31217     labelWidth : 4,
31218     labelAlign : 'left',
31219     editable : true,
31220     delegates : false,
31221     xhr : false, 
31222     
31223     labellg : 0,
31224     labelmd : 0,
31225     labelsm : 0,
31226     labelxs : 0,
31227     
31228     getAutoCreate : function()
31229     {   
31230         var managerWidget = {
31231             tag : 'div',
31232             cls : 'roo-document-manager',
31233             cn : [
31234                 {
31235                     tag : 'input',
31236                     cls : 'roo-document-manager-selector',
31237                     type : 'file'
31238                 },
31239                 {
31240                     tag : 'div',
31241                     cls : 'roo-document-manager-uploader',
31242                     cn : [
31243                         {
31244                             tag : 'div',
31245                             cls : 'roo-document-manager-upload-btn',
31246                             html : '<i class="fa fa-plus"></i>'
31247                         }
31248                     ]
31249                     
31250                 }
31251             ]
31252         };
31253         
31254         var content = [
31255             {
31256                 tag : 'div',
31257                 cls : 'column col-md-12',
31258                 cn : managerWidget
31259             }
31260         ];
31261         
31262         if(this.fieldLabel.length){
31263             
31264             content = [
31265                 {
31266                     tag : 'div',
31267                     cls : 'column col-md-12',
31268                     html : this.fieldLabel
31269                 },
31270                 {
31271                     tag : 'div',
31272                     cls : 'column col-md-12',
31273                     cn : managerWidget
31274                 }
31275             ];
31276
31277             if(this.labelAlign == 'left'){
31278                 content = [
31279                     {
31280                         tag : 'div',
31281                         cls : 'column',
31282                         html : this.fieldLabel
31283                     },
31284                     {
31285                         tag : 'div',
31286                         cls : 'column',
31287                         cn : managerWidget
31288                     }
31289                 ];
31290                 
31291                 if(this.labelWidth > 12){
31292                     content[0].style = "width: " + this.labelWidth + 'px';
31293                 }
31294
31295                 if(this.labelWidth < 13 && this.labelmd == 0){
31296                     this.labelmd = this.labelWidth;
31297                 }
31298
31299                 if(this.labellg > 0){
31300                     content[0].cls += ' col-lg-' + this.labellg;
31301                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31302                 }
31303
31304                 if(this.labelmd > 0){
31305                     content[0].cls += ' col-md-' + this.labelmd;
31306                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31307                 }
31308
31309                 if(this.labelsm > 0){
31310                     content[0].cls += ' col-sm-' + this.labelsm;
31311                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31312                 }
31313
31314                 if(this.labelxs > 0){
31315                     content[0].cls += ' col-xs-' + this.labelxs;
31316                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31317                 }
31318                 
31319             }
31320         }
31321         
31322         var cfg = {
31323             tag : 'div',
31324             cls : 'row clearfix',
31325             cn : content
31326         };
31327         
31328         return cfg;
31329         
31330     },
31331     
31332     initEvents : function()
31333     {
31334         this.managerEl = this.el.select('.roo-document-manager', true).first();
31335         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31336         
31337         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31338         this.selectorEl.hide();
31339         
31340         if(this.multiple){
31341             this.selectorEl.attr('multiple', 'multiple');
31342         }
31343         
31344         this.selectorEl.on('change', this.onFileSelected, this);
31345         
31346         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31347         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31348         
31349         this.uploader.on('click', this.onUploaderClick, this);
31350         
31351         this.renderProgressDialog();
31352         
31353         var _this = this;
31354         
31355         window.addEventListener("resize", function() { _this.refresh(); } );
31356         
31357         this.fireEvent('initial', this);
31358     },
31359     
31360     renderProgressDialog : function()
31361     {
31362         var _this = this;
31363         
31364         this.progressDialog = new Roo.bootstrap.Modal({
31365             cls : 'roo-document-manager-progress-dialog',
31366             allow_close : false,
31367             animate : false,
31368             title : '',
31369             buttons : [
31370                 {
31371                     name  :'cancel',
31372                     weight : 'danger',
31373                     html : 'Cancel'
31374                 }
31375             ], 
31376             listeners : { 
31377                 btnclick : function() {
31378                     _this.uploadCancel();
31379                     this.hide();
31380                 }
31381             }
31382         });
31383          
31384         this.progressDialog.render(Roo.get(document.body));
31385          
31386         this.progress = new Roo.bootstrap.Progress({
31387             cls : 'roo-document-manager-progress',
31388             active : true,
31389             striped : true
31390         });
31391         
31392         this.progress.render(this.progressDialog.getChildContainer());
31393         
31394         this.progressBar = new Roo.bootstrap.ProgressBar({
31395             cls : 'roo-document-manager-progress-bar',
31396             aria_valuenow : 0,
31397             aria_valuemin : 0,
31398             aria_valuemax : 12,
31399             panel : 'success'
31400         });
31401         
31402         this.progressBar.render(this.progress.getChildContainer());
31403     },
31404     
31405     onUploaderClick : function(e)
31406     {
31407         e.preventDefault();
31408      
31409         if(this.fireEvent('beforeselectfile', this) != false){
31410             this.selectorEl.dom.click();
31411         }
31412         
31413     },
31414     
31415     onFileSelected : function(e)
31416     {
31417         e.preventDefault();
31418         
31419         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31420             return;
31421         }
31422         
31423         Roo.each(this.selectorEl.dom.files, function(file){
31424             if(this.fireEvent('inspect', this, file) != false){
31425                 this.files.push(file);
31426             }
31427         }, this);
31428         
31429         this.queue();
31430         
31431     },
31432     
31433     queue : function()
31434     {
31435         this.selectorEl.dom.value = '';
31436         
31437         if(!this.files || !this.files.length){
31438             return;
31439         }
31440         
31441         if(this.boxes > 0 && this.files.length > this.boxes){
31442             this.files = this.files.slice(0, this.boxes);
31443         }
31444         
31445         this.uploader.show();
31446         
31447         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31448             this.uploader.hide();
31449         }
31450         
31451         var _this = this;
31452         
31453         var files = [];
31454         
31455         var docs = [];
31456         
31457         Roo.each(this.files, function(file){
31458             
31459             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31460                 var f = this.renderPreview(file);
31461                 files.push(f);
31462                 return;
31463             }
31464             
31465             if(file.type.indexOf('image') != -1){
31466                 this.delegates.push(
31467                     (function(){
31468                         _this.process(file);
31469                     }).createDelegate(this)
31470                 );
31471         
31472                 return;
31473             }
31474             
31475             docs.push(
31476                 (function(){
31477                     _this.process(file);
31478                 }).createDelegate(this)
31479             );
31480             
31481         }, this);
31482         
31483         this.files = files;
31484         
31485         this.delegates = this.delegates.concat(docs);
31486         
31487         if(!this.delegates.length){
31488             this.refresh();
31489             return;
31490         }
31491         
31492         this.progressBar.aria_valuemax = this.delegates.length;
31493         
31494         this.arrange();
31495         
31496         return;
31497     },
31498     
31499     arrange : function()
31500     {
31501         if(!this.delegates.length){
31502             this.progressDialog.hide();
31503             this.refresh();
31504             return;
31505         }
31506         
31507         var delegate = this.delegates.shift();
31508         
31509         this.progressDialog.show();
31510         
31511         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31512         
31513         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31514         
31515         delegate();
31516     },
31517     
31518     refresh : function()
31519     {
31520         this.uploader.show();
31521         
31522         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31523             this.uploader.hide();
31524         }
31525         
31526         Roo.isTouch ? this.closable(false) : this.closable(true);
31527         
31528         this.fireEvent('refresh', this);
31529     },
31530     
31531     onRemove : function(e, el, o)
31532     {
31533         e.preventDefault();
31534         
31535         this.fireEvent('remove', this, o);
31536         
31537     },
31538     
31539     remove : function(o)
31540     {
31541         var files = [];
31542         
31543         Roo.each(this.files, function(file){
31544             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31545                 files.push(file);
31546                 return;
31547             }
31548
31549             o.target.remove();
31550
31551         }, this);
31552         
31553         this.files = files;
31554         
31555         this.refresh();
31556     },
31557     
31558     clear : function()
31559     {
31560         Roo.each(this.files, function(file){
31561             if(!file.target){
31562                 return;
31563             }
31564             
31565             file.target.remove();
31566
31567         }, this);
31568         
31569         this.files = [];
31570         
31571         this.refresh();
31572     },
31573     
31574     onClick : function(e, el, o)
31575     {
31576         e.preventDefault();
31577         
31578         this.fireEvent('click', this, o);
31579         
31580     },
31581     
31582     closable : function(closable)
31583     {
31584         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31585             
31586             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31587             
31588             if(closable){
31589                 el.show();
31590                 return;
31591             }
31592             
31593             el.hide();
31594             
31595         }, this);
31596     },
31597     
31598     xhrOnLoad : function(xhr)
31599     {
31600         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31601             el.remove();
31602         }, this);
31603         
31604         if (xhr.readyState !== 4) {
31605             this.arrange();
31606             this.fireEvent('exception', this, xhr);
31607             return;
31608         }
31609
31610         var response = Roo.decode(xhr.responseText);
31611         
31612         if(!response.success){
31613             this.arrange();
31614             this.fireEvent('exception', this, xhr);
31615             return;
31616         }
31617         
31618         var file = this.renderPreview(response.data);
31619         
31620         this.files.push(file);
31621         
31622         this.arrange();
31623         
31624         this.fireEvent('afterupload', this, xhr);
31625         
31626     },
31627     
31628     xhrOnError : function(xhr)
31629     {
31630         Roo.log('xhr on error');
31631         
31632         var response = Roo.decode(xhr.responseText);
31633           
31634         Roo.log(response);
31635         
31636         this.arrange();
31637     },
31638     
31639     process : function(file)
31640     {
31641         if(this.fireEvent('process', this, file) !== false){
31642             if(this.editable && file.type.indexOf('image') != -1){
31643                 this.fireEvent('edit', this, file);
31644                 return;
31645             }
31646
31647             this.uploadStart(file, false);
31648
31649             return;
31650         }
31651         
31652     },
31653     
31654     uploadStart : function(file, crop)
31655     {
31656         this.xhr = new XMLHttpRequest();
31657         
31658         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31659             this.arrange();
31660             return;
31661         }
31662         
31663         file.xhr = this.xhr;
31664             
31665         this.managerEl.createChild({
31666             tag : 'div',
31667             cls : 'roo-document-manager-loading',
31668             cn : [
31669                 {
31670                     tag : 'div',
31671                     tooltip : file.name,
31672                     cls : 'roo-document-manager-thumb',
31673                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31674                 }
31675             ]
31676
31677         });
31678
31679         this.xhr.open(this.method, this.url, true);
31680         
31681         var headers = {
31682             "Accept": "application/json",
31683             "Cache-Control": "no-cache",
31684             "X-Requested-With": "XMLHttpRequest"
31685         };
31686         
31687         for (var headerName in headers) {
31688             var headerValue = headers[headerName];
31689             if (headerValue) {
31690                 this.xhr.setRequestHeader(headerName, headerValue);
31691             }
31692         }
31693         
31694         var _this = this;
31695         
31696         this.xhr.onload = function()
31697         {
31698             _this.xhrOnLoad(_this.xhr);
31699         }
31700         
31701         this.xhr.onerror = function()
31702         {
31703             _this.xhrOnError(_this.xhr);
31704         }
31705         
31706         var formData = new FormData();
31707
31708         formData.append('returnHTML', 'NO');
31709         
31710         if(crop){
31711             formData.append('crop', crop);
31712         }
31713         
31714         formData.append(this.paramName, file, file.name);
31715         
31716         var options = {
31717             file : file, 
31718             manually : false
31719         };
31720         
31721         if(this.fireEvent('prepare', this, formData, options) != false){
31722             
31723             if(options.manually){
31724                 return;
31725             }
31726             
31727             this.xhr.send(formData);
31728             return;
31729         };
31730         
31731         this.uploadCancel();
31732     },
31733     
31734     uploadCancel : function()
31735     {
31736         if (this.xhr) {
31737             this.xhr.abort();
31738         }
31739         
31740         this.delegates = [];
31741         
31742         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31743             el.remove();
31744         }, this);
31745         
31746         this.arrange();
31747     },
31748     
31749     renderPreview : function(file)
31750     {
31751         if(typeof(file.target) != 'undefined' && file.target){
31752             return file;
31753         }
31754         
31755         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31756         
31757         var previewEl = this.managerEl.createChild({
31758             tag : 'div',
31759             cls : 'roo-document-manager-preview',
31760             cn : [
31761                 {
31762                     tag : 'div',
31763                     tooltip : file[this.toolTipName],
31764                     cls : 'roo-document-manager-thumb',
31765                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31766                 },
31767                 {
31768                     tag : 'button',
31769                     cls : 'close',
31770                     html : '<i class="fa fa-times-circle"></i>'
31771                 }
31772             ]
31773         });
31774
31775         var close = previewEl.select('button.close', true).first();
31776
31777         close.on('click', this.onRemove, this, file);
31778
31779         file.target = previewEl;
31780
31781         var image = previewEl.select('img', true).first();
31782         
31783         var _this = this;
31784         
31785         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31786         
31787         image.on('click', this.onClick, this, file);
31788         
31789         this.fireEvent('previewrendered', this, file);
31790         
31791         return file;
31792         
31793     },
31794     
31795     onPreviewLoad : function(file, image)
31796     {
31797         if(typeof(file.target) == 'undefined' || !file.target){
31798             return;
31799         }
31800         
31801         var width = image.dom.naturalWidth || image.dom.width;
31802         var height = image.dom.naturalHeight || image.dom.height;
31803         
31804         if(!this.previewResize) {
31805             return;
31806         }
31807         
31808         if(width > height){
31809             file.target.addClass('wide');
31810             return;
31811         }
31812         
31813         file.target.addClass('tall');
31814         return;
31815         
31816     },
31817     
31818     uploadFromSource : function(file, crop)
31819     {
31820         this.xhr = new XMLHttpRequest();
31821         
31822         this.managerEl.createChild({
31823             tag : 'div',
31824             cls : 'roo-document-manager-loading',
31825             cn : [
31826                 {
31827                     tag : 'div',
31828                     tooltip : file.name,
31829                     cls : 'roo-document-manager-thumb',
31830                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31831                 }
31832             ]
31833
31834         });
31835
31836         this.xhr.open(this.method, this.url, true);
31837         
31838         var headers = {
31839             "Accept": "application/json",
31840             "Cache-Control": "no-cache",
31841             "X-Requested-With": "XMLHttpRequest"
31842         };
31843         
31844         for (var headerName in headers) {
31845             var headerValue = headers[headerName];
31846             if (headerValue) {
31847                 this.xhr.setRequestHeader(headerName, headerValue);
31848             }
31849         }
31850         
31851         var _this = this;
31852         
31853         this.xhr.onload = function()
31854         {
31855             _this.xhrOnLoad(_this.xhr);
31856         }
31857         
31858         this.xhr.onerror = function()
31859         {
31860             _this.xhrOnError(_this.xhr);
31861         }
31862         
31863         var formData = new FormData();
31864
31865         formData.append('returnHTML', 'NO');
31866         
31867         formData.append('crop', crop);
31868         
31869         if(typeof(file.filename) != 'undefined'){
31870             formData.append('filename', file.filename);
31871         }
31872         
31873         if(typeof(file.mimetype) != 'undefined'){
31874             formData.append('mimetype', file.mimetype);
31875         }
31876         
31877         Roo.log(formData);
31878         
31879         if(this.fireEvent('prepare', this, formData) != false){
31880             this.xhr.send(formData);
31881         };
31882     }
31883 });
31884
31885 /*
31886 * Licence: LGPL
31887 */
31888
31889 /**
31890  * @class Roo.bootstrap.DocumentViewer
31891  * @extends Roo.bootstrap.Component
31892  * Bootstrap DocumentViewer class
31893  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31894  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31895  * 
31896  * @constructor
31897  * Create a new DocumentViewer
31898  * @param {Object} config The config object
31899  */
31900
31901 Roo.bootstrap.DocumentViewer = function(config){
31902     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31903     
31904     this.addEvents({
31905         /**
31906          * @event initial
31907          * Fire after initEvent
31908          * @param {Roo.bootstrap.DocumentViewer} this
31909          */
31910         "initial" : true,
31911         /**
31912          * @event click
31913          * Fire after click
31914          * @param {Roo.bootstrap.DocumentViewer} this
31915          */
31916         "click" : true,
31917         /**
31918          * @event download
31919          * Fire after download button
31920          * @param {Roo.bootstrap.DocumentViewer} this
31921          */
31922         "download" : true,
31923         /**
31924          * @event trash
31925          * Fire after trash button
31926          * @param {Roo.bootstrap.DocumentViewer} this
31927          */
31928         "trash" : true
31929         
31930     });
31931 };
31932
31933 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31934     
31935     showDownload : true,
31936     
31937     showTrash : true,
31938     
31939     getAutoCreate : function()
31940     {
31941         var cfg = {
31942             tag : 'div',
31943             cls : 'roo-document-viewer',
31944             cn : [
31945                 {
31946                     tag : 'div',
31947                     cls : 'roo-document-viewer-body',
31948                     cn : [
31949                         {
31950                             tag : 'div',
31951                             cls : 'roo-document-viewer-thumb',
31952                             cn : [
31953                                 {
31954                                     tag : 'img',
31955                                     cls : 'roo-document-viewer-image'
31956                                 }
31957                             ]
31958                         }
31959                     ]
31960                 },
31961                 {
31962                     tag : 'div',
31963                     cls : 'roo-document-viewer-footer',
31964                     cn : {
31965                         tag : 'div',
31966                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31967                         cn : [
31968                             {
31969                                 tag : 'div',
31970                                 cls : 'btn-group roo-document-viewer-download',
31971                                 cn : [
31972                                     {
31973                                         tag : 'button',
31974                                         cls : 'btn btn-default',
31975                                         html : '<i class="fa fa-download"></i>'
31976                                     }
31977                                 ]
31978                             },
31979                             {
31980                                 tag : 'div',
31981                                 cls : 'btn-group roo-document-viewer-trash',
31982                                 cn : [
31983                                     {
31984                                         tag : 'button',
31985                                         cls : 'btn btn-default',
31986                                         html : '<i class="fa fa-trash"></i>'
31987                                     }
31988                                 ]
31989                             }
31990                         ]
31991                     }
31992                 }
31993             ]
31994         };
31995         
31996         return cfg;
31997     },
31998     
31999     initEvents : function()
32000     {
32001         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32002         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32003         
32004         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32005         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32006         
32007         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32008         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32009         
32010         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32011         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32012         
32013         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32014         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32015         
32016         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32017         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32018         
32019         this.bodyEl.on('click', this.onClick, this);
32020         this.downloadBtn.on('click', this.onDownload, this);
32021         this.trashBtn.on('click', this.onTrash, this);
32022         
32023         this.downloadBtn.hide();
32024         this.trashBtn.hide();
32025         
32026         if(this.showDownload){
32027             this.downloadBtn.show();
32028         }
32029         
32030         if(this.showTrash){
32031             this.trashBtn.show();
32032         }
32033         
32034         if(!this.showDownload && !this.showTrash) {
32035             this.footerEl.hide();
32036         }
32037         
32038     },
32039     
32040     initial : function()
32041     {
32042         this.fireEvent('initial', this);
32043         
32044     },
32045     
32046     onClick : function(e)
32047     {
32048         e.preventDefault();
32049         
32050         this.fireEvent('click', this);
32051     },
32052     
32053     onDownload : function(e)
32054     {
32055         e.preventDefault();
32056         
32057         this.fireEvent('download', this);
32058     },
32059     
32060     onTrash : function(e)
32061     {
32062         e.preventDefault();
32063         
32064         this.fireEvent('trash', this);
32065     }
32066     
32067 });
32068 /*
32069  * - LGPL
32070  *
32071  * nav progress bar
32072  * 
32073  */
32074
32075 /**
32076  * @class Roo.bootstrap.NavProgressBar
32077  * @extends Roo.bootstrap.Component
32078  * Bootstrap NavProgressBar class
32079  * 
32080  * @constructor
32081  * Create a new nav progress bar
32082  * @param {Object} config The config object
32083  */
32084
32085 Roo.bootstrap.NavProgressBar = function(config){
32086     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32087
32088     this.bullets = this.bullets || [];
32089    
32090 //    Roo.bootstrap.NavProgressBar.register(this);
32091      this.addEvents({
32092         /**
32093              * @event changed
32094              * Fires when the active item changes
32095              * @param {Roo.bootstrap.NavProgressBar} this
32096              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32097              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32098          */
32099         'changed': true
32100      });
32101     
32102 };
32103
32104 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32105     
32106     bullets : [],
32107     barItems : [],
32108     
32109     getAutoCreate : function()
32110     {
32111         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32112         
32113         cfg = {
32114             tag : 'div',
32115             cls : 'roo-navigation-bar-group',
32116             cn : [
32117                 {
32118                     tag : 'div',
32119                     cls : 'roo-navigation-top-bar'
32120                 },
32121                 {
32122                     tag : 'div',
32123                     cls : 'roo-navigation-bullets-bar',
32124                     cn : [
32125                         {
32126                             tag : 'ul',
32127                             cls : 'roo-navigation-bar'
32128                         }
32129                     ]
32130                 },
32131                 
32132                 {
32133                     tag : 'div',
32134                     cls : 'roo-navigation-bottom-bar'
32135                 }
32136             ]
32137             
32138         };
32139         
32140         return cfg;
32141         
32142     },
32143     
32144     initEvents: function() 
32145     {
32146         
32147     },
32148     
32149     onRender : function(ct, position) 
32150     {
32151         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32152         
32153         if(this.bullets.length){
32154             Roo.each(this.bullets, function(b){
32155                this.addItem(b);
32156             }, this);
32157         }
32158         
32159         this.format();
32160         
32161     },
32162     
32163     addItem : function(cfg)
32164     {
32165         var item = new Roo.bootstrap.NavProgressItem(cfg);
32166         
32167         item.parentId = this.id;
32168         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32169         
32170         if(cfg.html){
32171             var top = new Roo.bootstrap.Element({
32172                 tag : 'div',
32173                 cls : 'roo-navigation-bar-text'
32174             });
32175             
32176             var bottom = new Roo.bootstrap.Element({
32177                 tag : 'div',
32178                 cls : 'roo-navigation-bar-text'
32179             });
32180             
32181             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32182             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32183             
32184             var topText = new Roo.bootstrap.Element({
32185                 tag : 'span',
32186                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32187             });
32188             
32189             var bottomText = new Roo.bootstrap.Element({
32190                 tag : 'span',
32191                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32192             });
32193             
32194             topText.onRender(top.el, null);
32195             bottomText.onRender(bottom.el, null);
32196             
32197             item.topEl = top;
32198             item.bottomEl = bottom;
32199         }
32200         
32201         this.barItems.push(item);
32202         
32203         return item;
32204     },
32205     
32206     getActive : function()
32207     {
32208         var active = false;
32209         
32210         Roo.each(this.barItems, function(v){
32211             
32212             if (!v.isActive()) {
32213                 return;
32214             }
32215             
32216             active = v;
32217             return false;
32218             
32219         });
32220         
32221         return active;
32222     },
32223     
32224     setActiveItem : function(item)
32225     {
32226         var prev = false;
32227         
32228         Roo.each(this.barItems, function(v){
32229             if (v.rid == item.rid) {
32230                 return ;
32231             }
32232             
32233             if (v.isActive()) {
32234                 v.setActive(false);
32235                 prev = v;
32236             }
32237         });
32238
32239         item.setActive(true);
32240         
32241         this.fireEvent('changed', this, item, prev);
32242     },
32243     
32244     getBarItem: function(rid)
32245     {
32246         var ret = false;
32247         
32248         Roo.each(this.barItems, function(e) {
32249             if (e.rid != rid) {
32250                 return;
32251             }
32252             
32253             ret =  e;
32254             return false;
32255         });
32256         
32257         return ret;
32258     },
32259     
32260     indexOfItem : function(item)
32261     {
32262         var index = false;
32263         
32264         Roo.each(this.barItems, function(v, i){
32265             
32266             if (v.rid != item.rid) {
32267                 return;
32268             }
32269             
32270             index = i;
32271             return false
32272         });
32273         
32274         return index;
32275     },
32276     
32277     setActiveNext : function()
32278     {
32279         var i = this.indexOfItem(this.getActive());
32280         
32281         if (i > this.barItems.length) {
32282             return;
32283         }
32284         
32285         this.setActiveItem(this.barItems[i+1]);
32286     },
32287     
32288     setActivePrev : function()
32289     {
32290         var i = this.indexOfItem(this.getActive());
32291         
32292         if (i  < 1) {
32293             return;
32294         }
32295         
32296         this.setActiveItem(this.barItems[i-1]);
32297     },
32298     
32299     format : function()
32300     {
32301         if(!this.barItems.length){
32302             return;
32303         }
32304      
32305         var width = 100 / this.barItems.length;
32306         
32307         Roo.each(this.barItems, function(i){
32308             i.el.setStyle('width', width + '%');
32309             i.topEl.el.setStyle('width', width + '%');
32310             i.bottomEl.el.setStyle('width', width + '%');
32311         }, this);
32312         
32313     }
32314     
32315 });
32316 /*
32317  * - LGPL
32318  *
32319  * Nav Progress Item
32320  * 
32321  */
32322
32323 /**
32324  * @class Roo.bootstrap.NavProgressItem
32325  * @extends Roo.bootstrap.Component
32326  * Bootstrap NavProgressItem class
32327  * @cfg {String} rid the reference id
32328  * @cfg {Boolean} active (true|false) Is item active default false
32329  * @cfg {Boolean} disabled (true|false) Is item active default false
32330  * @cfg {String} html
32331  * @cfg {String} position (top|bottom) text position default bottom
32332  * @cfg {String} icon show icon instead of number
32333  * 
32334  * @constructor
32335  * Create a new NavProgressItem
32336  * @param {Object} config The config object
32337  */
32338 Roo.bootstrap.NavProgressItem = function(config){
32339     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32340     this.addEvents({
32341         // raw events
32342         /**
32343          * @event click
32344          * The raw click event for the entire grid.
32345          * @param {Roo.bootstrap.NavProgressItem} this
32346          * @param {Roo.EventObject} e
32347          */
32348         "click" : true
32349     });
32350    
32351 };
32352
32353 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32354     
32355     rid : '',
32356     active : false,
32357     disabled : false,
32358     html : '',
32359     position : 'bottom',
32360     icon : false,
32361     
32362     getAutoCreate : function()
32363     {
32364         var iconCls = 'roo-navigation-bar-item-icon';
32365         
32366         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32367         
32368         var cfg = {
32369             tag: 'li',
32370             cls: 'roo-navigation-bar-item',
32371             cn : [
32372                 {
32373                     tag : 'i',
32374                     cls : iconCls
32375                 }
32376             ]
32377         };
32378         
32379         if(this.active){
32380             cfg.cls += ' active';
32381         }
32382         if(this.disabled){
32383             cfg.cls += ' disabled';
32384         }
32385         
32386         return cfg;
32387     },
32388     
32389     disable : function()
32390     {
32391         this.setDisabled(true);
32392     },
32393     
32394     enable : function()
32395     {
32396         this.setDisabled(false);
32397     },
32398     
32399     initEvents: function() 
32400     {
32401         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32402         
32403         this.iconEl.on('click', this.onClick, this);
32404     },
32405     
32406     onClick : function(e)
32407     {
32408         e.preventDefault();
32409         
32410         if(this.disabled){
32411             return;
32412         }
32413         
32414         if(this.fireEvent('click', this, e) === false){
32415             return;
32416         };
32417         
32418         this.parent().setActiveItem(this);
32419     },
32420     
32421     isActive: function () 
32422     {
32423         return this.active;
32424     },
32425     
32426     setActive : function(state)
32427     {
32428         if(this.active == state){
32429             return;
32430         }
32431         
32432         this.active = state;
32433         
32434         if (state) {
32435             this.el.addClass('active');
32436             return;
32437         }
32438         
32439         this.el.removeClass('active');
32440         
32441         return;
32442     },
32443     
32444     setDisabled : function(state)
32445     {
32446         if(this.disabled == state){
32447             return;
32448         }
32449         
32450         this.disabled = state;
32451         
32452         if (state) {
32453             this.el.addClass('disabled');
32454             return;
32455         }
32456         
32457         this.el.removeClass('disabled');
32458     },
32459     
32460     tooltipEl : function()
32461     {
32462         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32463     }
32464 });
32465  
32466
32467  /*
32468  * - LGPL
32469  *
32470  * FieldLabel
32471  * 
32472  */
32473
32474 /**
32475  * @class Roo.bootstrap.FieldLabel
32476  * @extends Roo.bootstrap.Component
32477  * Bootstrap FieldLabel class
32478  * @cfg {String} html contents of the element
32479  * @cfg {String} tag tag of the element default label
32480  * @cfg {String} cls class of the element
32481  * @cfg {String} target label target 
32482  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32483  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32484  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32485  * @cfg {String} iconTooltip default "This field is required"
32486  * @cfg {String} indicatorpos (left|right) default left
32487  * 
32488  * @constructor
32489  * Create a new FieldLabel
32490  * @param {Object} config The config object
32491  */
32492
32493 Roo.bootstrap.FieldLabel = function(config){
32494     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32495     
32496     this.addEvents({
32497             /**
32498              * @event invalid
32499              * Fires after the field has been marked as invalid.
32500              * @param {Roo.form.FieldLabel} this
32501              * @param {String} msg The validation message
32502              */
32503             invalid : true,
32504             /**
32505              * @event valid
32506              * Fires after the field has been validated with no errors.
32507              * @param {Roo.form.FieldLabel} this
32508              */
32509             valid : true
32510         });
32511 };
32512
32513 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32514     
32515     tag: 'label',
32516     cls: '',
32517     html: '',
32518     target: '',
32519     allowBlank : true,
32520     invalidClass : 'has-warning',
32521     validClass : 'has-success',
32522     iconTooltip : 'This field is required',
32523     indicatorpos : 'left',
32524     
32525     getAutoCreate : function(){
32526         
32527         var cls = "";
32528         if (!this.allowBlank) {
32529             cls  = "visible";
32530         }
32531         
32532         var cfg = {
32533             tag : this.tag,
32534             cls : 'roo-bootstrap-field-label ' + this.cls,
32535             for : this.target,
32536             cn : [
32537                 {
32538                     tag : 'i',
32539                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32540                     tooltip : this.iconTooltip
32541                 },
32542                 {
32543                     tag : 'span',
32544                     html : this.html
32545                 }
32546             ] 
32547         };
32548         
32549         if(this.indicatorpos == 'right'){
32550             var cfg = {
32551                 tag : this.tag,
32552                 cls : 'roo-bootstrap-field-label ' + this.cls,
32553                 for : this.target,
32554                 cn : [
32555                     {
32556                         tag : 'span',
32557                         html : this.html
32558                     },
32559                     {
32560                         tag : 'i',
32561                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32562                         tooltip : this.iconTooltip
32563                     }
32564                 ] 
32565             };
32566         }
32567         
32568         return cfg;
32569     },
32570     
32571     initEvents: function() 
32572     {
32573         Roo.bootstrap.Element.superclass.initEvents.call(this);
32574         
32575         this.indicator = this.indicatorEl();
32576         
32577         if(this.indicator){
32578             this.indicator.removeClass('visible');
32579             this.indicator.addClass('invisible');
32580         }
32581         
32582         Roo.bootstrap.FieldLabel.register(this);
32583     },
32584     
32585     indicatorEl : function()
32586     {
32587         var indicator = this.el.select('i.roo-required-indicator',true).first();
32588         
32589         if(!indicator){
32590             return false;
32591         }
32592         
32593         return indicator;
32594         
32595     },
32596     
32597     /**
32598      * Mark this field as valid
32599      */
32600     markValid : function()
32601     {
32602         if(this.indicator){
32603             this.indicator.removeClass('visible');
32604             this.indicator.addClass('invisible');
32605         }
32606         if (Roo.bootstrap.version == 3) {
32607             this.el.removeClass(this.invalidClass);
32608             this.el.addClass(this.validClass);
32609         } else {
32610             this.el.removeClass('is-invalid');
32611             this.el.addClass('is-valid');
32612         }
32613         
32614         
32615         this.fireEvent('valid', this);
32616     },
32617     
32618     /**
32619      * Mark this field as invalid
32620      * @param {String} msg The validation message
32621      */
32622     markInvalid : function(msg)
32623     {
32624         if(this.indicator){
32625             this.indicator.removeClass('invisible');
32626             this.indicator.addClass('visible');
32627         }
32628           if (Roo.bootstrap.version == 3) {
32629             this.el.removeClass(this.validClass);
32630             this.el.addClass(this.invalidClass);
32631         } else {
32632             this.el.removeClass('is-valid');
32633             this.el.addClass('is-invalid');
32634         }
32635         
32636         
32637         this.fireEvent('invalid', this, msg);
32638     }
32639     
32640    
32641 });
32642
32643 Roo.apply(Roo.bootstrap.FieldLabel, {
32644     
32645     groups: {},
32646     
32647      /**
32648     * register a FieldLabel Group
32649     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32650     */
32651     register : function(label)
32652     {
32653         if(this.groups.hasOwnProperty(label.target)){
32654             return;
32655         }
32656      
32657         this.groups[label.target] = label;
32658         
32659     },
32660     /**
32661     * fetch a FieldLabel Group based on the target
32662     * @param {string} target
32663     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32664     */
32665     get: function(target) {
32666         if (typeof(this.groups[target]) == 'undefined') {
32667             return false;
32668         }
32669         
32670         return this.groups[target] ;
32671     }
32672 });
32673
32674  
32675
32676  /*
32677  * - LGPL
32678  *
32679  * page DateSplitField.
32680  * 
32681  */
32682
32683
32684 /**
32685  * @class Roo.bootstrap.DateSplitField
32686  * @extends Roo.bootstrap.Component
32687  * Bootstrap DateSplitField class
32688  * @cfg {string} fieldLabel - the label associated
32689  * @cfg {Number} labelWidth set the width of label (0-12)
32690  * @cfg {String} labelAlign (top|left)
32691  * @cfg {Boolean} dayAllowBlank (true|false) default false
32692  * @cfg {Boolean} monthAllowBlank (true|false) default false
32693  * @cfg {Boolean} yearAllowBlank (true|false) default false
32694  * @cfg {string} dayPlaceholder 
32695  * @cfg {string} monthPlaceholder
32696  * @cfg {string} yearPlaceholder
32697  * @cfg {string} dayFormat default 'd'
32698  * @cfg {string} monthFormat default 'm'
32699  * @cfg {string} yearFormat default 'Y'
32700  * @cfg {Number} labellg set the width of label (1-12)
32701  * @cfg {Number} labelmd set the width of label (1-12)
32702  * @cfg {Number} labelsm set the width of label (1-12)
32703  * @cfg {Number} labelxs set the width of label (1-12)
32704
32705  *     
32706  * @constructor
32707  * Create a new DateSplitField
32708  * @param {Object} config The config object
32709  */
32710
32711 Roo.bootstrap.DateSplitField = function(config){
32712     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32713     
32714     this.addEvents({
32715         // raw events
32716          /**
32717          * @event years
32718          * getting the data of years
32719          * @param {Roo.bootstrap.DateSplitField} this
32720          * @param {Object} years
32721          */
32722         "years" : true,
32723         /**
32724          * @event days
32725          * getting the data of days
32726          * @param {Roo.bootstrap.DateSplitField} this
32727          * @param {Object} days
32728          */
32729         "days" : true,
32730         /**
32731          * @event invalid
32732          * Fires after the field has been marked as invalid.
32733          * @param {Roo.form.Field} this
32734          * @param {String} msg The validation message
32735          */
32736         invalid : true,
32737        /**
32738          * @event valid
32739          * Fires after the field has been validated with no errors.
32740          * @param {Roo.form.Field} this
32741          */
32742         valid : true
32743     });
32744 };
32745
32746 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32747     
32748     fieldLabel : '',
32749     labelAlign : 'top',
32750     labelWidth : 3,
32751     dayAllowBlank : false,
32752     monthAllowBlank : false,
32753     yearAllowBlank : false,
32754     dayPlaceholder : '',
32755     monthPlaceholder : '',
32756     yearPlaceholder : '',
32757     dayFormat : 'd',
32758     monthFormat : 'm',
32759     yearFormat : 'Y',
32760     isFormField : true,
32761     labellg : 0,
32762     labelmd : 0,
32763     labelsm : 0,
32764     labelxs : 0,
32765     
32766     getAutoCreate : function()
32767     {
32768         var cfg = {
32769             tag : 'div',
32770             cls : 'row roo-date-split-field-group',
32771             cn : [
32772                 {
32773                     tag : 'input',
32774                     type : 'hidden',
32775                     cls : 'form-hidden-field roo-date-split-field-group-value',
32776                     name : this.name
32777                 }
32778             ]
32779         };
32780         
32781         var labelCls = 'col-md-12';
32782         var contentCls = 'col-md-4';
32783         
32784         if(this.fieldLabel){
32785             
32786             var label = {
32787                 tag : 'div',
32788                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32789                 cn : [
32790                     {
32791                         tag : 'label',
32792                         html : this.fieldLabel
32793                     }
32794                 ]
32795             };
32796             
32797             if(this.labelAlign == 'left'){
32798             
32799                 if(this.labelWidth > 12){
32800                     label.style = "width: " + this.labelWidth + 'px';
32801                 }
32802
32803                 if(this.labelWidth < 13 && this.labelmd == 0){
32804                     this.labelmd = this.labelWidth;
32805                 }
32806
32807                 if(this.labellg > 0){
32808                     labelCls = ' col-lg-' + this.labellg;
32809                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32810                 }
32811
32812                 if(this.labelmd > 0){
32813                     labelCls = ' col-md-' + this.labelmd;
32814                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32815                 }
32816
32817                 if(this.labelsm > 0){
32818                     labelCls = ' col-sm-' + this.labelsm;
32819                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32820                 }
32821
32822                 if(this.labelxs > 0){
32823                     labelCls = ' col-xs-' + this.labelxs;
32824                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32825                 }
32826             }
32827             
32828             label.cls += ' ' + labelCls;
32829             
32830             cfg.cn.push(label);
32831         }
32832         
32833         Roo.each(['day', 'month', 'year'], function(t){
32834             cfg.cn.push({
32835                 tag : 'div',
32836                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32837             });
32838         }, this);
32839         
32840         return cfg;
32841     },
32842     
32843     inputEl: function ()
32844     {
32845         return this.el.select('.roo-date-split-field-group-value', true).first();
32846     },
32847     
32848     onRender : function(ct, position) 
32849     {
32850         var _this = this;
32851         
32852         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32853         
32854         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32855         
32856         this.dayField = new Roo.bootstrap.ComboBox({
32857             allowBlank : this.dayAllowBlank,
32858             alwaysQuery : true,
32859             displayField : 'value',
32860             editable : false,
32861             fieldLabel : '',
32862             forceSelection : true,
32863             mode : 'local',
32864             placeholder : this.dayPlaceholder,
32865             selectOnFocus : true,
32866             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32867             triggerAction : 'all',
32868             typeAhead : true,
32869             valueField : 'value',
32870             store : new Roo.data.SimpleStore({
32871                 data : (function() {    
32872                     var days = [];
32873                     _this.fireEvent('days', _this, days);
32874                     return days;
32875                 })(),
32876                 fields : [ 'value' ]
32877             }),
32878             listeners : {
32879                 select : function (_self, record, index)
32880                 {
32881                     _this.setValue(_this.getValue());
32882                 }
32883             }
32884         });
32885
32886         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32887         
32888         this.monthField = new Roo.bootstrap.MonthField({
32889             after : '<i class=\"fa fa-calendar\"></i>',
32890             allowBlank : this.monthAllowBlank,
32891             placeholder : this.monthPlaceholder,
32892             readOnly : true,
32893             listeners : {
32894                 render : function (_self)
32895                 {
32896                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32897                         e.preventDefault();
32898                         _self.focus();
32899                     });
32900                 },
32901                 select : function (_self, oldvalue, newvalue)
32902                 {
32903                     _this.setValue(_this.getValue());
32904                 }
32905             }
32906         });
32907         
32908         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32909         
32910         this.yearField = new Roo.bootstrap.ComboBox({
32911             allowBlank : this.yearAllowBlank,
32912             alwaysQuery : true,
32913             displayField : 'value',
32914             editable : false,
32915             fieldLabel : '',
32916             forceSelection : true,
32917             mode : 'local',
32918             placeholder : this.yearPlaceholder,
32919             selectOnFocus : true,
32920             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32921             triggerAction : 'all',
32922             typeAhead : true,
32923             valueField : 'value',
32924             store : new Roo.data.SimpleStore({
32925                 data : (function() {
32926                     var years = [];
32927                     _this.fireEvent('years', _this, years);
32928                     return years;
32929                 })(),
32930                 fields : [ 'value' ]
32931             }),
32932             listeners : {
32933                 select : function (_self, record, index)
32934                 {
32935                     _this.setValue(_this.getValue());
32936                 }
32937             }
32938         });
32939
32940         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32941     },
32942     
32943     setValue : function(v, format)
32944     {
32945         this.inputEl.dom.value = v;
32946         
32947         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32948         
32949         var d = Date.parseDate(v, f);
32950         
32951         if(!d){
32952             this.validate();
32953             return;
32954         }
32955         
32956         this.setDay(d.format(this.dayFormat));
32957         this.setMonth(d.format(this.monthFormat));
32958         this.setYear(d.format(this.yearFormat));
32959         
32960         this.validate();
32961         
32962         return;
32963     },
32964     
32965     setDay : function(v)
32966     {
32967         this.dayField.setValue(v);
32968         this.inputEl.dom.value = this.getValue();
32969         this.validate();
32970         return;
32971     },
32972     
32973     setMonth : function(v)
32974     {
32975         this.monthField.setValue(v, true);
32976         this.inputEl.dom.value = this.getValue();
32977         this.validate();
32978         return;
32979     },
32980     
32981     setYear : function(v)
32982     {
32983         this.yearField.setValue(v);
32984         this.inputEl.dom.value = this.getValue();
32985         this.validate();
32986         return;
32987     },
32988     
32989     getDay : function()
32990     {
32991         return this.dayField.getValue();
32992     },
32993     
32994     getMonth : function()
32995     {
32996         return this.monthField.getValue();
32997     },
32998     
32999     getYear : function()
33000     {
33001         return this.yearField.getValue();
33002     },
33003     
33004     getValue : function()
33005     {
33006         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33007         
33008         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33009         
33010         return date;
33011     },
33012     
33013     reset : function()
33014     {
33015         this.setDay('');
33016         this.setMonth('');
33017         this.setYear('');
33018         this.inputEl.dom.value = '';
33019         this.validate();
33020         return;
33021     },
33022     
33023     validate : function()
33024     {
33025         var d = this.dayField.validate();
33026         var m = this.monthField.validate();
33027         var y = this.yearField.validate();
33028         
33029         var valid = true;
33030         
33031         if(
33032                 (!this.dayAllowBlank && !d) ||
33033                 (!this.monthAllowBlank && !m) ||
33034                 (!this.yearAllowBlank && !y)
33035         ){
33036             valid = false;
33037         }
33038         
33039         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33040             return valid;
33041         }
33042         
33043         if(valid){
33044             this.markValid();
33045             return valid;
33046         }
33047         
33048         this.markInvalid();
33049         
33050         return valid;
33051     },
33052     
33053     markValid : function()
33054     {
33055         
33056         var label = this.el.select('label', true).first();
33057         var icon = this.el.select('i.fa-star', true).first();
33058
33059         if(label && icon){
33060             icon.remove();
33061         }
33062         
33063         this.fireEvent('valid', this);
33064     },
33065     
33066      /**
33067      * Mark this field as invalid
33068      * @param {String} msg The validation message
33069      */
33070     markInvalid : function(msg)
33071     {
33072         
33073         var label = this.el.select('label', true).first();
33074         var icon = this.el.select('i.fa-star', true).first();
33075
33076         if(label && !icon){
33077             this.el.select('.roo-date-split-field-label', true).createChild({
33078                 tag : 'i',
33079                 cls : 'text-danger fa fa-lg fa-star',
33080                 tooltip : 'This field is required',
33081                 style : 'margin-right:5px;'
33082             }, label, true);
33083         }
33084         
33085         this.fireEvent('invalid', this, msg);
33086     },
33087     
33088     clearInvalid : function()
33089     {
33090         var label = this.el.select('label', true).first();
33091         var icon = this.el.select('i.fa-star', true).first();
33092
33093         if(label && icon){
33094             icon.remove();
33095         }
33096         
33097         this.fireEvent('valid', this);
33098     },
33099     
33100     getName: function()
33101     {
33102         return this.name;
33103     }
33104     
33105 });
33106
33107  /**
33108  *
33109  * This is based on 
33110  * http://masonry.desandro.com
33111  *
33112  * The idea is to render all the bricks based on vertical width...
33113  *
33114  * The original code extends 'outlayer' - we might need to use that....
33115  * 
33116  */
33117
33118
33119 /**
33120  * @class Roo.bootstrap.LayoutMasonry
33121  * @extends Roo.bootstrap.Component
33122  * Bootstrap Layout Masonry class
33123  * 
33124  * @constructor
33125  * Create a new Element
33126  * @param {Object} config The config object
33127  */
33128
33129 Roo.bootstrap.LayoutMasonry = function(config){
33130     
33131     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33132     
33133     this.bricks = [];
33134     
33135     Roo.bootstrap.LayoutMasonry.register(this);
33136     
33137     this.addEvents({
33138         // raw events
33139         /**
33140          * @event layout
33141          * Fire after layout the items
33142          * @param {Roo.bootstrap.LayoutMasonry} this
33143          * @param {Roo.EventObject} e
33144          */
33145         "layout" : true
33146     });
33147     
33148 };
33149
33150 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33151     
33152     /**
33153      * @cfg {Boolean} isLayoutInstant = no animation?
33154      */   
33155     isLayoutInstant : false, // needed?
33156    
33157     /**
33158      * @cfg {Number} boxWidth  width of the columns
33159      */   
33160     boxWidth : 450,
33161     
33162       /**
33163      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33164      */   
33165     boxHeight : 0,
33166     
33167     /**
33168      * @cfg {Number} padWidth padding below box..
33169      */   
33170     padWidth : 10, 
33171     
33172     /**
33173      * @cfg {Number} gutter gutter width..
33174      */   
33175     gutter : 10,
33176     
33177      /**
33178      * @cfg {Number} maxCols maximum number of columns
33179      */   
33180     
33181     maxCols: 0,
33182     
33183     /**
33184      * @cfg {Boolean} isAutoInitial defalut true
33185      */   
33186     isAutoInitial : true, 
33187     
33188     containerWidth: 0,
33189     
33190     /**
33191      * @cfg {Boolean} isHorizontal defalut false
33192      */   
33193     isHorizontal : false, 
33194
33195     currentSize : null,
33196     
33197     tag: 'div',
33198     
33199     cls: '',
33200     
33201     bricks: null, //CompositeElement
33202     
33203     cols : 1,
33204     
33205     _isLayoutInited : false,
33206     
33207 //    isAlternative : false, // only use for vertical layout...
33208     
33209     /**
33210      * @cfg {Number} alternativePadWidth padding below box..
33211      */   
33212     alternativePadWidth : 50,
33213     
33214     selectedBrick : [],
33215     
33216     getAutoCreate : function(){
33217         
33218         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33219         
33220         var cfg = {
33221             tag: this.tag,
33222             cls: 'blog-masonary-wrapper ' + this.cls,
33223             cn : {
33224                 cls : 'mas-boxes masonary'
33225             }
33226         };
33227         
33228         return cfg;
33229     },
33230     
33231     getChildContainer: function( )
33232     {
33233         if (this.boxesEl) {
33234             return this.boxesEl;
33235         }
33236         
33237         this.boxesEl = this.el.select('.mas-boxes').first();
33238         
33239         return this.boxesEl;
33240     },
33241     
33242     
33243     initEvents : function()
33244     {
33245         var _this = this;
33246         
33247         if(this.isAutoInitial){
33248             Roo.log('hook children rendered');
33249             this.on('childrenrendered', function() {
33250                 Roo.log('children rendered');
33251                 _this.initial();
33252             } ,this);
33253         }
33254     },
33255     
33256     initial : function()
33257     {
33258         this.selectedBrick = [];
33259         
33260         this.currentSize = this.el.getBox(true);
33261         
33262         Roo.EventManager.onWindowResize(this.resize, this); 
33263
33264         if(!this.isAutoInitial){
33265             this.layout();
33266             return;
33267         }
33268         
33269         this.layout();
33270         
33271         return;
33272         //this.layout.defer(500,this);
33273         
33274     },
33275     
33276     resize : function()
33277     {
33278         var cs = this.el.getBox(true);
33279         
33280         if (
33281                 this.currentSize.width == cs.width && 
33282                 this.currentSize.x == cs.x && 
33283                 this.currentSize.height == cs.height && 
33284                 this.currentSize.y == cs.y 
33285         ) {
33286             Roo.log("no change in with or X or Y");
33287             return;
33288         }
33289         
33290         this.currentSize = cs;
33291         
33292         this.layout();
33293         
33294     },
33295     
33296     layout : function()
33297     {   
33298         this._resetLayout();
33299         
33300         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33301         
33302         this.layoutItems( isInstant );
33303       
33304         this._isLayoutInited = true;
33305         
33306         this.fireEvent('layout', this);
33307         
33308     },
33309     
33310     _resetLayout : function()
33311     {
33312         if(this.isHorizontal){
33313             this.horizontalMeasureColumns();
33314             return;
33315         }
33316         
33317         this.verticalMeasureColumns();
33318         
33319     },
33320     
33321     verticalMeasureColumns : function()
33322     {
33323         this.getContainerWidth();
33324         
33325 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33326 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33327 //            return;
33328 //        }
33329         
33330         var boxWidth = this.boxWidth + this.padWidth;
33331         
33332         if(this.containerWidth < this.boxWidth){
33333             boxWidth = this.containerWidth
33334         }
33335         
33336         var containerWidth = this.containerWidth;
33337         
33338         var cols = Math.floor(containerWidth / boxWidth);
33339         
33340         this.cols = Math.max( cols, 1 );
33341         
33342         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33343         
33344         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33345         
33346         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33347         
33348         this.colWidth = boxWidth + avail - this.padWidth;
33349         
33350         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33351         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33352     },
33353     
33354     horizontalMeasureColumns : function()
33355     {
33356         this.getContainerWidth();
33357         
33358         var boxWidth = this.boxWidth;
33359         
33360         if(this.containerWidth < boxWidth){
33361             boxWidth = this.containerWidth;
33362         }
33363         
33364         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33365         
33366         this.el.setHeight(boxWidth);
33367         
33368     },
33369     
33370     getContainerWidth : function()
33371     {
33372         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33373     },
33374     
33375     layoutItems : function( isInstant )
33376     {
33377         Roo.log(this.bricks);
33378         
33379         var items = Roo.apply([], this.bricks);
33380         
33381         if(this.isHorizontal){
33382             this._horizontalLayoutItems( items , isInstant );
33383             return;
33384         }
33385         
33386 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33387 //            this._verticalAlternativeLayoutItems( items , isInstant );
33388 //            return;
33389 //        }
33390         
33391         this._verticalLayoutItems( items , isInstant );
33392         
33393     },
33394     
33395     _verticalLayoutItems : function ( items , isInstant)
33396     {
33397         if ( !items || !items.length ) {
33398             return;
33399         }
33400         
33401         var standard = [
33402             ['xs', 'xs', 'xs', 'tall'],
33403             ['xs', 'xs', 'tall'],
33404             ['xs', 'xs', 'sm'],
33405             ['xs', 'xs', 'xs'],
33406             ['xs', 'tall'],
33407             ['xs', 'sm'],
33408             ['xs', 'xs'],
33409             ['xs'],
33410             
33411             ['sm', 'xs', 'xs'],
33412             ['sm', 'xs'],
33413             ['sm'],
33414             
33415             ['tall', 'xs', 'xs', 'xs'],
33416             ['tall', 'xs', 'xs'],
33417             ['tall', 'xs'],
33418             ['tall']
33419             
33420         ];
33421         
33422         var queue = [];
33423         
33424         var boxes = [];
33425         
33426         var box = [];
33427         
33428         Roo.each(items, function(item, k){
33429             
33430             switch (item.size) {
33431                 // these layouts take up a full box,
33432                 case 'md' :
33433                 case 'md-left' :
33434                 case 'md-right' :
33435                 case 'wide' :
33436                     
33437                     if(box.length){
33438                         boxes.push(box);
33439                         box = [];
33440                     }
33441                     
33442                     boxes.push([item]);
33443                     
33444                     break;
33445                     
33446                 case 'xs' :
33447                 case 'sm' :
33448                 case 'tall' :
33449                     
33450                     box.push(item);
33451                     
33452                     break;
33453                 default :
33454                     break;
33455                     
33456             }
33457             
33458         }, this);
33459         
33460         if(box.length){
33461             boxes.push(box);
33462             box = [];
33463         }
33464         
33465         var filterPattern = function(box, length)
33466         {
33467             if(!box.length){
33468                 return;
33469             }
33470             
33471             var match = false;
33472             
33473             var pattern = box.slice(0, length);
33474             
33475             var format = [];
33476             
33477             Roo.each(pattern, function(i){
33478                 format.push(i.size);
33479             }, this);
33480             
33481             Roo.each(standard, function(s){
33482                 
33483                 if(String(s) != String(format)){
33484                     return;
33485                 }
33486                 
33487                 match = true;
33488                 return false;
33489                 
33490             }, this);
33491             
33492             if(!match && length == 1){
33493                 return;
33494             }
33495             
33496             if(!match){
33497                 filterPattern(box, length - 1);
33498                 return;
33499             }
33500                 
33501             queue.push(pattern);
33502
33503             box = box.slice(length, box.length);
33504
33505             filterPattern(box, 4);
33506
33507             return;
33508             
33509         }
33510         
33511         Roo.each(boxes, function(box, k){
33512             
33513             if(!box.length){
33514                 return;
33515             }
33516             
33517             if(box.length == 1){
33518                 queue.push(box);
33519                 return;
33520             }
33521             
33522             filterPattern(box, 4);
33523             
33524         }, this);
33525         
33526         this._processVerticalLayoutQueue( queue, isInstant );
33527         
33528     },
33529     
33530 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33531 //    {
33532 //        if ( !items || !items.length ) {
33533 //            return;
33534 //        }
33535 //
33536 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33537 //        
33538 //    },
33539     
33540     _horizontalLayoutItems : function ( items , isInstant)
33541     {
33542         if ( !items || !items.length || items.length < 3) {
33543             return;
33544         }
33545         
33546         items.reverse();
33547         
33548         var eItems = items.slice(0, 3);
33549         
33550         items = items.slice(3, items.length);
33551         
33552         var standard = [
33553             ['xs', 'xs', 'xs', 'wide'],
33554             ['xs', 'xs', 'wide'],
33555             ['xs', 'xs', 'sm'],
33556             ['xs', 'xs', 'xs'],
33557             ['xs', 'wide'],
33558             ['xs', 'sm'],
33559             ['xs', 'xs'],
33560             ['xs'],
33561             
33562             ['sm', 'xs', 'xs'],
33563             ['sm', 'xs'],
33564             ['sm'],
33565             
33566             ['wide', 'xs', 'xs', 'xs'],
33567             ['wide', 'xs', 'xs'],
33568             ['wide', 'xs'],
33569             ['wide'],
33570             
33571             ['wide-thin']
33572         ];
33573         
33574         var queue = [];
33575         
33576         var boxes = [];
33577         
33578         var box = [];
33579         
33580         Roo.each(items, function(item, k){
33581             
33582             switch (item.size) {
33583                 case 'md' :
33584                 case 'md-left' :
33585                 case 'md-right' :
33586                 case 'tall' :
33587                     
33588                     if(box.length){
33589                         boxes.push(box);
33590                         box = [];
33591                     }
33592                     
33593                     boxes.push([item]);
33594                     
33595                     break;
33596                     
33597                 case 'xs' :
33598                 case 'sm' :
33599                 case 'wide' :
33600                 case 'wide-thin' :
33601                     
33602                     box.push(item);
33603                     
33604                     break;
33605                 default :
33606                     break;
33607                     
33608             }
33609             
33610         }, this);
33611         
33612         if(box.length){
33613             boxes.push(box);
33614             box = [];
33615         }
33616         
33617         var filterPattern = function(box, length)
33618         {
33619             if(!box.length){
33620                 return;
33621             }
33622             
33623             var match = false;
33624             
33625             var pattern = box.slice(0, length);
33626             
33627             var format = [];
33628             
33629             Roo.each(pattern, function(i){
33630                 format.push(i.size);
33631             }, this);
33632             
33633             Roo.each(standard, function(s){
33634                 
33635                 if(String(s) != String(format)){
33636                     return;
33637                 }
33638                 
33639                 match = true;
33640                 return false;
33641                 
33642             }, this);
33643             
33644             if(!match && length == 1){
33645                 return;
33646             }
33647             
33648             if(!match){
33649                 filterPattern(box, length - 1);
33650                 return;
33651             }
33652                 
33653             queue.push(pattern);
33654
33655             box = box.slice(length, box.length);
33656
33657             filterPattern(box, 4);
33658
33659             return;
33660             
33661         }
33662         
33663         Roo.each(boxes, function(box, k){
33664             
33665             if(!box.length){
33666                 return;
33667             }
33668             
33669             if(box.length == 1){
33670                 queue.push(box);
33671                 return;
33672             }
33673             
33674             filterPattern(box, 4);
33675             
33676         }, this);
33677         
33678         
33679         var prune = [];
33680         
33681         var pos = this.el.getBox(true);
33682         
33683         var minX = pos.x;
33684         
33685         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33686         
33687         var hit_end = false;
33688         
33689         Roo.each(queue, function(box){
33690             
33691             if(hit_end){
33692                 
33693                 Roo.each(box, function(b){
33694                 
33695                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33696                     b.el.hide();
33697
33698                 }, this);
33699
33700                 return;
33701             }
33702             
33703             var mx = 0;
33704             
33705             Roo.each(box, function(b){
33706                 
33707                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33708                 b.el.show();
33709
33710                 mx = Math.max(mx, b.x);
33711                 
33712             }, this);
33713             
33714             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33715             
33716             if(maxX < minX){
33717                 
33718                 Roo.each(box, function(b){
33719                 
33720                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33721                     b.el.hide();
33722                     
33723                 }, this);
33724                 
33725                 hit_end = true;
33726                 
33727                 return;
33728             }
33729             
33730             prune.push(box);
33731             
33732         }, this);
33733         
33734         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33735     },
33736     
33737     /** Sets position of item in DOM
33738     * @param {Element} item
33739     * @param {Number} x - horizontal position
33740     * @param {Number} y - vertical position
33741     * @param {Boolean} isInstant - disables transitions
33742     */
33743     _processVerticalLayoutQueue : function( queue, isInstant )
33744     {
33745         var pos = this.el.getBox(true);
33746         var x = pos.x;
33747         var y = pos.y;
33748         var maxY = [];
33749         
33750         for (var i = 0; i < this.cols; i++){
33751             maxY[i] = pos.y;
33752         }
33753         
33754         Roo.each(queue, function(box, k){
33755             
33756             var col = k % this.cols;
33757             
33758             Roo.each(box, function(b,kk){
33759                 
33760                 b.el.position('absolute');
33761                 
33762                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33763                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33764                 
33765                 if(b.size == 'md-left' || b.size == 'md-right'){
33766                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33767                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33768                 }
33769                 
33770                 b.el.setWidth(width);
33771                 b.el.setHeight(height);
33772                 // iframe?
33773                 b.el.select('iframe',true).setSize(width,height);
33774                 
33775             }, this);
33776             
33777             for (var i = 0; i < this.cols; i++){
33778                 
33779                 if(maxY[i] < maxY[col]){
33780                     col = i;
33781                     continue;
33782                 }
33783                 
33784                 col = Math.min(col, i);
33785                 
33786             }
33787             
33788             x = pos.x + col * (this.colWidth + this.padWidth);
33789             
33790             y = maxY[col];
33791             
33792             var positions = [];
33793             
33794             switch (box.length){
33795                 case 1 :
33796                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33797                     break;
33798                 case 2 :
33799                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33800                     break;
33801                 case 3 :
33802                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33803                     break;
33804                 case 4 :
33805                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33806                     break;
33807                 default :
33808                     break;
33809             }
33810             
33811             Roo.each(box, function(b,kk){
33812                 
33813                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33814                 
33815                 var sz = b.el.getSize();
33816                 
33817                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33818                 
33819             }, this);
33820             
33821         }, this);
33822         
33823         var mY = 0;
33824         
33825         for (var i = 0; i < this.cols; i++){
33826             mY = Math.max(mY, maxY[i]);
33827         }
33828         
33829         this.el.setHeight(mY - pos.y);
33830         
33831     },
33832     
33833 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33834 //    {
33835 //        var pos = this.el.getBox(true);
33836 //        var x = pos.x;
33837 //        var y = pos.y;
33838 //        var maxX = pos.right;
33839 //        
33840 //        var maxHeight = 0;
33841 //        
33842 //        Roo.each(items, function(item, k){
33843 //            
33844 //            var c = k % 2;
33845 //            
33846 //            item.el.position('absolute');
33847 //                
33848 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33849 //
33850 //            item.el.setWidth(width);
33851 //
33852 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33853 //
33854 //            item.el.setHeight(height);
33855 //            
33856 //            if(c == 0){
33857 //                item.el.setXY([x, y], isInstant ? false : true);
33858 //            } else {
33859 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33860 //            }
33861 //            
33862 //            y = y + height + this.alternativePadWidth;
33863 //            
33864 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33865 //            
33866 //        }, this);
33867 //        
33868 //        this.el.setHeight(maxHeight);
33869 //        
33870 //    },
33871     
33872     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33873     {
33874         var pos = this.el.getBox(true);
33875         
33876         var minX = pos.x;
33877         var minY = pos.y;
33878         
33879         var maxX = pos.right;
33880         
33881         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33882         
33883         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33884         
33885         Roo.each(queue, function(box, k){
33886             
33887             Roo.each(box, function(b, kk){
33888                 
33889                 b.el.position('absolute');
33890                 
33891                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33892                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33893                 
33894                 if(b.size == 'md-left' || b.size == 'md-right'){
33895                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33896                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33897                 }
33898                 
33899                 b.el.setWidth(width);
33900                 b.el.setHeight(height);
33901                 
33902             }, this);
33903             
33904             if(!box.length){
33905                 return;
33906             }
33907             
33908             var positions = [];
33909             
33910             switch (box.length){
33911                 case 1 :
33912                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33913                     break;
33914                 case 2 :
33915                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33916                     break;
33917                 case 3 :
33918                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33919                     break;
33920                 case 4 :
33921                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33922                     break;
33923                 default :
33924                     break;
33925             }
33926             
33927             Roo.each(box, function(b,kk){
33928                 
33929                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33930                 
33931                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33932                 
33933             }, this);
33934             
33935         }, this);
33936         
33937     },
33938     
33939     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33940     {
33941         Roo.each(eItems, function(b,k){
33942             
33943             b.size = (k == 0) ? 'sm' : 'xs';
33944             b.x = (k == 0) ? 2 : 1;
33945             b.y = (k == 0) ? 2 : 1;
33946             
33947             b.el.position('absolute');
33948             
33949             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33950                 
33951             b.el.setWidth(width);
33952             
33953             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33954             
33955             b.el.setHeight(height);
33956             
33957         }, this);
33958
33959         var positions = [];
33960         
33961         positions.push({
33962             x : maxX - this.unitWidth * 2 - this.gutter,
33963             y : minY
33964         });
33965         
33966         positions.push({
33967             x : maxX - this.unitWidth,
33968             y : minY + (this.unitWidth + this.gutter) * 2
33969         });
33970         
33971         positions.push({
33972             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33973             y : minY
33974         });
33975         
33976         Roo.each(eItems, function(b,k){
33977             
33978             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33979
33980         }, this);
33981         
33982     },
33983     
33984     getVerticalOneBoxColPositions : function(x, y, box)
33985     {
33986         var pos = [];
33987         
33988         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33989         
33990         if(box[0].size == 'md-left'){
33991             rand = 0;
33992         }
33993         
33994         if(box[0].size == 'md-right'){
33995             rand = 1;
33996         }
33997         
33998         pos.push({
33999             x : x + (this.unitWidth + this.gutter) * rand,
34000             y : y
34001         });
34002         
34003         return pos;
34004     },
34005     
34006     getVerticalTwoBoxColPositions : function(x, y, box)
34007     {
34008         var pos = [];
34009         
34010         if(box[0].size == 'xs'){
34011             
34012             pos.push({
34013                 x : x,
34014                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34015             });
34016
34017             pos.push({
34018                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34019                 y : y
34020             });
34021             
34022             return pos;
34023             
34024         }
34025         
34026         pos.push({
34027             x : x,
34028             y : y
34029         });
34030
34031         pos.push({
34032             x : x + (this.unitWidth + this.gutter) * 2,
34033             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34034         });
34035         
34036         return pos;
34037         
34038     },
34039     
34040     getVerticalThreeBoxColPositions : function(x, y, box)
34041     {
34042         var pos = [];
34043         
34044         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34045             
34046             pos.push({
34047                 x : x,
34048                 y : y
34049             });
34050
34051             pos.push({
34052                 x : x + (this.unitWidth + this.gutter) * 1,
34053                 y : y
34054             });
34055             
34056             pos.push({
34057                 x : x + (this.unitWidth + this.gutter) * 2,
34058                 y : y
34059             });
34060             
34061             return pos;
34062             
34063         }
34064         
34065         if(box[0].size == 'xs' && box[1].size == 'xs'){
34066             
34067             pos.push({
34068                 x : x,
34069                 y : y
34070             });
34071
34072             pos.push({
34073                 x : x,
34074                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34075             });
34076             
34077             pos.push({
34078                 x : x + (this.unitWidth + this.gutter) * 1,
34079                 y : y
34080             });
34081             
34082             return pos;
34083             
34084         }
34085         
34086         pos.push({
34087             x : x,
34088             y : y
34089         });
34090
34091         pos.push({
34092             x : x + (this.unitWidth + this.gutter) * 2,
34093             y : y
34094         });
34095
34096         pos.push({
34097             x : x + (this.unitWidth + this.gutter) * 2,
34098             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34099         });
34100             
34101         return pos;
34102         
34103     },
34104     
34105     getVerticalFourBoxColPositions : function(x, y, box)
34106     {
34107         var pos = [];
34108         
34109         if(box[0].size == 'xs'){
34110             
34111             pos.push({
34112                 x : x,
34113                 y : y
34114             });
34115
34116             pos.push({
34117                 x : x,
34118                 y : y + (this.unitHeight + this.gutter) * 1
34119             });
34120             
34121             pos.push({
34122                 x : x,
34123                 y : y + (this.unitHeight + this.gutter) * 2
34124             });
34125             
34126             pos.push({
34127                 x : x + (this.unitWidth + this.gutter) * 1,
34128                 y : y
34129             });
34130             
34131             return pos;
34132             
34133         }
34134         
34135         pos.push({
34136             x : x,
34137             y : y
34138         });
34139
34140         pos.push({
34141             x : x + (this.unitWidth + this.gutter) * 2,
34142             y : y
34143         });
34144
34145         pos.push({
34146             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34147             y : y + (this.unitHeight + this.gutter) * 1
34148         });
34149
34150         pos.push({
34151             x : x + (this.unitWidth + this.gutter) * 2,
34152             y : y + (this.unitWidth + this.gutter) * 2
34153         });
34154
34155         return pos;
34156         
34157     },
34158     
34159     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34160     {
34161         var pos = [];
34162         
34163         if(box[0].size == 'md-left'){
34164             pos.push({
34165                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34166                 y : minY
34167             });
34168             
34169             return pos;
34170         }
34171         
34172         if(box[0].size == 'md-right'){
34173             pos.push({
34174                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34175                 y : minY + (this.unitWidth + this.gutter) * 1
34176             });
34177             
34178             return pos;
34179         }
34180         
34181         var rand = Math.floor(Math.random() * (4 - box[0].y));
34182         
34183         pos.push({
34184             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34185             y : minY + (this.unitWidth + this.gutter) * rand
34186         });
34187         
34188         return pos;
34189         
34190     },
34191     
34192     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34193     {
34194         var pos = [];
34195         
34196         if(box[0].size == 'xs'){
34197             
34198             pos.push({
34199                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34200                 y : minY
34201             });
34202
34203             pos.push({
34204                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34205                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34206             });
34207             
34208             return pos;
34209             
34210         }
34211         
34212         pos.push({
34213             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34214             y : minY
34215         });
34216
34217         pos.push({
34218             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34219             y : minY + (this.unitWidth + this.gutter) * 2
34220         });
34221         
34222         return pos;
34223         
34224     },
34225     
34226     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34227     {
34228         var pos = [];
34229         
34230         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34231             
34232             pos.push({
34233                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34234                 y : minY
34235             });
34236
34237             pos.push({
34238                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34239                 y : minY + (this.unitWidth + this.gutter) * 1
34240             });
34241             
34242             pos.push({
34243                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34244                 y : minY + (this.unitWidth + this.gutter) * 2
34245             });
34246             
34247             return pos;
34248             
34249         }
34250         
34251         if(box[0].size == 'xs' && box[1].size == 'xs'){
34252             
34253             pos.push({
34254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34255                 y : minY
34256             });
34257
34258             pos.push({
34259                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34260                 y : minY
34261             });
34262             
34263             pos.push({
34264                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34265                 y : minY + (this.unitWidth + this.gutter) * 1
34266             });
34267             
34268             return pos;
34269             
34270         }
34271         
34272         pos.push({
34273             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34274             y : minY
34275         });
34276
34277         pos.push({
34278             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34279             y : minY + (this.unitWidth + this.gutter) * 2
34280         });
34281
34282         pos.push({
34283             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34284             y : minY + (this.unitWidth + this.gutter) * 2
34285         });
34286             
34287         return pos;
34288         
34289     },
34290     
34291     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34292     {
34293         var pos = [];
34294         
34295         if(box[0].size == 'xs'){
34296             
34297             pos.push({
34298                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34299                 y : minY
34300             });
34301
34302             pos.push({
34303                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34304                 y : minY
34305             });
34306             
34307             pos.push({
34308                 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),
34309                 y : minY
34310             });
34311             
34312             pos.push({
34313                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34314                 y : minY + (this.unitWidth + this.gutter) * 1
34315             });
34316             
34317             return pos;
34318             
34319         }
34320         
34321         pos.push({
34322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34323             y : minY
34324         });
34325         
34326         pos.push({
34327             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34328             y : minY + (this.unitWidth + this.gutter) * 2
34329         });
34330         
34331         pos.push({
34332             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34333             y : minY + (this.unitWidth + this.gutter) * 2
34334         });
34335         
34336         pos.push({
34337             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),
34338             y : minY + (this.unitWidth + this.gutter) * 2
34339         });
34340
34341         return pos;
34342         
34343     },
34344     
34345     /**
34346     * remove a Masonry Brick
34347     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34348     */
34349     removeBrick : function(brick_id)
34350     {
34351         if (!brick_id) {
34352             return;
34353         }
34354         
34355         for (var i = 0; i<this.bricks.length; i++) {
34356             if (this.bricks[i].id == brick_id) {
34357                 this.bricks.splice(i,1);
34358                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34359                 this.initial();
34360             }
34361         }
34362     },
34363     
34364     /**
34365     * adds a Masonry Brick
34366     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34367     */
34368     addBrick : function(cfg)
34369     {
34370         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34371         //this.register(cn);
34372         cn.parentId = this.id;
34373         cn.render(this.el);
34374         return cn;
34375     },
34376     
34377     /**
34378     * register a Masonry Brick
34379     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34380     */
34381     
34382     register : function(brick)
34383     {
34384         this.bricks.push(brick);
34385         brick.masonryId = this.id;
34386     },
34387     
34388     /**
34389     * clear all the Masonry Brick
34390     */
34391     clearAll : function()
34392     {
34393         this.bricks = [];
34394         //this.getChildContainer().dom.innerHTML = "";
34395         this.el.dom.innerHTML = '';
34396     },
34397     
34398     getSelected : function()
34399     {
34400         if (!this.selectedBrick) {
34401             return false;
34402         }
34403         
34404         return this.selectedBrick;
34405     }
34406 });
34407
34408 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34409     
34410     groups: {},
34411      /**
34412     * register a Masonry Layout
34413     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34414     */
34415     
34416     register : function(layout)
34417     {
34418         this.groups[layout.id] = layout;
34419     },
34420     /**
34421     * fetch a  Masonry Layout based on the masonry layout ID
34422     * @param {string} the masonry layout to add
34423     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34424     */
34425     
34426     get: function(layout_id) {
34427         if (typeof(this.groups[layout_id]) == 'undefined') {
34428             return false;
34429         }
34430         return this.groups[layout_id] ;
34431     }
34432     
34433     
34434     
34435 });
34436
34437  
34438
34439  /**
34440  *
34441  * This is based on 
34442  * http://masonry.desandro.com
34443  *
34444  * The idea is to render all the bricks based on vertical width...
34445  *
34446  * The original code extends 'outlayer' - we might need to use that....
34447  * 
34448  */
34449
34450
34451 /**
34452  * @class Roo.bootstrap.LayoutMasonryAuto
34453  * @extends Roo.bootstrap.Component
34454  * Bootstrap Layout Masonry class
34455  * 
34456  * @constructor
34457  * Create a new Element
34458  * @param {Object} config The config object
34459  */
34460
34461 Roo.bootstrap.LayoutMasonryAuto = function(config){
34462     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34463 };
34464
34465 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34466     
34467       /**
34468      * @cfg {Boolean} isFitWidth  - resize the width..
34469      */   
34470     isFitWidth : false,  // options..
34471     /**
34472      * @cfg {Boolean} isOriginLeft = left align?
34473      */   
34474     isOriginLeft : true,
34475     /**
34476      * @cfg {Boolean} isOriginTop = top align?
34477      */   
34478     isOriginTop : false,
34479     /**
34480      * @cfg {Boolean} isLayoutInstant = no animation?
34481      */   
34482     isLayoutInstant : false, // needed?
34483     /**
34484      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34485      */   
34486     isResizingContainer : true,
34487     /**
34488      * @cfg {Number} columnWidth  width of the columns 
34489      */   
34490     
34491     columnWidth : 0,
34492     
34493     /**
34494      * @cfg {Number} maxCols maximum number of columns
34495      */   
34496     
34497     maxCols: 0,
34498     /**
34499      * @cfg {Number} padHeight padding below box..
34500      */   
34501     
34502     padHeight : 10, 
34503     
34504     /**
34505      * @cfg {Boolean} isAutoInitial defalut true
34506      */   
34507     
34508     isAutoInitial : true, 
34509     
34510     // private?
34511     gutter : 0,
34512     
34513     containerWidth: 0,
34514     initialColumnWidth : 0,
34515     currentSize : null,
34516     
34517     colYs : null, // array.
34518     maxY : 0,
34519     padWidth: 10,
34520     
34521     
34522     tag: 'div',
34523     cls: '',
34524     bricks: null, //CompositeElement
34525     cols : 0, // array?
34526     // element : null, // wrapped now this.el
34527     _isLayoutInited : null, 
34528     
34529     
34530     getAutoCreate : function(){
34531         
34532         var cfg = {
34533             tag: this.tag,
34534             cls: 'blog-masonary-wrapper ' + this.cls,
34535             cn : {
34536                 cls : 'mas-boxes masonary'
34537             }
34538         };
34539         
34540         return cfg;
34541     },
34542     
34543     getChildContainer: function( )
34544     {
34545         if (this.boxesEl) {
34546             return this.boxesEl;
34547         }
34548         
34549         this.boxesEl = this.el.select('.mas-boxes').first();
34550         
34551         return this.boxesEl;
34552     },
34553     
34554     
34555     initEvents : function()
34556     {
34557         var _this = this;
34558         
34559         if(this.isAutoInitial){
34560             Roo.log('hook children rendered');
34561             this.on('childrenrendered', function() {
34562                 Roo.log('children rendered');
34563                 _this.initial();
34564             } ,this);
34565         }
34566         
34567     },
34568     
34569     initial : function()
34570     {
34571         this.reloadItems();
34572
34573         this.currentSize = this.el.getBox(true);
34574
34575         /// was window resize... - let's see if this works..
34576         Roo.EventManager.onWindowResize(this.resize, this); 
34577
34578         if(!this.isAutoInitial){
34579             this.layout();
34580             return;
34581         }
34582         
34583         this.layout.defer(500,this);
34584     },
34585     
34586     reloadItems: function()
34587     {
34588         this.bricks = this.el.select('.masonry-brick', true);
34589         
34590         this.bricks.each(function(b) {
34591             //Roo.log(b.getSize());
34592             if (!b.attr('originalwidth')) {
34593                 b.attr('originalwidth',  b.getSize().width);
34594             }
34595             
34596         });
34597         
34598         Roo.log(this.bricks.elements.length);
34599     },
34600     
34601     resize : function()
34602     {
34603         Roo.log('resize');
34604         var cs = this.el.getBox(true);
34605         
34606         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34607             Roo.log("no change in with or X");
34608             return;
34609         }
34610         this.currentSize = cs;
34611         this.layout();
34612     },
34613     
34614     layout : function()
34615     {
34616          Roo.log('layout');
34617         this._resetLayout();
34618         //this._manageStamps();
34619       
34620         // don't animate first layout
34621         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34622         this.layoutItems( isInstant );
34623       
34624         // flag for initalized
34625         this._isLayoutInited = true;
34626     },
34627     
34628     layoutItems : function( isInstant )
34629     {
34630         //var items = this._getItemsForLayout( this.items );
34631         // original code supports filtering layout items.. we just ignore it..
34632         
34633         this._layoutItems( this.bricks , isInstant );
34634       
34635         this._postLayout();
34636     },
34637     _layoutItems : function ( items , isInstant)
34638     {
34639        //this.fireEvent( 'layout', this, items );
34640     
34641
34642         if ( !items || !items.elements.length ) {
34643           // no items, emit event with empty array
34644             return;
34645         }
34646
34647         var queue = [];
34648         items.each(function(item) {
34649             Roo.log("layout item");
34650             Roo.log(item);
34651             // get x/y object from method
34652             var position = this._getItemLayoutPosition( item );
34653             // enqueue
34654             position.item = item;
34655             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34656             queue.push( position );
34657         }, this);
34658       
34659         this._processLayoutQueue( queue );
34660     },
34661     /** Sets position of item in DOM
34662     * @param {Element} item
34663     * @param {Number} x - horizontal position
34664     * @param {Number} y - vertical position
34665     * @param {Boolean} isInstant - disables transitions
34666     */
34667     _processLayoutQueue : function( queue )
34668     {
34669         for ( var i=0, len = queue.length; i < len; i++ ) {
34670             var obj = queue[i];
34671             obj.item.position('absolute');
34672             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34673         }
34674     },
34675       
34676     
34677     /**
34678     * Any logic you want to do after each layout,
34679     * i.e. size the container
34680     */
34681     _postLayout : function()
34682     {
34683         this.resizeContainer();
34684     },
34685     
34686     resizeContainer : function()
34687     {
34688         if ( !this.isResizingContainer ) {
34689             return;
34690         }
34691         var size = this._getContainerSize();
34692         if ( size ) {
34693             this.el.setSize(size.width,size.height);
34694             this.boxesEl.setSize(size.width,size.height);
34695         }
34696     },
34697     
34698     
34699     
34700     _resetLayout : function()
34701     {
34702         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34703         this.colWidth = this.el.getWidth();
34704         //this.gutter = this.el.getWidth(); 
34705         
34706         this.measureColumns();
34707
34708         // reset column Y
34709         var i = this.cols;
34710         this.colYs = [];
34711         while (i--) {
34712             this.colYs.push( 0 );
34713         }
34714     
34715         this.maxY = 0;
34716     },
34717
34718     measureColumns : function()
34719     {
34720         this.getContainerWidth();
34721       // if columnWidth is 0, default to outerWidth of first item
34722         if ( !this.columnWidth ) {
34723             var firstItem = this.bricks.first();
34724             Roo.log(firstItem);
34725             this.columnWidth  = this.containerWidth;
34726             if (firstItem && firstItem.attr('originalwidth') ) {
34727                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34728             }
34729             // columnWidth fall back to item of first element
34730             Roo.log("set column width?");
34731                         this.initialColumnWidth = this.columnWidth  ;
34732
34733             // if first elem has no width, default to size of container
34734             
34735         }
34736         
34737         
34738         if (this.initialColumnWidth) {
34739             this.columnWidth = this.initialColumnWidth;
34740         }
34741         
34742         
34743             
34744         // column width is fixed at the top - however if container width get's smaller we should
34745         // reduce it...
34746         
34747         // this bit calcs how man columns..
34748             
34749         var columnWidth = this.columnWidth += this.gutter;
34750       
34751         // calculate columns
34752         var containerWidth = this.containerWidth + this.gutter;
34753         
34754         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34755         // fix rounding errors, typically with gutters
34756         var excess = columnWidth - containerWidth % columnWidth;
34757         
34758         
34759         // if overshoot is less than a pixel, round up, otherwise floor it
34760         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34761         cols = Math[ mathMethod ]( cols );
34762         this.cols = Math.max( cols, 1 );
34763         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34764         
34765          // padding positioning..
34766         var totalColWidth = this.cols * this.columnWidth;
34767         var padavail = this.containerWidth - totalColWidth;
34768         // so for 2 columns - we need 3 'pads'
34769         
34770         var padNeeded = (1+this.cols) * this.padWidth;
34771         
34772         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34773         
34774         this.columnWidth += padExtra
34775         //this.padWidth = Math.floor(padavail /  ( this.cols));
34776         
34777         // adjust colum width so that padding is fixed??
34778         
34779         // we have 3 columns ... total = width * 3
34780         // we have X left over... that should be used by 
34781         
34782         //if (this.expandC) {
34783             
34784         //}
34785         
34786         
34787         
34788     },
34789     
34790     getContainerWidth : function()
34791     {
34792        /* // container is parent if fit width
34793         var container = this.isFitWidth ? this.element.parentNode : this.element;
34794         // check that this.size and size are there
34795         // IE8 triggers resize on body size change, so they might not be
34796         
34797         var size = getSize( container );  //FIXME
34798         this.containerWidth = size && size.innerWidth; //FIXME
34799         */
34800          
34801         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34802         
34803     },
34804     
34805     _getItemLayoutPosition : function( item )  // what is item?
34806     {
34807         // we resize the item to our columnWidth..
34808       
34809         item.setWidth(this.columnWidth);
34810         item.autoBoxAdjust  = false;
34811         
34812         var sz = item.getSize();
34813  
34814         // how many columns does this brick span
34815         var remainder = this.containerWidth % this.columnWidth;
34816         
34817         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34818         // round if off by 1 pixel, otherwise use ceil
34819         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34820         colSpan = Math.min( colSpan, this.cols );
34821         
34822         // normally this should be '1' as we dont' currently allow multi width columns..
34823         
34824         var colGroup = this._getColGroup( colSpan );
34825         // get the minimum Y value from the columns
34826         var minimumY = Math.min.apply( Math, colGroup );
34827         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34828         
34829         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34830          
34831         // position the brick
34832         var position = {
34833             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34834             y: this.currentSize.y + minimumY + this.padHeight
34835         };
34836         
34837         Roo.log(position);
34838         // apply setHeight to necessary columns
34839         var setHeight = minimumY + sz.height + this.padHeight;
34840         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34841         
34842         var setSpan = this.cols + 1 - colGroup.length;
34843         for ( var i = 0; i < setSpan; i++ ) {
34844           this.colYs[ shortColIndex + i ] = setHeight ;
34845         }
34846       
34847         return position;
34848     },
34849     
34850     /**
34851      * @param {Number} colSpan - number of columns the element spans
34852      * @returns {Array} colGroup
34853      */
34854     _getColGroup : function( colSpan )
34855     {
34856         if ( colSpan < 2 ) {
34857           // if brick spans only one column, use all the column Ys
34858           return this.colYs;
34859         }
34860       
34861         var colGroup = [];
34862         // how many different places could this brick fit horizontally
34863         var groupCount = this.cols + 1 - colSpan;
34864         // for each group potential horizontal position
34865         for ( var i = 0; i < groupCount; i++ ) {
34866           // make an array of colY values for that one group
34867           var groupColYs = this.colYs.slice( i, i + colSpan );
34868           // and get the max value of the array
34869           colGroup[i] = Math.max.apply( Math, groupColYs );
34870         }
34871         return colGroup;
34872     },
34873     /*
34874     _manageStamp : function( stamp )
34875     {
34876         var stampSize =  stamp.getSize();
34877         var offset = stamp.getBox();
34878         // get the columns that this stamp affects
34879         var firstX = this.isOriginLeft ? offset.x : offset.right;
34880         var lastX = firstX + stampSize.width;
34881         var firstCol = Math.floor( firstX / this.columnWidth );
34882         firstCol = Math.max( 0, firstCol );
34883         
34884         var lastCol = Math.floor( lastX / this.columnWidth );
34885         // lastCol should not go over if multiple of columnWidth #425
34886         lastCol -= lastX % this.columnWidth ? 0 : 1;
34887         lastCol = Math.min( this.cols - 1, lastCol );
34888         
34889         // set colYs to bottom of the stamp
34890         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34891             stampSize.height;
34892             
34893         for ( var i = firstCol; i <= lastCol; i++ ) {
34894           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34895         }
34896     },
34897     */
34898     
34899     _getContainerSize : function()
34900     {
34901         this.maxY = Math.max.apply( Math, this.colYs );
34902         var size = {
34903             height: this.maxY
34904         };
34905       
34906         if ( this.isFitWidth ) {
34907             size.width = this._getContainerFitWidth();
34908         }
34909       
34910         return size;
34911     },
34912     
34913     _getContainerFitWidth : function()
34914     {
34915         var unusedCols = 0;
34916         // count unused columns
34917         var i = this.cols;
34918         while ( --i ) {
34919           if ( this.colYs[i] !== 0 ) {
34920             break;
34921           }
34922           unusedCols++;
34923         }
34924         // fit container to columns that have been used
34925         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34926     },
34927     
34928     needsResizeLayout : function()
34929     {
34930         var previousWidth = this.containerWidth;
34931         this.getContainerWidth();
34932         return previousWidth !== this.containerWidth;
34933     }
34934  
34935 });
34936
34937  
34938
34939  /*
34940  * - LGPL
34941  *
34942  * element
34943  * 
34944  */
34945
34946 /**
34947  * @class Roo.bootstrap.MasonryBrick
34948  * @extends Roo.bootstrap.Component
34949  * Bootstrap MasonryBrick class
34950  * 
34951  * @constructor
34952  * Create a new MasonryBrick
34953  * @param {Object} config The config object
34954  */
34955
34956 Roo.bootstrap.MasonryBrick = function(config){
34957     
34958     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34959     
34960     Roo.bootstrap.MasonryBrick.register(this);
34961     
34962     this.addEvents({
34963         // raw events
34964         /**
34965          * @event click
34966          * When a MasonryBrick is clcik
34967          * @param {Roo.bootstrap.MasonryBrick} this
34968          * @param {Roo.EventObject} e
34969          */
34970         "click" : true
34971     });
34972 };
34973
34974 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34975     
34976     /**
34977      * @cfg {String} title
34978      */   
34979     title : '',
34980     /**
34981      * @cfg {String} html
34982      */   
34983     html : '',
34984     /**
34985      * @cfg {String} bgimage
34986      */   
34987     bgimage : '',
34988     /**
34989      * @cfg {String} videourl
34990      */   
34991     videourl : '',
34992     /**
34993      * @cfg {String} cls
34994      */   
34995     cls : '',
34996     /**
34997      * @cfg {String} href
34998      */   
34999     href : '',
35000     /**
35001      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35002      */   
35003     size : 'xs',
35004     
35005     /**
35006      * @cfg {String} placetitle (center|bottom)
35007      */   
35008     placetitle : '',
35009     
35010     /**
35011      * @cfg {Boolean} isFitContainer defalut true
35012      */   
35013     isFitContainer : true, 
35014     
35015     /**
35016      * @cfg {Boolean} preventDefault defalut false
35017      */   
35018     preventDefault : false, 
35019     
35020     /**
35021      * @cfg {Boolean} inverse defalut false
35022      */   
35023     maskInverse : false, 
35024     
35025     getAutoCreate : function()
35026     {
35027         if(!this.isFitContainer){
35028             return this.getSplitAutoCreate();
35029         }
35030         
35031         var cls = 'masonry-brick masonry-brick-full';
35032         
35033         if(this.href.length){
35034             cls += ' masonry-brick-link';
35035         }
35036         
35037         if(this.bgimage.length){
35038             cls += ' masonry-brick-image';
35039         }
35040         
35041         if(this.maskInverse){
35042             cls += ' mask-inverse';
35043         }
35044         
35045         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35046             cls += ' enable-mask';
35047         }
35048         
35049         if(this.size){
35050             cls += ' masonry-' + this.size + '-brick';
35051         }
35052         
35053         if(this.placetitle.length){
35054             
35055             switch (this.placetitle) {
35056                 case 'center' :
35057                     cls += ' masonry-center-title';
35058                     break;
35059                 case 'bottom' :
35060                     cls += ' masonry-bottom-title';
35061                     break;
35062                 default:
35063                     break;
35064             }
35065             
35066         } else {
35067             if(!this.html.length && !this.bgimage.length){
35068                 cls += ' masonry-center-title';
35069             }
35070
35071             if(!this.html.length && this.bgimage.length){
35072                 cls += ' masonry-bottom-title';
35073             }
35074         }
35075         
35076         if(this.cls){
35077             cls += ' ' + this.cls;
35078         }
35079         
35080         var cfg = {
35081             tag: (this.href.length) ? 'a' : 'div',
35082             cls: cls,
35083             cn: [
35084                 {
35085                     tag: 'div',
35086                     cls: 'masonry-brick-mask'
35087                 },
35088                 {
35089                     tag: 'div',
35090                     cls: 'masonry-brick-paragraph',
35091                     cn: []
35092                 }
35093             ]
35094         };
35095         
35096         if(this.href.length){
35097             cfg.href = this.href;
35098         }
35099         
35100         var cn = cfg.cn[1].cn;
35101         
35102         if(this.title.length){
35103             cn.push({
35104                 tag: 'h4',
35105                 cls: 'masonry-brick-title',
35106                 html: this.title
35107             });
35108         }
35109         
35110         if(this.html.length){
35111             cn.push({
35112                 tag: 'p',
35113                 cls: 'masonry-brick-text',
35114                 html: this.html
35115             });
35116         }
35117         
35118         if (!this.title.length && !this.html.length) {
35119             cfg.cn[1].cls += ' hide';
35120         }
35121         
35122         if(this.bgimage.length){
35123             cfg.cn.push({
35124                 tag: 'img',
35125                 cls: 'masonry-brick-image-view',
35126                 src: this.bgimage
35127             });
35128         }
35129         
35130         if(this.videourl.length){
35131             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35132             // youtube support only?
35133             cfg.cn.push({
35134                 tag: 'iframe',
35135                 cls: 'masonry-brick-image-view',
35136                 src: vurl,
35137                 frameborder : 0,
35138                 allowfullscreen : true
35139             });
35140         }
35141         
35142         return cfg;
35143         
35144     },
35145     
35146     getSplitAutoCreate : function()
35147     {
35148         var cls = 'masonry-brick masonry-brick-split';
35149         
35150         if(this.href.length){
35151             cls += ' masonry-brick-link';
35152         }
35153         
35154         if(this.bgimage.length){
35155             cls += ' masonry-brick-image';
35156         }
35157         
35158         if(this.size){
35159             cls += ' masonry-' + this.size + '-brick';
35160         }
35161         
35162         switch (this.placetitle) {
35163             case 'center' :
35164                 cls += ' masonry-center-title';
35165                 break;
35166             case 'bottom' :
35167                 cls += ' masonry-bottom-title';
35168                 break;
35169             default:
35170                 if(!this.bgimage.length){
35171                     cls += ' masonry-center-title';
35172                 }
35173
35174                 if(this.bgimage.length){
35175                     cls += ' masonry-bottom-title';
35176                 }
35177                 break;
35178         }
35179         
35180         if(this.cls){
35181             cls += ' ' + this.cls;
35182         }
35183         
35184         var cfg = {
35185             tag: (this.href.length) ? 'a' : 'div',
35186             cls: cls,
35187             cn: [
35188                 {
35189                     tag: 'div',
35190                     cls: 'masonry-brick-split-head',
35191                     cn: [
35192                         {
35193                             tag: 'div',
35194                             cls: 'masonry-brick-paragraph',
35195                             cn: []
35196                         }
35197                     ]
35198                 },
35199                 {
35200                     tag: 'div',
35201                     cls: 'masonry-brick-split-body',
35202                     cn: []
35203                 }
35204             ]
35205         };
35206         
35207         if(this.href.length){
35208             cfg.href = this.href;
35209         }
35210         
35211         if(this.title.length){
35212             cfg.cn[0].cn[0].cn.push({
35213                 tag: 'h4',
35214                 cls: 'masonry-brick-title',
35215                 html: this.title
35216             });
35217         }
35218         
35219         if(this.html.length){
35220             cfg.cn[1].cn.push({
35221                 tag: 'p',
35222                 cls: 'masonry-brick-text',
35223                 html: this.html
35224             });
35225         }
35226
35227         if(this.bgimage.length){
35228             cfg.cn[0].cn.push({
35229                 tag: 'img',
35230                 cls: 'masonry-brick-image-view',
35231                 src: this.bgimage
35232             });
35233         }
35234         
35235         if(this.videourl.length){
35236             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35237             // youtube support only?
35238             cfg.cn[0].cn.cn.push({
35239                 tag: 'iframe',
35240                 cls: 'masonry-brick-image-view',
35241                 src: vurl,
35242                 frameborder : 0,
35243                 allowfullscreen : true
35244             });
35245         }
35246         
35247         return cfg;
35248     },
35249     
35250     initEvents: function() 
35251     {
35252         switch (this.size) {
35253             case 'xs' :
35254                 this.x = 1;
35255                 this.y = 1;
35256                 break;
35257             case 'sm' :
35258                 this.x = 2;
35259                 this.y = 2;
35260                 break;
35261             case 'md' :
35262             case 'md-left' :
35263             case 'md-right' :
35264                 this.x = 3;
35265                 this.y = 3;
35266                 break;
35267             case 'tall' :
35268                 this.x = 2;
35269                 this.y = 3;
35270                 break;
35271             case 'wide' :
35272                 this.x = 3;
35273                 this.y = 2;
35274                 break;
35275             case 'wide-thin' :
35276                 this.x = 3;
35277                 this.y = 1;
35278                 break;
35279                         
35280             default :
35281                 break;
35282         }
35283         
35284         if(Roo.isTouch){
35285             this.el.on('touchstart', this.onTouchStart, this);
35286             this.el.on('touchmove', this.onTouchMove, this);
35287             this.el.on('touchend', this.onTouchEnd, this);
35288             this.el.on('contextmenu', this.onContextMenu, this);
35289         } else {
35290             this.el.on('mouseenter'  ,this.enter, this);
35291             this.el.on('mouseleave', this.leave, this);
35292             this.el.on('click', this.onClick, this);
35293         }
35294         
35295         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35296             this.parent().bricks.push(this);   
35297         }
35298         
35299     },
35300     
35301     onClick: function(e, el)
35302     {
35303         var time = this.endTimer - this.startTimer;
35304         // Roo.log(e.preventDefault());
35305         if(Roo.isTouch){
35306             if(time > 1000){
35307                 e.preventDefault();
35308                 return;
35309             }
35310         }
35311         
35312         if(!this.preventDefault){
35313             return;
35314         }
35315         
35316         e.preventDefault();
35317         
35318         if (this.activeClass != '') {
35319             this.selectBrick();
35320         }
35321         
35322         this.fireEvent('click', this, e);
35323     },
35324     
35325     enter: function(e, el)
35326     {
35327         e.preventDefault();
35328         
35329         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35330             return;
35331         }
35332         
35333         if(this.bgimage.length && this.html.length){
35334             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35335         }
35336     },
35337     
35338     leave: function(e, el)
35339     {
35340         e.preventDefault();
35341         
35342         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35343             return;
35344         }
35345         
35346         if(this.bgimage.length && this.html.length){
35347             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35348         }
35349     },
35350     
35351     onTouchStart: function(e, el)
35352     {
35353 //        e.preventDefault();
35354         
35355         this.touchmoved = false;
35356         
35357         if(!this.isFitContainer){
35358             return;
35359         }
35360         
35361         if(!this.bgimage.length || !this.html.length){
35362             return;
35363         }
35364         
35365         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35366         
35367         this.timer = new Date().getTime();
35368         
35369     },
35370     
35371     onTouchMove: function(e, el)
35372     {
35373         this.touchmoved = true;
35374     },
35375     
35376     onContextMenu : function(e,el)
35377     {
35378         e.preventDefault();
35379         e.stopPropagation();
35380         return false;
35381     },
35382     
35383     onTouchEnd: function(e, el)
35384     {
35385 //        e.preventDefault();
35386         
35387         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35388         
35389             this.leave(e,el);
35390             
35391             return;
35392         }
35393         
35394         if(!this.bgimage.length || !this.html.length){
35395             
35396             if(this.href.length){
35397                 window.location.href = this.href;
35398             }
35399             
35400             return;
35401         }
35402         
35403         if(!this.isFitContainer){
35404             return;
35405         }
35406         
35407         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35408         
35409         window.location.href = this.href;
35410     },
35411     
35412     //selection on single brick only
35413     selectBrick : function() {
35414         
35415         if (!this.parentId) {
35416             return;
35417         }
35418         
35419         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35420         var index = m.selectedBrick.indexOf(this.id);
35421         
35422         if ( index > -1) {
35423             m.selectedBrick.splice(index,1);
35424             this.el.removeClass(this.activeClass);
35425             return;
35426         }
35427         
35428         for(var i = 0; i < m.selectedBrick.length; i++) {
35429             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35430             b.el.removeClass(b.activeClass);
35431         }
35432         
35433         m.selectedBrick = [];
35434         
35435         m.selectedBrick.push(this.id);
35436         this.el.addClass(this.activeClass);
35437         return;
35438     },
35439     
35440     isSelected : function(){
35441         return this.el.hasClass(this.activeClass);
35442         
35443     }
35444 });
35445
35446 Roo.apply(Roo.bootstrap.MasonryBrick, {
35447     
35448     //groups: {},
35449     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35450      /**
35451     * register a Masonry Brick
35452     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35453     */
35454     
35455     register : function(brick)
35456     {
35457         //this.groups[brick.id] = brick;
35458         this.groups.add(brick.id, brick);
35459     },
35460     /**
35461     * fetch a  masonry brick based on the masonry brick ID
35462     * @param {string} the masonry brick to add
35463     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35464     */
35465     
35466     get: function(brick_id) 
35467     {
35468         // if (typeof(this.groups[brick_id]) == 'undefined') {
35469         //     return false;
35470         // }
35471         // return this.groups[brick_id] ;
35472         
35473         if(this.groups.key(brick_id)) {
35474             return this.groups.key(brick_id);
35475         }
35476         
35477         return false;
35478     }
35479     
35480     
35481     
35482 });
35483
35484  /*
35485  * - LGPL
35486  *
35487  * element
35488  * 
35489  */
35490
35491 /**
35492  * @class Roo.bootstrap.Brick
35493  * @extends Roo.bootstrap.Component
35494  * Bootstrap Brick class
35495  * 
35496  * @constructor
35497  * Create a new Brick
35498  * @param {Object} config The config object
35499  */
35500
35501 Roo.bootstrap.Brick = function(config){
35502     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35503     
35504     this.addEvents({
35505         // raw events
35506         /**
35507          * @event click
35508          * When a Brick is click
35509          * @param {Roo.bootstrap.Brick} this
35510          * @param {Roo.EventObject} e
35511          */
35512         "click" : true
35513     });
35514 };
35515
35516 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35517     
35518     /**
35519      * @cfg {String} title
35520      */   
35521     title : '',
35522     /**
35523      * @cfg {String} html
35524      */   
35525     html : '',
35526     /**
35527      * @cfg {String} bgimage
35528      */   
35529     bgimage : '',
35530     /**
35531      * @cfg {String} cls
35532      */   
35533     cls : '',
35534     /**
35535      * @cfg {String} href
35536      */   
35537     href : '',
35538     /**
35539      * @cfg {String} video
35540      */   
35541     video : '',
35542     /**
35543      * @cfg {Boolean} square
35544      */   
35545     square : true,
35546     
35547     getAutoCreate : function()
35548     {
35549         var cls = 'roo-brick';
35550         
35551         if(this.href.length){
35552             cls += ' roo-brick-link';
35553         }
35554         
35555         if(this.bgimage.length){
35556             cls += ' roo-brick-image';
35557         }
35558         
35559         if(!this.html.length && !this.bgimage.length){
35560             cls += ' roo-brick-center-title';
35561         }
35562         
35563         if(!this.html.length && this.bgimage.length){
35564             cls += ' roo-brick-bottom-title';
35565         }
35566         
35567         if(this.cls){
35568             cls += ' ' + this.cls;
35569         }
35570         
35571         var cfg = {
35572             tag: (this.href.length) ? 'a' : 'div',
35573             cls: cls,
35574             cn: [
35575                 {
35576                     tag: 'div',
35577                     cls: 'roo-brick-paragraph',
35578                     cn: []
35579                 }
35580             ]
35581         };
35582         
35583         if(this.href.length){
35584             cfg.href = this.href;
35585         }
35586         
35587         var cn = cfg.cn[0].cn;
35588         
35589         if(this.title.length){
35590             cn.push({
35591                 tag: 'h4',
35592                 cls: 'roo-brick-title',
35593                 html: this.title
35594             });
35595         }
35596         
35597         if(this.html.length){
35598             cn.push({
35599                 tag: 'p',
35600                 cls: 'roo-brick-text',
35601                 html: this.html
35602             });
35603         } else {
35604             cn.cls += ' hide';
35605         }
35606         
35607         if(this.bgimage.length){
35608             cfg.cn.push({
35609                 tag: 'img',
35610                 cls: 'roo-brick-image-view',
35611                 src: this.bgimage
35612             });
35613         }
35614         
35615         return cfg;
35616     },
35617     
35618     initEvents: function() 
35619     {
35620         if(this.title.length || this.html.length){
35621             this.el.on('mouseenter'  ,this.enter, this);
35622             this.el.on('mouseleave', this.leave, this);
35623         }
35624         
35625         Roo.EventManager.onWindowResize(this.resize, this); 
35626         
35627         if(this.bgimage.length){
35628             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35629             this.imageEl.on('load', this.onImageLoad, this);
35630             return;
35631         }
35632         
35633         this.resize();
35634     },
35635     
35636     onImageLoad : function()
35637     {
35638         this.resize();
35639     },
35640     
35641     resize : function()
35642     {
35643         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35644         
35645         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35646         
35647         if(this.bgimage.length){
35648             var image = this.el.select('.roo-brick-image-view', true).first();
35649             
35650             image.setWidth(paragraph.getWidth());
35651             
35652             if(this.square){
35653                 image.setHeight(paragraph.getWidth());
35654             }
35655             
35656             this.el.setHeight(image.getHeight());
35657             paragraph.setHeight(image.getHeight());
35658             
35659         }
35660         
35661     },
35662     
35663     enter: function(e, el)
35664     {
35665         e.preventDefault();
35666         
35667         if(this.bgimage.length){
35668             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35669             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35670         }
35671     },
35672     
35673     leave: function(e, el)
35674     {
35675         e.preventDefault();
35676         
35677         if(this.bgimage.length){
35678             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35679             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35680         }
35681     }
35682     
35683 });
35684
35685  
35686
35687  /*
35688  * - LGPL
35689  *
35690  * Number field 
35691  */
35692
35693 /**
35694  * @class Roo.bootstrap.NumberField
35695  * @extends Roo.bootstrap.Input
35696  * Bootstrap NumberField class
35697  * 
35698  * 
35699  * 
35700  * 
35701  * @constructor
35702  * Create a new NumberField
35703  * @param {Object} config The config object
35704  */
35705
35706 Roo.bootstrap.NumberField = function(config){
35707     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35708 };
35709
35710 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35711     
35712     /**
35713      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35714      */
35715     allowDecimals : true,
35716     /**
35717      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35718      */
35719     decimalSeparator : ".",
35720     /**
35721      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35722      */
35723     decimalPrecision : 2,
35724     /**
35725      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35726      */
35727     allowNegative : true,
35728     
35729     /**
35730      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35731      */
35732     allowZero: true,
35733     /**
35734      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35735      */
35736     minValue : Number.NEGATIVE_INFINITY,
35737     /**
35738      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35739      */
35740     maxValue : Number.MAX_VALUE,
35741     /**
35742      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35743      */
35744     minText : "The minimum value for this field is {0}",
35745     /**
35746      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35747      */
35748     maxText : "The maximum value for this field is {0}",
35749     /**
35750      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35751      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35752      */
35753     nanText : "{0} is not a valid number",
35754     /**
35755      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35756      */
35757     thousandsDelimiter : false,
35758     /**
35759      * @cfg {String} valueAlign alignment of value
35760      */
35761     valueAlign : "left",
35762
35763     getAutoCreate : function()
35764     {
35765         var hiddenInput = {
35766             tag: 'input',
35767             type: 'hidden',
35768             id: Roo.id(),
35769             cls: 'hidden-number-input'
35770         };
35771         
35772         if (this.name) {
35773             hiddenInput.name = this.name;
35774         }
35775         
35776         this.name = '';
35777         
35778         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35779         
35780         this.name = hiddenInput.name;
35781         
35782         if(cfg.cn.length > 0) {
35783             cfg.cn.push(hiddenInput);
35784         }
35785         
35786         return cfg;
35787     },
35788
35789     // private
35790     initEvents : function()
35791     {   
35792         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35793         
35794         var allowed = "0123456789";
35795         
35796         if(this.allowDecimals){
35797             allowed += this.decimalSeparator;
35798         }
35799         
35800         if(this.allowNegative){
35801             allowed += "-";
35802         }
35803         
35804         if(this.thousandsDelimiter) {
35805             allowed += ",";
35806         }
35807         
35808         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35809         
35810         var keyPress = function(e){
35811             
35812             var k = e.getKey();
35813             
35814             var c = e.getCharCode();
35815             
35816             if(
35817                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35818                     allowed.indexOf(String.fromCharCode(c)) === -1
35819             ){
35820                 e.stopEvent();
35821                 return;
35822             }
35823             
35824             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35825                 return;
35826             }
35827             
35828             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35829                 e.stopEvent();
35830             }
35831         };
35832         
35833         this.el.on("keypress", keyPress, this);
35834     },
35835     
35836     validateValue : function(value)
35837     {
35838         
35839         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35840             return false;
35841         }
35842         
35843         var num = this.parseValue(value);
35844         
35845         if(isNaN(num)){
35846             this.markInvalid(String.format(this.nanText, value));
35847             return false;
35848         }
35849         
35850         if(num < this.minValue){
35851             this.markInvalid(String.format(this.minText, this.minValue));
35852             return false;
35853         }
35854         
35855         if(num > this.maxValue){
35856             this.markInvalid(String.format(this.maxText, this.maxValue));
35857             return false;
35858         }
35859         
35860         return true;
35861     },
35862
35863     getValue : function()
35864     {
35865         var v = this.hiddenEl().getValue();
35866         
35867         return this.fixPrecision(this.parseValue(v));
35868     },
35869
35870     parseValue : function(value)
35871     {
35872         if(this.thousandsDelimiter) {
35873             value += "";
35874             r = new RegExp(",", "g");
35875             value = value.replace(r, "");
35876         }
35877         
35878         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35879         return isNaN(value) ? '' : value;
35880     },
35881
35882     fixPrecision : function(value)
35883     {
35884         if(this.thousandsDelimiter) {
35885             value += "";
35886             r = new RegExp(",", "g");
35887             value = value.replace(r, "");
35888         }
35889         
35890         var nan = isNaN(value);
35891         
35892         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35893             return nan ? '' : value;
35894         }
35895         return parseFloat(value).toFixed(this.decimalPrecision);
35896     },
35897
35898     setValue : function(v)
35899     {
35900         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35901         
35902         this.value = v;
35903         
35904         if(this.rendered){
35905             
35906             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35907             
35908             this.inputEl().dom.value = (v == '') ? '' :
35909                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35910             
35911             if(!this.allowZero && v === '0') {
35912                 this.hiddenEl().dom.value = '';
35913                 this.inputEl().dom.value = '';
35914             }
35915             
35916             this.validate();
35917         }
35918     },
35919
35920     decimalPrecisionFcn : function(v)
35921     {
35922         return Math.floor(v);
35923     },
35924
35925     beforeBlur : function()
35926     {
35927         var v = this.parseValue(this.getRawValue());
35928         
35929         if(v || v === 0 || v === ''){
35930             this.setValue(v);
35931         }
35932     },
35933     
35934     hiddenEl : function()
35935     {
35936         return this.el.select('input.hidden-number-input',true).first();
35937     }
35938     
35939 });
35940
35941  
35942
35943 /*
35944 * Licence: LGPL
35945 */
35946
35947 /**
35948  * @class Roo.bootstrap.DocumentSlider
35949  * @extends Roo.bootstrap.Component
35950  * Bootstrap DocumentSlider class
35951  * 
35952  * @constructor
35953  * Create a new DocumentViewer
35954  * @param {Object} config The config object
35955  */
35956
35957 Roo.bootstrap.DocumentSlider = function(config){
35958     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35959     
35960     this.files = [];
35961     
35962     this.addEvents({
35963         /**
35964          * @event initial
35965          * Fire after initEvent
35966          * @param {Roo.bootstrap.DocumentSlider} this
35967          */
35968         "initial" : true,
35969         /**
35970          * @event update
35971          * Fire after update
35972          * @param {Roo.bootstrap.DocumentSlider} this
35973          */
35974         "update" : true,
35975         /**
35976          * @event click
35977          * Fire after click
35978          * @param {Roo.bootstrap.DocumentSlider} this
35979          */
35980         "click" : true
35981     });
35982 };
35983
35984 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35985     
35986     files : false,
35987     
35988     indicator : 0,
35989     
35990     getAutoCreate : function()
35991     {
35992         var cfg = {
35993             tag : 'div',
35994             cls : 'roo-document-slider',
35995             cn : [
35996                 {
35997                     tag : 'div',
35998                     cls : 'roo-document-slider-header',
35999                     cn : [
36000                         {
36001                             tag : 'div',
36002                             cls : 'roo-document-slider-header-title'
36003                         }
36004                     ]
36005                 },
36006                 {
36007                     tag : 'div',
36008                     cls : 'roo-document-slider-body',
36009                     cn : [
36010                         {
36011                             tag : 'div',
36012                             cls : 'roo-document-slider-prev',
36013                             cn : [
36014                                 {
36015                                     tag : 'i',
36016                                     cls : 'fa fa-chevron-left'
36017                                 }
36018                             ]
36019                         },
36020                         {
36021                             tag : 'div',
36022                             cls : 'roo-document-slider-thumb',
36023                             cn : [
36024                                 {
36025                                     tag : 'img',
36026                                     cls : 'roo-document-slider-image'
36027                                 }
36028                             ]
36029                         },
36030                         {
36031                             tag : 'div',
36032                             cls : 'roo-document-slider-next',
36033                             cn : [
36034                                 {
36035                                     tag : 'i',
36036                                     cls : 'fa fa-chevron-right'
36037                                 }
36038                             ]
36039                         }
36040                     ]
36041                 }
36042             ]
36043         };
36044         
36045         return cfg;
36046     },
36047     
36048     initEvents : function()
36049     {
36050         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36051         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36052         
36053         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36054         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36055         
36056         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36057         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36058         
36059         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36060         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36061         
36062         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36063         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36064         
36065         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36066         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36067         
36068         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36069         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36070         
36071         this.thumbEl.on('click', this.onClick, this);
36072         
36073         this.prevIndicator.on('click', this.prev, this);
36074         
36075         this.nextIndicator.on('click', this.next, this);
36076         
36077     },
36078     
36079     initial : function()
36080     {
36081         if(this.files.length){
36082             this.indicator = 1;
36083             this.update()
36084         }
36085         
36086         this.fireEvent('initial', this);
36087     },
36088     
36089     update : function()
36090     {
36091         this.imageEl.attr('src', this.files[this.indicator - 1]);
36092         
36093         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36094         
36095         this.prevIndicator.show();
36096         
36097         if(this.indicator == 1){
36098             this.prevIndicator.hide();
36099         }
36100         
36101         this.nextIndicator.show();
36102         
36103         if(this.indicator == this.files.length){
36104             this.nextIndicator.hide();
36105         }
36106         
36107         this.thumbEl.scrollTo('top');
36108         
36109         this.fireEvent('update', this);
36110     },
36111     
36112     onClick : function(e)
36113     {
36114         e.preventDefault();
36115         
36116         this.fireEvent('click', this);
36117     },
36118     
36119     prev : function(e)
36120     {
36121         e.preventDefault();
36122         
36123         this.indicator = Math.max(1, this.indicator - 1);
36124         
36125         this.update();
36126     },
36127     
36128     next : function(e)
36129     {
36130         e.preventDefault();
36131         
36132         this.indicator = Math.min(this.files.length, this.indicator + 1);
36133         
36134         this.update();
36135     }
36136 });
36137 /*
36138  * - LGPL
36139  *
36140  * RadioSet
36141  *
36142  *
36143  */
36144
36145 /**
36146  * @class Roo.bootstrap.RadioSet
36147  * @extends Roo.bootstrap.Input
36148  * Bootstrap RadioSet class
36149  * @cfg {String} indicatorpos (left|right) default left
36150  * @cfg {Boolean} inline (true|false) inline the element (default true)
36151  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36152  * @constructor
36153  * Create a new RadioSet
36154  * @param {Object} config The config object
36155  */
36156
36157 Roo.bootstrap.RadioSet = function(config){
36158     
36159     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36160     
36161     this.radioes = [];
36162     
36163     Roo.bootstrap.RadioSet.register(this);
36164     
36165     this.addEvents({
36166         /**
36167         * @event check
36168         * Fires when the element is checked or unchecked.
36169         * @param {Roo.bootstrap.RadioSet} this This radio
36170         * @param {Roo.bootstrap.Radio} item The checked item
36171         */
36172        check : true,
36173        /**
36174         * @event click
36175         * Fires when the element is click.
36176         * @param {Roo.bootstrap.RadioSet} this This radio set
36177         * @param {Roo.bootstrap.Radio} item The checked item
36178         * @param {Roo.EventObject} e The event object
36179         */
36180        click : true
36181     });
36182     
36183 };
36184
36185 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36186
36187     radioes : false,
36188     
36189     inline : true,
36190     
36191     weight : '',
36192     
36193     indicatorpos : 'left',
36194     
36195     getAutoCreate : function()
36196     {
36197         var label = {
36198             tag : 'label',
36199             cls : 'roo-radio-set-label',
36200             cn : [
36201                 {
36202                     tag : 'span',
36203                     html : this.fieldLabel
36204                 }
36205             ]
36206         };
36207         if (Roo.bootstrap.version == 3) {
36208             
36209             
36210             if(this.indicatorpos == 'left'){
36211                 label.cn.unshift({
36212                     tag : 'i',
36213                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36214                     tooltip : 'This field is required'
36215                 });
36216             } else {
36217                 label.cn.push({
36218                     tag : 'i',
36219                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36220                     tooltip : 'This field is required'
36221                 });
36222             }
36223         }
36224         var items = {
36225             tag : 'div',
36226             cls : 'roo-radio-set-items'
36227         };
36228         
36229         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36230         
36231         if (align === 'left' && this.fieldLabel.length) {
36232             
36233             items = {
36234                 cls : "roo-radio-set-right", 
36235                 cn: [
36236                     items
36237                 ]
36238             };
36239             
36240             if(this.labelWidth > 12){
36241                 label.style = "width: " + this.labelWidth + 'px';
36242             }
36243             
36244             if(this.labelWidth < 13 && this.labelmd == 0){
36245                 this.labelmd = this.labelWidth;
36246             }
36247             
36248             if(this.labellg > 0){
36249                 label.cls += ' col-lg-' + this.labellg;
36250                 items.cls += ' col-lg-' + (12 - this.labellg);
36251             }
36252             
36253             if(this.labelmd > 0){
36254                 label.cls += ' col-md-' + this.labelmd;
36255                 items.cls += ' col-md-' + (12 - this.labelmd);
36256             }
36257             
36258             if(this.labelsm > 0){
36259                 label.cls += ' col-sm-' + this.labelsm;
36260                 items.cls += ' col-sm-' + (12 - this.labelsm);
36261             }
36262             
36263             if(this.labelxs > 0){
36264                 label.cls += ' col-xs-' + this.labelxs;
36265                 items.cls += ' col-xs-' + (12 - this.labelxs);
36266             }
36267         }
36268         
36269         var cfg = {
36270             tag : 'div',
36271             cls : 'roo-radio-set',
36272             cn : [
36273                 {
36274                     tag : 'input',
36275                     cls : 'roo-radio-set-input',
36276                     type : 'hidden',
36277                     name : this.name,
36278                     value : this.value ? this.value :  ''
36279                 },
36280                 label,
36281                 items
36282             ]
36283         };
36284         
36285         if(this.weight.length){
36286             cfg.cls += ' roo-radio-' + this.weight;
36287         }
36288         
36289         if(this.inline) {
36290             cfg.cls += ' roo-radio-set-inline';
36291         }
36292         
36293         var settings=this;
36294         ['xs','sm','md','lg'].map(function(size){
36295             if (settings[size]) {
36296                 cfg.cls += ' col-' + size + '-' + settings[size];
36297             }
36298         });
36299         
36300         return cfg;
36301         
36302     },
36303
36304     initEvents : function()
36305     {
36306         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36307         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36308         
36309         if(!this.fieldLabel.length){
36310             this.labelEl.hide();
36311         }
36312         
36313         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36314         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36315         
36316         this.indicator = this.indicatorEl();
36317         
36318         if(this.indicator){
36319             this.indicator.addClass('invisible');
36320         }
36321         
36322         this.originalValue = this.getValue();
36323         
36324     },
36325     
36326     inputEl: function ()
36327     {
36328         return this.el.select('.roo-radio-set-input', true).first();
36329     },
36330     
36331     getChildContainer : function()
36332     {
36333         return this.itemsEl;
36334     },
36335     
36336     register : function(item)
36337     {
36338         this.radioes.push(item);
36339         
36340     },
36341     
36342     validate : function()
36343     {   
36344         if(this.getVisibilityEl().hasClass('hidden')){
36345             return true;
36346         }
36347         
36348         var valid = false;
36349         
36350         Roo.each(this.radioes, function(i){
36351             if(!i.checked){
36352                 return;
36353             }
36354             
36355             valid = true;
36356             return false;
36357         });
36358         
36359         if(this.allowBlank) {
36360             return true;
36361         }
36362         
36363         if(this.disabled || valid){
36364             this.markValid();
36365             return true;
36366         }
36367         
36368         this.markInvalid();
36369         return false;
36370         
36371     },
36372     
36373     markValid : function()
36374     {
36375         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36376             this.indicatorEl().removeClass('visible');
36377             this.indicatorEl().addClass('invisible');
36378         }
36379         
36380         
36381         if (Roo.bootstrap.version == 3) {
36382             this.el.removeClass([this.invalidClass, this.validClass]);
36383             this.el.addClass(this.validClass);
36384         } else {
36385             this.el.removeClass(['is-invalid','is-valid']);
36386             this.el.addClass(['is-valid']);
36387         }
36388         this.fireEvent('valid', this);
36389     },
36390     
36391     markInvalid : function(msg)
36392     {
36393         if(this.allowBlank || this.disabled){
36394             return;
36395         }
36396         
36397         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36398             this.indicatorEl().removeClass('invisible');
36399             this.indicatorEl().addClass('visible');
36400         }
36401         if (Roo.bootstrap.version == 3) {
36402             this.el.removeClass([this.invalidClass, this.validClass]);
36403             this.el.addClass(this.invalidClass);
36404         } else {
36405             this.el.removeClass(['is-invalid','is-valid']);
36406             this.el.addClass(['is-invalid']);
36407         }
36408         
36409         this.fireEvent('invalid', this, msg);
36410         
36411     },
36412     
36413     setValue : function(v, suppressEvent)
36414     {   
36415         if(this.value === v){
36416             return;
36417         }
36418         
36419         this.value = v;
36420         
36421         if(this.rendered){
36422             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36423         }
36424         
36425         Roo.each(this.radioes, function(i){
36426             i.checked = false;
36427             i.el.removeClass('checked');
36428         });
36429         
36430         Roo.each(this.radioes, function(i){
36431             
36432             if(i.value === v || i.value.toString() === v.toString()){
36433                 i.checked = true;
36434                 i.el.addClass('checked');
36435                 
36436                 if(suppressEvent !== true){
36437                     this.fireEvent('check', this, i);
36438                 }
36439                 
36440                 return false;
36441             }
36442             
36443         }, this);
36444         
36445         this.validate();
36446     },
36447     
36448     clearInvalid : function(){
36449         
36450         if(!this.el || this.preventMark){
36451             return;
36452         }
36453         
36454         this.el.removeClass([this.invalidClass]);
36455         
36456         this.fireEvent('valid', this);
36457     }
36458     
36459 });
36460
36461 Roo.apply(Roo.bootstrap.RadioSet, {
36462     
36463     groups: {},
36464     
36465     register : function(set)
36466     {
36467         this.groups[set.name] = set;
36468     },
36469     
36470     get: function(name) 
36471     {
36472         if (typeof(this.groups[name]) == 'undefined') {
36473             return false;
36474         }
36475         
36476         return this.groups[name] ;
36477     }
36478     
36479 });
36480 /*
36481  * Based on:
36482  * Ext JS Library 1.1.1
36483  * Copyright(c) 2006-2007, Ext JS, LLC.
36484  *
36485  * Originally Released Under LGPL - original licence link has changed is not relivant.
36486  *
36487  * Fork - LGPL
36488  * <script type="text/javascript">
36489  */
36490
36491
36492 /**
36493  * @class Roo.bootstrap.SplitBar
36494  * @extends Roo.util.Observable
36495  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36496  * <br><br>
36497  * Usage:
36498  * <pre><code>
36499 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36500                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36501 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36502 split.minSize = 100;
36503 split.maxSize = 600;
36504 split.animate = true;
36505 split.on('moved', splitterMoved);
36506 </code></pre>
36507  * @constructor
36508  * Create a new SplitBar
36509  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36510  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36511  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36512  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36513                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36514                         position of the SplitBar).
36515  */
36516 Roo.bootstrap.SplitBar = function(cfg){
36517     
36518     /** @private */
36519     
36520     //{
36521     //  dragElement : elm
36522     //  resizingElement: el,
36523         // optional..
36524     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36525     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36526         // existingProxy ???
36527     //}
36528     
36529     this.el = Roo.get(cfg.dragElement, true);
36530     this.el.dom.unselectable = "on";
36531     /** @private */
36532     this.resizingEl = Roo.get(cfg.resizingElement, true);
36533
36534     /**
36535      * @private
36536      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36537      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36538      * @type Number
36539      */
36540     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36541     
36542     /**
36543      * The minimum size of the resizing element. (Defaults to 0)
36544      * @type Number
36545      */
36546     this.minSize = 0;
36547     
36548     /**
36549      * The maximum size of the resizing element. (Defaults to 2000)
36550      * @type Number
36551      */
36552     this.maxSize = 2000;
36553     
36554     /**
36555      * Whether to animate the transition to the new size
36556      * @type Boolean
36557      */
36558     this.animate = false;
36559     
36560     /**
36561      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36562      * @type Boolean
36563      */
36564     this.useShim = false;
36565     
36566     /** @private */
36567     this.shim = null;
36568     
36569     if(!cfg.existingProxy){
36570         /** @private */
36571         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36572     }else{
36573         this.proxy = Roo.get(cfg.existingProxy).dom;
36574     }
36575     /** @private */
36576     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36577     
36578     /** @private */
36579     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36580     
36581     /** @private */
36582     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36583     
36584     /** @private */
36585     this.dragSpecs = {};
36586     
36587     /**
36588      * @private The adapter to use to positon and resize elements
36589      */
36590     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36591     this.adapter.init(this);
36592     
36593     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36594         /** @private */
36595         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36596         this.el.addClass("roo-splitbar-h");
36597     }else{
36598         /** @private */
36599         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36600         this.el.addClass("roo-splitbar-v");
36601     }
36602     
36603     this.addEvents({
36604         /**
36605          * @event resize
36606          * Fires when the splitter is moved (alias for {@link #event-moved})
36607          * @param {Roo.bootstrap.SplitBar} this
36608          * @param {Number} newSize the new width or height
36609          */
36610         "resize" : true,
36611         /**
36612          * @event moved
36613          * Fires when the splitter is moved
36614          * @param {Roo.bootstrap.SplitBar} this
36615          * @param {Number} newSize the new width or height
36616          */
36617         "moved" : true,
36618         /**
36619          * @event beforeresize
36620          * Fires before the splitter is dragged
36621          * @param {Roo.bootstrap.SplitBar} this
36622          */
36623         "beforeresize" : true,
36624
36625         "beforeapply" : true
36626     });
36627
36628     Roo.util.Observable.call(this);
36629 };
36630
36631 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36632     onStartProxyDrag : function(x, y){
36633         this.fireEvent("beforeresize", this);
36634         if(!this.overlay){
36635             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36636             o.unselectable();
36637             o.enableDisplayMode("block");
36638             // all splitbars share the same overlay
36639             Roo.bootstrap.SplitBar.prototype.overlay = o;
36640         }
36641         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36642         this.overlay.show();
36643         Roo.get(this.proxy).setDisplayed("block");
36644         var size = this.adapter.getElementSize(this);
36645         this.activeMinSize = this.getMinimumSize();;
36646         this.activeMaxSize = this.getMaximumSize();;
36647         var c1 = size - this.activeMinSize;
36648         var c2 = Math.max(this.activeMaxSize - size, 0);
36649         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36650             this.dd.resetConstraints();
36651             this.dd.setXConstraint(
36652                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36653                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36654             );
36655             this.dd.setYConstraint(0, 0);
36656         }else{
36657             this.dd.resetConstraints();
36658             this.dd.setXConstraint(0, 0);
36659             this.dd.setYConstraint(
36660                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36661                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36662             );
36663          }
36664         this.dragSpecs.startSize = size;
36665         this.dragSpecs.startPoint = [x, y];
36666         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36667     },
36668     
36669     /** 
36670      * @private Called after the drag operation by the DDProxy
36671      */
36672     onEndProxyDrag : function(e){
36673         Roo.get(this.proxy).setDisplayed(false);
36674         var endPoint = Roo.lib.Event.getXY(e);
36675         if(this.overlay){
36676             this.overlay.hide();
36677         }
36678         var newSize;
36679         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36680             newSize = this.dragSpecs.startSize + 
36681                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36682                     endPoint[0] - this.dragSpecs.startPoint[0] :
36683                     this.dragSpecs.startPoint[0] - endPoint[0]
36684                 );
36685         }else{
36686             newSize = this.dragSpecs.startSize + 
36687                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36688                     endPoint[1] - this.dragSpecs.startPoint[1] :
36689                     this.dragSpecs.startPoint[1] - endPoint[1]
36690                 );
36691         }
36692         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36693         if(newSize != this.dragSpecs.startSize){
36694             if(this.fireEvent('beforeapply', this, newSize) !== false){
36695                 this.adapter.setElementSize(this, newSize);
36696                 this.fireEvent("moved", this, newSize);
36697                 this.fireEvent("resize", this, newSize);
36698             }
36699         }
36700     },
36701     
36702     /**
36703      * Get the adapter this SplitBar uses
36704      * @return The adapter object
36705      */
36706     getAdapter : function(){
36707         return this.adapter;
36708     },
36709     
36710     /**
36711      * Set the adapter this SplitBar uses
36712      * @param {Object} adapter A SplitBar adapter object
36713      */
36714     setAdapter : function(adapter){
36715         this.adapter = adapter;
36716         this.adapter.init(this);
36717     },
36718     
36719     /**
36720      * Gets the minimum size for the resizing element
36721      * @return {Number} The minimum size
36722      */
36723     getMinimumSize : function(){
36724         return this.minSize;
36725     },
36726     
36727     /**
36728      * Sets the minimum size for the resizing element
36729      * @param {Number} minSize The minimum size
36730      */
36731     setMinimumSize : function(minSize){
36732         this.minSize = minSize;
36733     },
36734     
36735     /**
36736      * Gets the maximum size for the resizing element
36737      * @return {Number} The maximum size
36738      */
36739     getMaximumSize : function(){
36740         return this.maxSize;
36741     },
36742     
36743     /**
36744      * Sets the maximum size for the resizing element
36745      * @param {Number} maxSize The maximum size
36746      */
36747     setMaximumSize : function(maxSize){
36748         this.maxSize = maxSize;
36749     },
36750     
36751     /**
36752      * Sets the initialize size for the resizing element
36753      * @param {Number} size The initial size
36754      */
36755     setCurrentSize : function(size){
36756         var oldAnimate = this.animate;
36757         this.animate = false;
36758         this.adapter.setElementSize(this, size);
36759         this.animate = oldAnimate;
36760     },
36761     
36762     /**
36763      * Destroy this splitbar. 
36764      * @param {Boolean} removeEl True to remove the element
36765      */
36766     destroy : function(removeEl){
36767         if(this.shim){
36768             this.shim.remove();
36769         }
36770         this.dd.unreg();
36771         this.proxy.parentNode.removeChild(this.proxy);
36772         if(removeEl){
36773             this.el.remove();
36774         }
36775     }
36776 });
36777
36778 /**
36779  * @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.
36780  */
36781 Roo.bootstrap.SplitBar.createProxy = function(dir){
36782     var proxy = new Roo.Element(document.createElement("div"));
36783     proxy.unselectable();
36784     var cls = 'roo-splitbar-proxy';
36785     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36786     document.body.appendChild(proxy.dom);
36787     return proxy.dom;
36788 };
36789
36790 /** 
36791  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36792  * Default Adapter. It assumes the splitter and resizing element are not positioned
36793  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36794  */
36795 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36796 };
36797
36798 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36799     // do nothing for now
36800     init : function(s){
36801     
36802     },
36803     /**
36804      * Called before drag operations to get the current size of the resizing element. 
36805      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36806      */
36807      getElementSize : function(s){
36808         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36809             return s.resizingEl.getWidth();
36810         }else{
36811             return s.resizingEl.getHeight();
36812         }
36813     },
36814     
36815     /**
36816      * Called after drag operations to set the size of the resizing element.
36817      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36818      * @param {Number} newSize The new size to set
36819      * @param {Function} onComplete A function to be invoked when resizing is complete
36820      */
36821     setElementSize : function(s, newSize, onComplete){
36822         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36823             if(!s.animate){
36824                 s.resizingEl.setWidth(newSize);
36825                 if(onComplete){
36826                     onComplete(s, newSize);
36827                 }
36828             }else{
36829                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36830             }
36831         }else{
36832             
36833             if(!s.animate){
36834                 s.resizingEl.setHeight(newSize);
36835                 if(onComplete){
36836                     onComplete(s, newSize);
36837                 }
36838             }else{
36839                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36840             }
36841         }
36842     }
36843 };
36844
36845 /** 
36846  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36847  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36848  * Adapter that  moves the splitter element to align with the resized sizing element. 
36849  * Used with an absolute positioned SplitBar.
36850  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36851  * document.body, make sure you assign an id to the body element.
36852  */
36853 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36854     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36855     this.container = Roo.get(container);
36856 };
36857
36858 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36859     init : function(s){
36860         this.basic.init(s);
36861     },
36862     
36863     getElementSize : function(s){
36864         return this.basic.getElementSize(s);
36865     },
36866     
36867     setElementSize : function(s, newSize, onComplete){
36868         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36869     },
36870     
36871     moveSplitter : function(s){
36872         var yes = Roo.bootstrap.SplitBar;
36873         switch(s.placement){
36874             case yes.LEFT:
36875                 s.el.setX(s.resizingEl.getRight());
36876                 break;
36877             case yes.RIGHT:
36878                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36879                 break;
36880             case yes.TOP:
36881                 s.el.setY(s.resizingEl.getBottom());
36882                 break;
36883             case yes.BOTTOM:
36884                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36885                 break;
36886         }
36887     }
36888 };
36889
36890 /**
36891  * Orientation constant - Create a vertical SplitBar
36892  * @static
36893  * @type Number
36894  */
36895 Roo.bootstrap.SplitBar.VERTICAL = 1;
36896
36897 /**
36898  * Orientation constant - Create a horizontal SplitBar
36899  * @static
36900  * @type Number
36901  */
36902 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36903
36904 /**
36905  * Placement constant - The resizing element is to the left of the splitter element
36906  * @static
36907  * @type Number
36908  */
36909 Roo.bootstrap.SplitBar.LEFT = 1;
36910
36911 /**
36912  * Placement constant - The resizing element is to the right of the splitter element
36913  * @static
36914  * @type Number
36915  */
36916 Roo.bootstrap.SplitBar.RIGHT = 2;
36917
36918 /**
36919  * Placement constant - The resizing element is positioned above the splitter element
36920  * @static
36921  * @type Number
36922  */
36923 Roo.bootstrap.SplitBar.TOP = 3;
36924
36925 /**
36926  * Placement constant - The resizing element is positioned under splitter element
36927  * @static
36928  * @type Number
36929  */
36930 Roo.bootstrap.SplitBar.BOTTOM = 4;
36931 Roo.namespace("Roo.bootstrap.layout");/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941
36942 /**
36943  * @class Roo.bootstrap.layout.Manager
36944  * @extends Roo.bootstrap.Component
36945  * Base class for layout managers.
36946  */
36947 Roo.bootstrap.layout.Manager = function(config)
36948 {
36949     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36950
36951
36952
36953
36954
36955     /** false to disable window resize monitoring @type Boolean */
36956     this.monitorWindowResize = true;
36957     this.regions = {};
36958     this.addEvents({
36959         /**
36960          * @event layout
36961          * Fires when a layout is performed.
36962          * @param {Roo.LayoutManager} this
36963          */
36964         "layout" : true,
36965         /**
36966          * @event regionresized
36967          * Fires when the user resizes a region.
36968          * @param {Roo.LayoutRegion} region The resized region
36969          * @param {Number} newSize The new size (width for east/west, height for north/south)
36970          */
36971         "regionresized" : true,
36972         /**
36973          * @event regioncollapsed
36974          * Fires when a region is collapsed.
36975          * @param {Roo.LayoutRegion} region The collapsed region
36976          */
36977         "regioncollapsed" : true,
36978         /**
36979          * @event regionexpanded
36980          * Fires when a region is expanded.
36981          * @param {Roo.LayoutRegion} region The expanded region
36982          */
36983         "regionexpanded" : true
36984     });
36985     this.updating = false;
36986
36987     if (config.el) {
36988         this.el = Roo.get(config.el);
36989         this.initEvents();
36990     }
36991
36992 };
36993
36994 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36995
36996
36997     regions : null,
36998
36999     monitorWindowResize : true,
37000
37001
37002     updating : false,
37003
37004
37005     onRender : function(ct, position)
37006     {
37007         if(!this.el){
37008             this.el = Roo.get(ct);
37009             this.initEvents();
37010         }
37011         //this.fireEvent('render',this);
37012     },
37013
37014
37015     initEvents: function()
37016     {
37017
37018
37019         // ie scrollbar fix
37020         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37021             document.body.scroll = "no";
37022         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37023             this.el.position('relative');
37024         }
37025         this.id = this.el.id;
37026         this.el.addClass("roo-layout-container");
37027         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37028         if(this.el.dom != document.body ) {
37029             this.el.on('resize', this.layout,this);
37030             this.el.on('show', this.layout,this);
37031         }
37032
37033     },
37034
37035     /**
37036      * Returns true if this layout is currently being updated
37037      * @return {Boolean}
37038      */
37039     isUpdating : function(){
37040         return this.updating;
37041     },
37042
37043     /**
37044      * Suspend the LayoutManager from doing auto-layouts while
37045      * making multiple add or remove calls
37046      */
37047     beginUpdate : function(){
37048         this.updating = true;
37049     },
37050
37051     /**
37052      * Restore auto-layouts and optionally disable the manager from performing a layout
37053      * @param {Boolean} noLayout true to disable a layout update
37054      */
37055     endUpdate : function(noLayout){
37056         this.updating = false;
37057         if(!noLayout){
37058             this.layout();
37059         }
37060     },
37061
37062     layout: function(){
37063         // abstract...
37064     },
37065
37066     onRegionResized : function(region, newSize){
37067         this.fireEvent("regionresized", region, newSize);
37068         this.layout();
37069     },
37070
37071     onRegionCollapsed : function(region){
37072         this.fireEvent("regioncollapsed", region);
37073     },
37074
37075     onRegionExpanded : function(region){
37076         this.fireEvent("regionexpanded", region);
37077     },
37078
37079     /**
37080      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37081      * performs box-model adjustments.
37082      * @return {Object} The size as an object {width: (the width), height: (the height)}
37083      */
37084     getViewSize : function()
37085     {
37086         var size;
37087         if(this.el.dom != document.body){
37088             size = this.el.getSize();
37089         }else{
37090             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37091         }
37092         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37093         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37094         return size;
37095     },
37096
37097     /**
37098      * Returns the Element this layout is bound to.
37099      * @return {Roo.Element}
37100      */
37101     getEl : function(){
37102         return this.el;
37103     },
37104
37105     /**
37106      * Returns the specified region.
37107      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37108      * @return {Roo.LayoutRegion}
37109      */
37110     getRegion : function(target){
37111         return this.regions[target.toLowerCase()];
37112     },
37113
37114     onWindowResize : function(){
37115         if(this.monitorWindowResize){
37116             this.layout();
37117         }
37118     }
37119 });
37120 /*
37121  * Based on:
37122  * Ext JS Library 1.1.1
37123  * Copyright(c) 2006-2007, Ext JS, LLC.
37124  *
37125  * Originally Released Under LGPL - original licence link has changed is not relivant.
37126  *
37127  * Fork - LGPL
37128  * <script type="text/javascript">
37129  */
37130 /**
37131  * @class Roo.bootstrap.layout.Border
37132  * @extends Roo.bootstrap.layout.Manager
37133  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37134  * please see: examples/bootstrap/nested.html<br><br>
37135  
37136 <b>The container the layout is rendered into can be either the body element or any other element.
37137 If it is not the body element, the container needs to either be an absolute positioned element,
37138 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37139 the container size if it is not the body element.</b>
37140
37141 * @constructor
37142 * Create a new Border
37143 * @param {Object} config Configuration options
37144  */
37145 Roo.bootstrap.layout.Border = function(config){
37146     config = config || {};
37147     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37148     
37149     
37150     
37151     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37152         if(config[region]){
37153             config[region].region = region;
37154             this.addRegion(config[region]);
37155         }
37156     },this);
37157     
37158 };
37159
37160 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37161
37162 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37163     
37164     parent : false, // this might point to a 'nest' or a ???
37165     
37166     /**
37167      * Creates and adds a new region if it doesn't already exist.
37168      * @param {String} target The target region key (north, south, east, west or center).
37169      * @param {Object} config The regions config object
37170      * @return {BorderLayoutRegion} The new region
37171      */
37172     addRegion : function(config)
37173     {
37174         if(!this.regions[config.region]){
37175             var r = this.factory(config);
37176             this.bindRegion(r);
37177         }
37178         return this.regions[config.region];
37179     },
37180
37181     // private (kinda)
37182     bindRegion : function(r){
37183         this.regions[r.config.region] = r;
37184         
37185         r.on("visibilitychange",    this.layout, this);
37186         r.on("paneladded",          this.layout, this);
37187         r.on("panelremoved",        this.layout, this);
37188         r.on("invalidated",         this.layout, this);
37189         r.on("resized",             this.onRegionResized, this);
37190         r.on("collapsed",           this.onRegionCollapsed, this);
37191         r.on("expanded",            this.onRegionExpanded, this);
37192     },
37193
37194     /**
37195      * Performs a layout update.
37196      */
37197     layout : function()
37198     {
37199         if(this.updating) {
37200             return;
37201         }
37202         
37203         // render all the rebions if they have not been done alreayd?
37204         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37205             if(this.regions[region] && !this.regions[region].bodyEl){
37206                 this.regions[region].onRender(this.el)
37207             }
37208         },this);
37209         
37210         var size = this.getViewSize();
37211         var w = size.width;
37212         var h = size.height;
37213         var centerW = w;
37214         var centerH = h;
37215         var centerY = 0;
37216         var centerX = 0;
37217         //var x = 0, y = 0;
37218
37219         var rs = this.regions;
37220         var north = rs["north"];
37221         var south = rs["south"]; 
37222         var west = rs["west"];
37223         var east = rs["east"];
37224         var center = rs["center"];
37225         //if(this.hideOnLayout){ // not supported anymore
37226             //c.el.setStyle("display", "none");
37227         //}
37228         if(north && north.isVisible()){
37229             var b = north.getBox();
37230             var m = north.getMargins();
37231             b.width = w - (m.left+m.right);
37232             b.x = m.left;
37233             b.y = m.top;
37234             centerY = b.height + b.y + m.bottom;
37235             centerH -= centerY;
37236             north.updateBox(this.safeBox(b));
37237         }
37238         if(south && south.isVisible()){
37239             var b = south.getBox();
37240             var m = south.getMargins();
37241             b.width = w - (m.left+m.right);
37242             b.x = m.left;
37243             var totalHeight = (b.height + m.top + m.bottom);
37244             b.y = h - totalHeight + m.top;
37245             centerH -= totalHeight;
37246             south.updateBox(this.safeBox(b));
37247         }
37248         if(west && west.isVisible()){
37249             var b = west.getBox();
37250             var m = west.getMargins();
37251             b.height = centerH - (m.top+m.bottom);
37252             b.x = m.left;
37253             b.y = centerY + m.top;
37254             var totalWidth = (b.width + m.left + m.right);
37255             centerX += totalWidth;
37256             centerW -= totalWidth;
37257             west.updateBox(this.safeBox(b));
37258         }
37259         if(east && east.isVisible()){
37260             var b = east.getBox();
37261             var m = east.getMargins();
37262             b.height = centerH - (m.top+m.bottom);
37263             var totalWidth = (b.width + m.left + m.right);
37264             b.x = w - totalWidth + m.left;
37265             b.y = centerY + m.top;
37266             centerW -= totalWidth;
37267             east.updateBox(this.safeBox(b));
37268         }
37269         if(center){
37270             var m = center.getMargins();
37271             var centerBox = {
37272                 x: centerX + m.left,
37273                 y: centerY + m.top,
37274                 width: centerW - (m.left+m.right),
37275                 height: centerH - (m.top+m.bottom)
37276             };
37277             //if(this.hideOnLayout){
37278                 //center.el.setStyle("display", "block");
37279             //}
37280             center.updateBox(this.safeBox(centerBox));
37281         }
37282         this.el.repaint();
37283         this.fireEvent("layout", this);
37284     },
37285
37286     // private
37287     safeBox : function(box){
37288         box.width = Math.max(0, box.width);
37289         box.height = Math.max(0, box.height);
37290         return box;
37291     },
37292
37293     /**
37294      * Adds a ContentPanel (or subclass) to this layout.
37295      * @param {String} target The target region key (north, south, east, west or center).
37296      * @param {Roo.ContentPanel} panel The panel to add
37297      * @return {Roo.ContentPanel} The added panel
37298      */
37299     add : function(target, panel){
37300          
37301         target = target.toLowerCase();
37302         return this.regions[target].add(panel);
37303     },
37304
37305     /**
37306      * Remove a ContentPanel (or subclass) to this layout.
37307      * @param {String} target The target region key (north, south, east, west or center).
37308      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37309      * @return {Roo.ContentPanel} The removed panel
37310      */
37311     remove : function(target, panel){
37312         target = target.toLowerCase();
37313         return this.regions[target].remove(panel);
37314     },
37315
37316     /**
37317      * Searches all regions for a panel with the specified id
37318      * @param {String} panelId
37319      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37320      */
37321     findPanel : function(panelId){
37322         var rs = this.regions;
37323         for(var target in rs){
37324             if(typeof rs[target] != "function"){
37325                 var p = rs[target].getPanel(panelId);
37326                 if(p){
37327                     return p;
37328                 }
37329             }
37330         }
37331         return null;
37332     },
37333
37334     /**
37335      * Searches all regions for a panel with the specified id and activates (shows) it.
37336      * @param {String/ContentPanel} panelId The panels id or the panel itself
37337      * @return {Roo.ContentPanel} The shown panel or null
37338      */
37339     showPanel : function(panelId) {
37340       var rs = this.regions;
37341       for(var target in rs){
37342          var r = rs[target];
37343          if(typeof r != "function"){
37344             if(r.hasPanel(panelId)){
37345                return r.showPanel(panelId);
37346             }
37347          }
37348       }
37349       return null;
37350    },
37351
37352    /**
37353      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37354      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37355      */
37356    /*
37357     restoreState : function(provider){
37358         if(!provider){
37359             provider = Roo.state.Manager;
37360         }
37361         var sm = new Roo.LayoutStateManager();
37362         sm.init(this, provider);
37363     },
37364 */
37365  
37366  
37367     /**
37368      * Adds a xtype elements to the layout.
37369      * <pre><code>
37370
37371 layout.addxtype({
37372        xtype : 'ContentPanel',
37373        region: 'west',
37374        items: [ .... ]
37375    }
37376 );
37377
37378 layout.addxtype({
37379         xtype : 'NestedLayoutPanel',
37380         region: 'west',
37381         layout: {
37382            center: { },
37383            west: { }   
37384         },
37385         items : [ ... list of content panels or nested layout panels.. ]
37386    }
37387 );
37388 </code></pre>
37389      * @param {Object} cfg Xtype definition of item to add.
37390      */
37391     addxtype : function(cfg)
37392     {
37393         // basically accepts a pannel...
37394         // can accept a layout region..!?!?
37395         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37396         
37397         
37398         // theory?  children can only be panels??
37399         
37400         //if (!cfg.xtype.match(/Panel$/)) {
37401         //    return false;
37402         //}
37403         var ret = false;
37404         
37405         if (typeof(cfg.region) == 'undefined') {
37406             Roo.log("Failed to add Panel, region was not set");
37407             Roo.log(cfg);
37408             return false;
37409         }
37410         var region = cfg.region;
37411         delete cfg.region;
37412         
37413           
37414         var xitems = [];
37415         if (cfg.items) {
37416             xitems = cfg.items;
37417             delete cfg.items;
37418         }
37419         var nb = false;
37420         
37421         if ( region == 'center') {
37422             Roo.log("Center: " + cfg.title);
37423         }
37424         
37425         
37426         switch(cfg.xtype) 
37427         {
37428             case 'Content':  // ContentPanel (el, cfg)
37429             case 'Scroll':  // ContentPanel (el, cfg)
37430             case 'View': 
37431                 cfg.autoCreate = cfg.autoCreate || true;
37432                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37433                 //} else {
37434                 //    var el = this.el.createChild();
37435                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37436                 //}
37437                 
37438                 this.add(region, ret);
37439                 break;
37440             
37441             /*
37442             case 'TreePanel': // our new panel!
37443                 cfg.el = this.el.createChild();
37444                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37445                 this.add(region, ret);
37446                 break;
37447             */
37448             
37449             case 'Nest': 
37450                 // create a new Layout (which is  a Border Layout...
37451                 
37452                 var clayout = cfg.layout;
37453                 clayout.el  = this.el.createChild();
37454                 clayout.items   = clayout.items  || [];
37455                 
37456                 delete cfg.layout;
37457                 
37458                 // replace this exitems with the clayout ones..
37459                 xitems = clayout.items;
37460                  
37461                 // force background off if it's in center...
37462                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37463                     cfg.background = false;
37464                 }
37465                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37466                 
37467                 
37468                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37469                 //console.log('adding nested layout panel '  + cfg.toSource());
37470                 this.add(region, ret);
37471                 nb = {}; /// find first...
37472                 break;
37473             
37474             case 'Grid':
37475                 
37476                 // needs grid and region
37477                 
37478                 //var el = this.getRegion(region).el.createChild();
37479                 /*
37480                  *var el = this.el.createChild();
37481                 // create the grid first...
37482                 cfg.grid.container = el;
37483                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37484                 */
37485                 
37486                 if (region == 'center' && this.active ) {
37487                     cfg.background = false;
37488                 }
37489                 
37490                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37491                 
37492                 this.add(region, ret);
37493                 /*
37494                 if (cfg.background) {
37495                     // render grid on panel activation (if panel background)
37496                     ret.on('activate', function(gp) {
37497                         if (!gp.grid.rendered) {
37498                     //        gp.grid.render(el);
37499                         }
37500                     });
37501                 } else {
37502                   //  cfg.grid.render(el);
37503                 }
37504                 */
37505                 break;
37506            
37507            
37508             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37509                 // it was the old xcomponent building that caused this before.
37510                 // espeically if border is the top element in the tree.
37511                 ret = this;
37512                 break; 
37513                 
37514                     
37515                 
37516                 
37517                 
37518             default:
37519                 /*
37520                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37521                     
37522                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37523                     this.add(region, ret);
37524                 } else {
37525                 */
37526                     Roo.log(cfg);
37527                     throw "Can not add '" + cfg.xtype + "' to Border";
37528                     return null;
37529              
37530                                 
37531              
37532         }
37533         this.beginUpdate();
37534         // add children..
37535         var region = '';
37536         var abn = {};
37537         Roo.each(xitems, function(i)  {
37538             region = nb && i.region ? i.region : false;
37539             
37540             var add = ret.addxtype(i);
37541            
37542             if (region) {
37543                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37544                 if (!i.background) {
37545                     abn[region] = nb[region] ;
37546                 }
37547             }
37548             
37549         });
37550         this.endUpdate();
37551
37552         // make the last non-background panel active..
37553         //if (nb) { Roo.log(abn); }
37554         if (nb) {
37555             
37556             for(var r in abn) {
37557                 region = this.getRegion(r);
37558                 if (region) {
37559                     // tried using nb[r], but it does not work..
37560                      
37561                     region.showPanel(abn[r]);
37562                    
37563                 }
37564             }
37565         }
37566         return ret;
37567         
37568     },
37569     
37570     
37571 // private
37572     factory : function(cfg)
37573     {
37574         
37575         var validRegions = Roo.bootstrap.layout.Border.regions;
37576
37577         var target = cfg.region;
37578         cfg.mgr = this;
37579         
37580         var r = Roo.bootstrap.layout;
37581         Roo.log(target);
37582         switch(target){
37583             case "north":
37584                 return new r.North(cfg);
37585             case "south":
37586                 return new r.South(cfg);
37587             case "east":
37588                 return new r.East(cfg);
37589             case "west":
37590                 return new r.West(cfg);
37591             case "center":
37592                 return new r.Center(cfg);
37593         }
37594         throw 'Layout region "'+target+'" not supported.';
37595     }
37596     
37597     
37598 });
37599  /*
37600  * Based on:
37601  * Ext JS Library 1.1.1
37602  * Copyright(c) 2006-2007, Ext JS, LLC.
37603  *
37604  * Originally Released Under LGPL - original licence link has changed is not relivant.
37605  *
37606  * Fork - LGPL
37607  * <script type="text/javascript">
37608  */
37609  
37610 /**
37611  * @class Roo.bootstrap.layout.Basic
37612  * @extends Roo.util.Observable
37613  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37614  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37615  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37616  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37617  * @cfg {string}   region  the region that it inhabits..
37618  * @cfg {bool}   skipConfig skip config?
37619  * 
37620
37621  */
37622 Roo.bootstrap.layout.Basic = function(config){
37623     
37624     this.mgr = config.mgr;
37625     
37626     this.position = config.region;
37627     
37628     var skipConfig = config.skipConfig;
37629     
37630     this.events = {
37631         /**
37632          * @scope Roo.BasicLayoutRegion
37633          */
37634         
37635         /**
37636          * @event beforeremove
37637          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37638          * @param {Roo.LayoutRegion} this
37639          * @param {Roo.ContentPanel} panel The panel
37640          * @param {Object} e The cancel event object
37641          */
37642         "beforeremove" : true,
37643         /**
37644          * @event invalidated
37645          * Fires when the layout for this region is changed.
37646          * @param {Roo.LayoutRegion} this
37647          */
37648         "invalidated" : true,
37649         /**
37650          * @event visibilitychange
37651          * Fires when this region is shown or hidden 
37652          * @param {Roo.LayoutRegion} this
37653          * @param {Boolean} visibility true or false
37654          */
37655         "visibilitychange" : true,
37656         /**
37657          * @event paneladded
37658          * Fires when a panel is added. 
37659          * @param {Roo.LayoutRegion} this
37660          * @param {Roo.ContentPanel} panel The panel
37661          */
37662         "paneladded" : true,
37663         /**
37664          * @event panelremoved
37665          * Fires when a panel is removed. 
37666          * @param {Roo.LayoutRegion} this
37667          * @param {Roo.ContentPanel} panel The panel
37668          */
37669         "panelremoved" : true,
37670         /**
37671          * @event beforecollapse
37672          * Fires when this region before collapse.
37673          * @param {Roo.LayoutRegion} this
37674          */
37675         "beforecollapse" : true,
37676         /**
37677          * @event collapsed
37678          * Fires when this region is collapsed.
37679          * @param {Roo.LayoutRegion} this
37680          */
37681         "collapsed" : true,
37682         /**
37683          * @event expanded
37684          * Fires when this region is expanded.
37685          * @param {Roo.LayoutRegion} this
37686          */
37687         "expanded" : true,
37688         /**
37689          * @event slideshow
37690          * Fires when this region is slid into view.
37691          * @param {Roo.LayoutRegion} this
37692          */
37693         "slideshow" : true,
37694         /**
37695          * @event slidehide
37696          * Fires when this region slides out of view. 
37697          * @param {Roo.LayoutRegion} this
37698          */
37699         "slidehide" : true,
37700         /**
37701          * @event panelactivated
37702          * Fires when a panel is activated. 
37703          * @param {Roo.LayoutRegion} this
37704          * @param {Roo.ContentPanel} panel The activated panel
37705          */
37706         "panelactivated" : true,
37707         /**
37708          * @event resized
37709          * Fires when the user resizes this region. 
37710          * @param {Roo.LayoutRegion} this
37711          * @param {Number} newSize The new size (width for east/west, height for north/south)
37712          */
37713         "resized" : true
37714     };
37715     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37716     this.panels = new Roo.util.MixedCollection();
37717     this.panels.getKey = this.getPanelId.createDelegate(this);
37718     this.box = null;
37719     this.activePanel = null;
37720     // ensure listeners are added...
37721     
37722     if (config.listeners || config.events) {
37723         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37724             listeners : config.listeners || {},
37725             events : config.events || {}
37726         });
37727     }
37728     
37729     if(skipConfig !== true){
37730         this.applyConfig(config);
37731     }
37732 };
37733
37734 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37735 {
37736     getPanelId : function(p){
37737         return p.getId();
37738     },
37739     
37740     applyConfig : function(config){
37741         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37742         this.config = config;
37743         
37744     },
37745     
37746     /**
37747      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37748      * the width, for horizontal (north, south) the height.
37749      * @param {Number} newSize The new width or height
37750      */
37751     resizeTo : function(newSize){
37752         var el = this.el ? this.el :
37753                  (this.activePanel ? this.activePanel.getEl() : null);
37754         if(el){
37755             switch(this.position){
37756                 case "east":
37757                 case "west":
37758                     el.setWidth(newSize);
37759                     this.fireEvent("resized", this, newSize);
37760                 break;
37761                 case "north":
37762                 case "south":
37763                     el.setHeight(newSize);
37764                     this.fireEvent("resized", this, newSize);
37765                 break;                
37766             }
37767         }
37768     },
37769     
37770     getBox : function(){
37771         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37772     },
37773     
37774     getMargins : function(){
37775         return this.margins;
37776     },
37777     
37778     updateBox : function(box){
37779         this.box = box;
37780         var el = this.activePanel.getEl();
37781         el.dom.style.left = box.x + "px";
37782         el.dom.style.top = box.y + "px";
37783         this.activePanel.setSize(box.width, box.height);
37784     },
37785     
37786     /**
37787      * Returns the container element for this region.
37788      * @return {Roo.Element}
37789      */
37790     getEl : function(){
37791         return this.activePanel;
37792     },
37793     
37794     /**
37795      * Returns true if this region is currently visible.
37796      * @return {Boolean}
37797      */
37798     isVisible : function(){
37799         return this.activePanel ? true : false;
37800     },
37801     
37802     setActivePanel : function(panel){
37803         panel = this.getPanel(panel);
37804         if(this.activePanel && this.activePanel != panel){
37805             this.activePanel.setActiveState(false);
37806             this.activePanel.getEl().setLeftTop(-10000,-10000);
37807         }
37808         this.activePanel = panel;
37809         panel.setActiveState(true);
37810         if(this.box){
37811             panel.setSize(this.box.width, this.box.height);
37812         }
37813         this.fireEvent("panelactivated", this, panel);
37814         this.fireEvent("invalidated");
37815     },
37816     
37817     /**
37818      * Show the specified panel.
37819      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37820      * @return {Roo.ContentPanel} The shown panel or null
37821      */
37822     showPanel : function(panel){
37823         panel = this.getPanel(panel);
37824         if(panel){
37825             this.setActivePanel(panel);
37826         }
37827         return panel;
37828     },
37829     
37830     /**
37831      * Get the active panel for this region.
37832      * @return {Roo.ContentPanel} The active panel or null
37833      */
37834     getActivePanel : function(){
37835         return this.activePanel;
37836     },
37837     
37838     /**
37839      * Add the passed ContentPanel(s)
37840      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37841      * @return {Roo.ContentPanel} The panel added (if only one was added)
37842      */
37843     add : function(panel){
37844         if(arguments.length > 1){
37845             for(var i = 0, len = arguments.length; i < len; i++) {
37846                 this.add(arguments[i]);
37847             }
37848             return null;
37849         }
37850         if(this.hasPanel(panel)){
37851             this.showPanel(panel);
37852             return panel;
37853         }
37854         var el = panel.getEl();
37855         if(el.dom.parentNode != this.mgr.el.dom){
37856             this.mgr.el.dom.appendChild(el.dom);
37857         }
37858         if(panel.setRegion){
37859             panel.setRegion(this);
37860         }
37861         this.panels.add(panel);
37862         el.setStyle("position", "absolute");
37863         if(!panel.background){
37864             this.setActivePanel(panel);
37865             if(this.config.initialSize && this.panels.getCount()==1){
37866                 this.resizeTo(this.config.initialSize);
37867             }
37868         }
37869         this.fireEvent("paneladded", this, panel);
37870         return panel;
37871     },
37872     
37873     /**
37874      * Returns true if the panel is in this region.
37875      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37876      * @return {Boolean}
37877      */
37878     hasPanel : function(panel){
37879         if(typeof panel == "object"){ // must be panel obj
37880             panel = panel.getId();
37881         }
37882         return this.getPanel(panel) ? true : false;
37883     },
37884     
37885     /**
37886      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37887      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37888      * @param {Boolean} preservePanel Overrides the config preservePanel option
37889      * @return {Roo.ContentPanel} The panel that was removed
37890      */
37891     remove : function(panel, preservePanel){
37892         panel = this.getPanel(panel);
37893         if(!panel){
37894             return null;
37895         }
37896         var e = {};
37897         this.fireEvent("beforeremove", this, panel, e);
37898         if(e.cancel === true){
37899             return null;
37900         }
37901         var panelId = panel.getId();
37902         this.panels.removeKey(panelId);
37903         return panel;
37904     },
37905     
37906     /**
37907      * Returns the panel specified or null if it's not in this region.
37908      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37909      * @return {Roo.ContentPanel}
37910      */
37911     getPanel : function(id){
37912         if(typeof id == "object"){ // must be panel obj
37913             return id;
37914         }
37915         return this.panels.get(id);
37916     },
37917     
37918     /**
37919      * Returns this regions position (north/south/east/west/center).
37920      * @return {String} 
37921      */
37922     getPosition: function(){
37923         return this.position;    
37924     }
37925 });/*
37926  * Based on:
37927  * Ext JS Library 1.1.1
37928  * Copyright(c) 2006-2007, Ext JS, LLC.
37929  *
37930  * Originally Released Under LGPL - original licence link has changed is not relivant.
37931  *
37932  * Fork - LGPL
37933  * <script type="text/javascript">
37934  */
37935  
37936 /**
37937  * @class Roo.bootstrap.layout.Region
37938  * @extends Roo.bootstrap.layout.Basic
37939  * This class represents a region in a layout manager.
37940  
37941  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37942  * @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})
37943  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37944  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37945  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37946  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37947  * @cfg {String}    title           The title for the region (overrides panel titles)
37948  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37949  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37950  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37951  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37952  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37953  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37954  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37955  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37956  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37957  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37958
37959  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37960  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37961  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37962  * @cfg {Number}    width           For East/West panels
37963  * @cfg {Number}    height          For North/South panels
37964  * @cfg {Boolean}   split           To show the splitter
37965  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37966  * 
37967  * @cfg {string}   cls             Extra CSS classes to add to region
37968  * 
37969  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37970  * @cfg {string}   region  the region that it inhabits..
37971  *
37972
37973  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37974  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37975
37976  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37977  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37978  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37979  */
37980 Roo.bootstrap.layout.Region = function(config)
37981 {
37982     this.applyConfig(config);
37983
37984     var mgr = config.mgr;
37985     var pos = config.region;
37986     config.skipConfig = true;
37987     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37988     
37989     if (mgr.el) {
37990         this.onRender(mgr.el);   
37991     }
37992      
37993     this.visible = true;
37994     this.collapsed = false;
37995     this.unrendered_panels = [];
37996 };
37997
37998 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37999
38000     position: '', // set by wrapper (eg. north/south etc..)
38001     unrendered_panels : null,  // unrendered panels.
38002     
38003     tabPosition : false,
38004     
38005     mgr: false, // points to 'Border'
38006     
38007     
38008     createBody : function(){
38009         /** This region's body element 
38010         * @type Roo.Element */
38011         this.bodyEl = this.el.createChild({
38012                 tag: "div",
38013                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38014         });
38015     },
38016
38017     onRender: function(ctr, pos)
38018     {
38019         var dh = Roo.DomHelper;
38020         /** This region's container element 
38021         * @type Roo.Element */
38022         this.el = dh.append(ctr.dom, {
38023                 tag: "div",
38024                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38025             }, true);
38026         /** This region's title element 
38027         * @type Roo.Element */
38028     
38029         this.titleEl = dh.append(this.el.dom,  {
38030                 tag: "div",
38031                 unselectable: "on",
38032                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38033                 children:[
38034                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38035                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38036                 ]
38037             }, true);
38038         
38039         this.titleEl.enableDisplayMode();
38040         /** This region's title text element 
38041         * @type HTMLElement */
38042         this.titleTextEl = this.titleEl.dom.firstChild;
38043         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38044         /*
38045         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38046         this.closeBtn.enableDisplayMode();
38047         this.closeBtn.on("click", this.closeClicked, this);
38048         this.closeBtn.hide();
38049     */
38050         this.createBody(this.config);
38051         if(this.config.hideWhenEmpty){
38052             this.hide();
38053             this.on("paneladded", this.validateVisibility, this);
38054             this.on("panelremoved", this.validateVisibility, this);
38055         }
38056         if(this.autoScroll){
38057             this.bodyEl.setStyle("overflow", "auto");
38058         }else{
38059             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38060         }
38061         //if(c.titlebar !== false){
38062             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38063                 this.titleEl.hide();
38064             }else{
38065                 this.titleEl.show();
38066                 if(this.config.title){
38067                     this.titleTextEl.innerHTML = this.config.title;
38068                 }
38069             }
38070         //}
38071         if(this.config.collapsed){
38072             this.collapse(true);
38073         }
38074         if(this.config.hidden){
38075             this.hide();
38076         }
38077         
38078         if (this.unrendered_panels && this.unrendered_panels.length) {
38079             for (var i =0;i< this.unrendered_panels.length; i++) {
38080                 this.add(this.unrendered_panels[i]);
38081             }
38082             this.unrendered_panels = null;
38083             
38084         }
38085         
38086     },
38087     
38088     applyConfig : function(c)
38089     {
38090         /*
38091          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38092             var dh = Roo.DomHelper;
38093             if(c.titlebar !== false){
38094                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38095                 this.collapseBtn.on("click", this.collapse, this);
38096                 this.collapseBtn.enableDisplayMode();
38097                 /*
38098                 if(c.showPin === true || this.showPin){
38099                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38100                     this.stickBtn.enableDisplayMode();
38101                     this.stickBtn.on("click", this.expand, this);
38102                     this.stickBtn.hide();
38103                 }
38104                 
38105             }
38106             */
38107             /** This region's collapsed element
38108             * @type Roo.Element */
38109             /*
38110              *
38111             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38112                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38113             ]}, true);
38114             
38115             if(c.floatable !== false){
38116                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38117                this.collapsedEl.on("click", this.collapseClick, this);
38118             }
38119
38120             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38121                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38122                    id: "message", unselectable: "on", style:{"float":"left"}});
38123                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38124              }
38125             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38126             this.expandBtn.on("click", this.expand, this);
38127             
38128         }
38129         
38130         if(this.collapseBtn){
38131             this.collapseBtn.setVisible(c.collapsible == true);
38132         }
38133         
38134         this.cmargins = c.cmargins || this.cmargins ||
38135                          (this.position == "west" || this.position == "east" ?
38136                              {top: 0, left: 2, right:2, bottom: 0} :
38137                              {top: 2, left: 0, right:0, bottom: 2});
38138         */
38139         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38140         
38141         
38142         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38143         
38144         this.autoScroll = c.autoScroll || false;
38145         
38146         
38147        
38148         
38149         this.duration = c.duration || .30;
38150         this.slideDuration = c.slideDuration || .45;
38151         this.config = c;
38152        
38153     },
38154     /**
38155      * Returns true if this region is currently visible.
38156      * @return {Boolean}
38157      */
38158     isVisible : function(){
38159         return this.visible;
38160     },
38161
38162     /**
38163      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38164      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38165      */
38166     //setCollapsedTitle : function(title){
38167     //    title = title || "&#160;";
38168      //   if(this.collapsedTitleTextEl){
38169       //      this.collapsedTitleTextEl.innerHTML = title;
38170        // }
38171     //},
38172
38173     getBox : function(){
38174         var b;
38175       //  if(!this.collapsed){
38176             b = this.el.getBox(false, true);
38177        // }else{
38178           //  b = this.collapsedEl.getBox(false, true);
38179         //}
38180         return b;
38181     },
38182
38183     getMargins : function(){
38184         return this.margins;
38185         //return this.collapsed ? this.cmargins : this.margins;
38186     },
38187 /*
38188     highlight : function(){
38189         this.el.addClass("x-layout-panel-dragover");
38190     },
38191
38192     unhighlight : function(){
38193         this.el.removeClass("x-layout-panel-dragover");
38194     },
38195 */
38196     updateBox : function(box)
38197     {
38198         if (!this.bodyEl) {
38199             return; // not rendered yet..
38200         }
38201         
38202         this.box = box;
38203         if(!this.collapsed){
38204             this.el.dom.style.left = box.x + "px";
38205             this.el.dom.style.top = box.y + "px";
38206             this.updateBody(box.width, box.height);
38207         }else{
38208             this.collapsedEl.dom.style.left = box.x + "px";
38209             this.collapsedEl.dom.style.top = box.y + "px";
38210             this.collapsedEl.setSize(box.width, box.height);
38211         }
38212         if(this.tabs){
38213             this.tabs.autoSizeTabs();
38214         }
38215     },
38216
38217     updateBody : function(w, h)
38218     {
38219         if(w !== null){
38220             this.el.setWidth(w);
38221             w -= this.el.getBorderWidth("rl");
38222             if(this.config.adjustments){
38223                 w += this.config.adjustments[0];
38224             }
38225         }
38226         if(h !== null && h > 0){
38227             this.el.setHeight(h);
38228             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38229             h -= this.el.getBorderWidth("tb");
38230             if(this.config.adjustments){
38231                 h += this.config.adjustments[1];
38232             }
38233             this.bodyEl.setHeight(h);
38234             if(this.tabs){
38235                 h = this.tabs.syncHeight(h);
38236             }
38237         }
38238         if(this.panelSize){
38239             w = w !== null ? w : this.panelSize.width;
38240             h = h !== null ? h : this.panelSize.height;
38241         }
38242         if(this.activePanel){
38243             var el = this.activePanel.getEl();
38244             w = w !== null ? w : el.getWidth();
38245             h = h !== null ? h : el.getHeight();
38246             this.panelSize = {width: w, height: h};
38247             this.activePanel.setSize(w, h);
38248         }
38249         if(Roo.isIE && this.tabs){
38250             this.tabs.el.repaint();
38251         }
38252     },
38253
38254     /**
38255      * Returns the container element for this region.
38256      * @return {Roo.Element}
38257      */
38258     getEl : function(){
38259         return this.el;
38260     },
38261
38262     /**
38263      * Hides this region.
38264      */
38265     hide : function(){
38266         //if(!this.collapsed){
38267             this.el.dom.style.left = "-2000px";
38268             this.el.hide();
38269         //}else{
38270          //   this.collapsedEl.dom.style.left = "-2000px";
38271          //   this.collapsedEl.hide();
38272        // }
38273         this.visible = false;
38274         this.fireEvent("visibilitychange", this, false);
38275     },
38276
38277     /**
38278      * Shows this region if it was previously hidden.
38279      */
38280     show : function(){
38281         //if(!this.collapsed){
38282             this.el.show();
38283         //}else{
38284         //    this.collapsedEl.show();
38285        // }
38286         this.visible = true;
38287         this.fireEvent("visibilitychange", this, true);
38288     },
38289 /*
38290     closeClicked : function(){
38291         if(this.activePanel){
38292             this.remove(this.activePanel);
38293         }
38294     },
38295
38296     collapseClick : function(e){
38297         if(this.isSlid){
38298            e.stopPropagation();
38299            this.slideIn();
38300         }else{
38301            e.stopPropagation();
38302            this.slideOut();
38303         }
38304     },
38305 */
38306     /**
38307      * Collapses this region.
38308      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38309      */
38310     /*
38311     collapse : function(skipAnim, skipCheck = false){
38312         if(this.collapsed) {
38313             return;
38314         }
38315         
38316         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38317             
38318             this.collapsed = true;
38319             if(this.split){
38320                 this.split.el.hide();
38321             }
38322             if(this.config.animate && skipAnim !== true){
38323                 this.fireEvent("invalidated", this);
38324                 this.animateCollapse();
38325             }else{
38326                 this.el.setLocation(-20000,-20000);
38327                 this.el.hide();
38328                 this.collapsedEl.show();
38329                 this.fireEvent("collapsed", this);
38330                 this.fireEvent("invalidated", this);
38331             }
38332         }
38333         
38334     },
38335 */
38336     animateCollapse : function(){
38337         // overridden
38338     },
38339
38340     /**
38341      * Expands this region if it was previously collapsed.
38342      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38343      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38344      */
38345     /*
38346     expand : function(e, skipAnim){
38347         if(e) {
38348             e.stopPropagation();
38349         }
38350         if(!this.collapsed || this.el.hasActiveFx()) {
38351             return;
38352         }
38353         if(this.isSlid){
38354             this.afterSlideIn();
38355             skipAnim = true;
38356         }
38357         this.collapsed = false;
38358         if(this.config.animate && skipAnim !== true){
38359             this.animateExpand();
38360         }else{
38361             this.el.show();
38362             if(this.split){
38363                 this.split.el.show();
38364             }
38365             this.collapsedEl.setLocation(-2000,-2000);
38366             this.collapsedEl.hide();
38367             this.fireEvent("invalidated", this);
38368             this.fireEvent("expanded", this);
38369         }
38370     },
38371 */
38372     animateExpand : function(){
38373         // overridden
38374     },
38375
38376     initTabs : function()
38377     {
38378         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38379         
38380         var ts = new Roo.bootstrap.panel.Tabs({
38381             el: this.bodyEl.dom,
38382             region : this,
38383             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38384             disableTooltips: this.config.disableTabTips,
38385             toolbar : this.config.toolbar
38386         });
38387         
38388         if(this.config.hideTabs){
38389             ts.stripWrap.setDisplayed(false);
38390         }
38391         this.tabs = ts;
38392         ts.resizeTabs = this.config.resizeTabs === true;
38393         ts.minTabWidth = this.config.minTabWidth || 40;
38394         ts.maxTabWidth = this.config.maxTabWidth || 250;
38395         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38396         ts.monitorResize = false;
38397         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38398         ts.bodyEl.addClass('roo-layout-tabs-body');
38399         this.panels.each(this.initPanelAsTab, this);
38400     },
38401
38402     initPanelAsTab : function(panel){
38403         var ti = this.tabs.addTab(
38404             panel.getEl().id,
38405             panel.getTitle(),
38406             null,
38407             this.config.closeOnTab && panel.isClosable(),
38408             panel.tpl
38409         );
38410         if(panel.tabTip !== undefined){
38411             ti.setTooltip(panel.tabTip);
38412         }
38413         ti.on("activate", function(){
38414               this.setActivePanel(panel);
38415         }, this);
38416         
38417         if(this.config.closeOnTab){
38418             ti.on("beforeclose", function(t, e){
38419                 e.cancel = true;
38420                 this.remove(panel);
38421             }, this);
38422         }
38423         
38424         panel.tabItem = ti;
38425         
38426         return ti;
38427     },
38428
38429     updatePanelTitle : function(panel, title)
38430     {
38431         if(this.activePanel == panel){
38432             this.updateTitle(title);
38433         }
38434         if(this.tabs){
38435             var ti = this.tabs.getTab(panel.getEl().id);
38436             ti.setText(title);
38437             if(panel.tabTip !== undefined){
38438                 ti.setTooltip(panel.tabTip);
38439             }
38440         }
38441     },
38442
38443     updateTitle : function(title){
38444         if(this.titleTextEl && !this.config.title){
38445             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38446         }
38447     },
38448
38449     setActivePanel : function(panel)
38450     {
38451         panel = this.getPanel(panel);
38452         if(this.activePanel && this.activePanel != panel){
38453             if(this.activePanel.setActiveState(false) === false){
38454                 return;
38455             }
38456         }
38457         this.activePanel = panel;
38458         panel.setActiveState(true);
38459         if(this.panelSize){
38460             panel.setSize(this.panelSize.width, this.panelSize.height);
38461         }
38462         if(this.closeBtn){
38463             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38464         }
38465         this.updateTitle(panel.getTitle());
38466         if(this.tabs){
38467             this.fireEvent("invalidated", this);
38468         }
38469         this.fireEvent("panelactivated", this, panel);
38470     },
38471
38472     /**
38473      * Shows the specified panel.
38474      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38475      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38476      */
38477     showPanel : function(panel)
38478     {
38479         panel = this.getPanel(panel);
38480         if(panel){
38481             if(this.tabs){
38482                 var tab = this.tabs.getTab(panel.getEl().id);
38483                 if(tab.isHidden()){
38484                     this.tabs.unhideTab(tab.id);
38485                 }
38486                 tab.activate();
38487             }else{
38488                 this.setActivePanel(panel);
38489             }
38490         }
38491         return panel;
38492     },
38493
38494     /**
38495      * Get the active panel for this region.
38496      * @return {Roo.ContentPanel} The active panel or null
38497      */
38498     getActivePanel : function(){
38499         return this.activePanel;
38500     },
38501
38502     validateVisibility : function(){
38503         if(this.panels.getCount() < 1){
38504             this.updateTitle("&#160;");
38505             this.closeBtn.hide();
38506             this.hide();
38507         }else{
38508             if(!this.isVisible()){
38509                 this.show();
38510             }
38511         }
38512     },
38513
38514     /**
38515      * Adds the passed ContentPanel(s) to this region.
38516      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38517      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38518      */
38519     add : function(panel)
38520     {
38521         if(arguments.length > 1){
38522             for(var i = 0, len = arguments.length; i < len; i++) {
38523                 this.add(arguments[i]);
38524             }
38525             return null;
38526         }
38527         
38528         // if we have not been rendered yet, then we can not really do much of this..
38529         if (!this.bodyEl) {
38530             this.unrendered_panels.push(panel);
38531             return panel;
38532         }
38533         
38534         
38535         
38536         
38537         if(this.hasPanel(panel)){
38538             this.showPanel(panel);
38539             return panel;
38540         }
38541         panel.setRegion(this);
38542         this.panels.add(panel);
38543        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38544             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38545             // and hide them... ???
38546             this.bodyEl.dom.appendChild(panel.getEl().dom);
38547             if(panel.background !== true){
38548                 this.setActivePanel(panel);
38549             }
38550             this.fireEvent("paneladded", this, panel);
38551             return panel;
38552         }
38553         */
38554         if(!this.tabs){
38555             this.initTabs();
38556         }else{
38557             this.initPanelAsTab(panel);
38558         }
38559         
38560         
38561         if(panel.background !== true){
38562             this.tabs.activate(panel.getEl().id);
38563         }
38564         this.fireEvent("paneladded", this, panel);
38565         return panel;
38566     },
38567
38568     /**
38569      * Hides the tab for the specified panel.
38570      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38571      */
38572     hidePanel : function(panel){
38573         if(this.tabs && (panel = this.getPanel(panel))){
38574             this.tabs.hideTab(panel.getEl().id);
38575         }
38576     },
38577
38578     /**
38579      * Unhides the tab for a previously hidden panel.
38580      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38581      */
38582     unhidePanel : function(panel){
38583         if(this.tabs && (panel = this.getPanel(panel))){
38584             this.tabs.unhideTab(panel.getEl().id);
38585         }
38586     },
38587
38588     clearPanels : function(){
38589         while(this.panels.getCount() > 0){
38590              this.remove(this.panels.first());
38591         }
38592     },
38593
38594     /**
38595      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38596      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38597      * @param {Boolean} preservePanel Overrides the config preservePanel option
38598      * @return {Roo.ContentPanel} The panel that was removed
38599      */
38600     remove : function(panel, preservePanel)
38601     {
38602         panel = this.getPanel(panel);
38603         if(!panel){
38604             return null;
38605         }
38606         var e = {};
38607         this.fireEvent("beforeremove", this, panel, e);
38608         if(e.cancel === true){
38609             return null;
38610         }
38611         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38612         var panelId = panel.getId();
38613         this.panels.removeKey(panelId);
38614         if(preservePanel){
38615             document.body.appendChild(panel.getEl().dom);
38616         }
38617         if(this.tabs){
38618             this.tabs.removeTab(panel.getEl().id);
38619         }else if (!preservePanel){
38620             this.bodyEl.dom.removeChild(panel.getEl().dom);
38621         }
38622         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38623             var p = this.panels.first();
38624             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38625             tempEl.appendChild(p.getEl().dom);
38626             this.bodyEl.update("");
38627             this.bodyEl.dom.appendChild(p.getEl().dom);
38628             tempEl = null;
38629             this.updateTitle(p.getTitle());
38630             this.tabs = null;
38631             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38632             this.setActivePanel(p);
38633         }
38634         panel.setRegion(null);
38635         if(this.activePanel == panel){
38636             this.activePanel = null;
38637         }
38638         if(this.config.autoDestroy !== false && preservePanel !== true){
38639             try{panel.destroy();}catch(e){}
38640         }
38641         this.fireEvent("panelremoved", this, panel);
38642         return panel;
38643     },
38644
38645     /**
38646      * Returns the TabPanel component used by this region
38647      * @return {Roo.TabPanel}
38648      */
38649     getTabs : function(){
38650         return this.tabs;
38651     },
38652
38653     createTool : function(parentEl, className){
38654         var btn = Roo.DomHelper.append(parentEl, {
38655             tag: "div",
38656             cls: "x-layout-tools-button",
38657             children: [ {
38658                 tag: "div",
38659                 cls: "roo-layout-tools-button-inner " + className,
38660                 html: "&#160;"
38661             }]
38662         }, true);
38663         btn.addClassOnOver("roo-layout-tools-button-over");
38664         return btn;
38665     }
38666 });/*
38667  * Based on:
38668  * Ext JS Library 1.1.1
38669  * Copyright(c) 2006-2007, Ext JS, LLC.
38670  *
38671  * Originally Released Under LGPL - original licence link has changed is not relivant.
38672  *
38673  * Fork - LGPL
38674  * <script type="text/javascript">
38675  */
38676  
38677
38678
38679 /**
38680  * @class Roo.SplitLayoutRegion
38681  * @extends Roo.LayoutRegion
38682  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38683  */
38684 Roo.bootstrap.layout.Split = function(config){
38685     this.cursor = config.cursor;
38686     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38687 };
38688
38689 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38690 {
38691     splitTip : "Drag to resize.",
38692     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38693     useSplitTips : false,
38694
38695     applyConfig : function(config){
38696         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38697     },
38698     
38699     onRender : function(ctr,pos) {
38700         
38701         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38702         if(!this.config.split){
38703             return;
38704         }
38705         if(!this.split){
38706             
38707             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38708                             tag: "div",
38709                             id: this.el.id + "-split",
38710                             cls: "roo-layout-split roo-layout-split-"+this.position,
38711                             html: "&#160;"
38712             });
38713             /** The SplitBar for this region 
38714             * @type Roo.SplitBar */
38715             // does not exist yet...
38716             Roo.log([this.position, this.orientation]);
38717             
38718             this.split = new Roo.bootstrap.SplitBar({
38719                 dragElement : splitEl,
38720                 resizingElement: this.el,
38721                 orientation : this.orientation
38722             });
38723             
38724             this.split.on("moved", this.onSplitMove, this);
38725             this.split.useShim = this.config.useShim === true;
38726             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38727             if(this.useSplitTips){
38728                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38729             }
38730             //if(config.collapsible){
38731             //    this.split.el.on("dblclick", this.collapse,  this);
38732             //}
38733         }
38734         if(typeof this.config.minSize != "undefined"){
38735             this.split.minSize = this.config.minSize;
38736         }
38737         if(typeof this.config.maxSize != "undefined"){
38738             this.split.maxSize = this.config.maxSize;
38739         }
38740         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38741             this.hideSplitter();
38742         }
38743         
38744     },
38745
38746     getHMaxSize : function(){
38747          var cmax = this.config.maxSize || 10000;
38748          var center = this.mgr.getRegion("center");
38749          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38750     },
38751
38752     getVMaxSize : function(){
38753          var cmax = this.config.maxSize || 10000;
38754          var center = this.mgr.getRegion("center");
38755          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38756     },
38757
38758     onSplitMove : function(split, newSize){
38759         this.fireEvent("resized", this, newSize);
38760     },
38761     
38762     /** 
38763      * Returns the {@link Roo.SplitBar} for this region.
38764      * @return {Roo.SplitBar}
38765      */
38766     getSplitBar : function(){
38767         return this.split;
38768     },
38769     
38770     hide : function(){
38771         this.hideSplitter();
38772         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38773     },
38774
38775     hideSplitter : function(){
38776         if(this.split){
38777             this.split.el.setLocation(-2000,-2000);
38778             this.split.el.hide();
38779         }
38780     },
38781
38782     show : function(){
38783         if(this.split){
38784             this.split.el.show();
38785         }
38786         Roo.bootstrap.layout.Split.superclass.show.call(this);
38787     },
38788     
38789     beforeSlide: function(){
38790         if(Roo.isGecko){// firefox overflow auto bug workaround
38791             this.bodyEl.clip();
38792             if(this.tabs) {
38793                 this.tabs.bodyEl.clip();
38794             }
38795             if(this.activePanel){
38796                 this.activePanel.getEl().clip();
38797                 
38798                 if(this.activePanel.beforeSlide){
38799                     this.activePanel.beforeSlide();
38800                 }
38801             }
38802         }
38803     },
38804     
38805     afterSlide : function(){
38806         if(Roo.isGecko){// firefox overflow auto bug workaround
38807             this.bodyEl.unclip();
38808             if(this.tabs) {
38809                 this.tabs.bodyEl.unclip();
38810             }
38811             if(this.activePanel){
38812                 this.activePanel.getEl().unclip();
38813                 if(this.activePanel.afterSlide){
38814                     this.activePanel.afterSlide();
38815                 }
38816             }
38817         }
38818     },
38819
38820     initAutoHide : function(){
38821         if(this.autoHide !== false){
38822             if(!this.autoHideHd){
38823                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38824                 this.autoHideHd = {
38825                     "mouseout": function(e){
38826                         if(!e.within(this.el, true)){
38827                             st.delay(500);
38828                         }
38829                     },
38830                     "mouseover" : function(e){
38831                         st.cancel();
38832                     },
38833                     scope : this
38834                 };
38835             }
38836             this.el.on(this.autoHideHd);
38837         }
38838     },
38839
38840     clearAutoHide : function(){
38841         if(this.autoHide !== false){
38842             this.el.un("mouseout", this.autoHideHd.mouseout);
38843             this.el.un("mouseover", this.autoHideHd.mouseover);
38844         }
38845     },
38846
38847     clearMonitor : function(){
38848         Roo.get(document).un("click", this.slideInIf, this);
38849     },
38850
38851     // these names are backwards but not changed for compat
38852     slideOut : function(){
38853         if(this.isSlid || this.el.hasActiveFx()){
38854             return;
38855         }
38856         this.isSlid = true;
38857         if(this.collapseBtn){
38858             this.collapseBtn.hide();
38859         }
38860         this.closeBtnState = this.closeBtn.getStyle('display');
38861         this.closeBtn.hide();
38862         if(this.stickBtn){
38863             this.stickBtn.show();
38864         }
38865         this.el.show();
38866         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38867         this.beforeSlide();
38868         this.el.setStyle("z-index", 10001);
38869         this.el.slideIn(this.getSlideAnchor(), {
38870             callback: function(){
38871                 this.afterSlide();
38872                 this.initAutoHide();
38873                 Roo.get(document).on("click", this.slideInIf, this);
38874                 this.fireEvent("slideshow", this);
38875             },
38876             scope: this,
38877             block: true
38878         });
38879     },
38880
38881     afterSlideIn : function(){
38882         this.clearAutoHide();
38883         this.isSlid = false;
38884         this.clearMonitor();
38885         this.el.setStyle("z-index", "");
38886         if(this.collapseBtn){
38887             this.collapseBtn.show();
38888         }
38889         this.closeBtn.setStyle('display', this.closeBtnState);
38890         if(this.stickBtn){
38891             this.stickBtn.hide();
38892         }
38893         this.fireEvent("slidehide", this);
38894     },
38895
38896     slideIn : function(cb){
38897         if(!this.isSlid || this.el.hasActiveFx()){
38898             Roo.callback(cb);
38899             return;
38900         }
38901         this.isSlid = false;
38902         this.beforeSlide();
38903         this.el.slideOut(this.getSlideAnchor(), {
38904             callback: function(){
38905                 this.el.setLeftTop(-10000, -10000);
38906                 this.afterSlide();
38907                 this.afterSlideIn();
38908                 Roo.callback(cb);
38909             },
38910             scope: this,
38911             block: true
38912         });
38913     },
38914     
38915     slideInIf : function(e){
38916         if(!e.within(this.el)){
38917             this.slideIn();
38918         }
38919     },
38920
38921     animateCollapse : function(){
38922         this.beforeSlide();
38923         this.el.setStyle("z-index", 20000);
38924         var anchor = this.getSlideAnchor();
38925         this.el.slideOut(anchor, {
38926             callback : function(){
38927                 this.el.setStyle("z-index", "");
38928                 this.collapsedEl.slideIn(anchor, {duration:.3});
38929                 this.afterSlide();
38930                 this.el.setLocation(-10000,-10000);
38931                 this.el.hide();
38932                 this.fireEvent("collapsed", this);
38933             },
38934             scope: this,
38935             block: true
38936         });
38937     },
38938
38939     animateExpand : function(){
38940         this.beforeSlide();
38941         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38942         this.el.setStyle("z-index", 20000);
38943         this.collapsedEl.hide({
38944             duration:.1
38945         });
38946         this.el.slideIn(this.getSlideAnchor(), {
38947             callback : function(){
38948                 this.el.setStyle("z-index", "");
38949                 this.afterSlide();
38950                 if(this.split){
38951                     this.split.el.show();
38952                 }
38953                 this.fireEvent("invalidated", this);
38954                 this.fireEvent("expanded", this);
38955             },
38956             scope: this,
38957             block: true
38958         });
38959     },
38960
38961     anchors : {
38962         "west" : "left",
38963         "east" : "right",
38964         "north" : "top",
38965         "south" : "bottom"
38966     },
38967
38968     sanchors : {
38969         "west" : "l",
38970         "east" : "r",
38971         "north" : "t",
38972         "south" : "b"
38973     },
38974
38975     canchors : {
38976         "west" : "tl-tr",
38977         "east" : "tr-tl",
38978         "north" : "tl-bl",
38979         "south" : "bl-tl"
38980     },
38981
38982     getAnchor : function(){
38983         return this.anchors[this.position];
38984     },
38985
38986     getCollapseAnchor : function(){
38987         return this.canchors[this.position];
38988     },
38989
38990     getSlideAnchor : function(){
38991         return this.sanchors[this.position];
38992     },
38993
38994     getAlignAdj : function(){
38995         var cm = this.cmargins;
38996         switch(this.position){
38997             case "west":
38998                 return [0, 0];
38999             break;
39000             case "east":
39001                 return [0, 0];
39002             break;
39003             case "north":
39004                 return [0, 0];
39005             break;
39006             case "south":
39007                 return [0, 0];
39008             break;
39009         }
39010     },
39011
39012     getExpandAdj : function(){
39013         var c = this.collapsedEl, cm = this.cmargins;
39014         switch(this.position){
39015             case "west":
39016                 return [-(cm.right+c.getWidth()+cm.left), 0];
39017             break;
39018             case "east":
39019                 return [cm.right+c.getWidth()+cm.left, 0];
39020             break;
39021             case "north":
39022                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39023             break;
39024             case "south":
39025                 return [0, cm.top+cm.bottom+c.getHeight()];
39026             break;
39027         }
39028     }
39029 });/*
39030  * Based on:
39031  * Ext JS Library 1.1.1
39032  * Copyright(c) 2006-2007, Ext JS, LLC.
39033  *
39034  * Originally Released Under LGPL - original licence link has changed is not relivant.
39035  *
39036  * Fork - LGPL
39037  * <script type="text/javascript">
39038  */
39039 /*
39040  * These classes are private internal classes
39041  */
39042 Roo.bootstrap.layout.Center = function(config){
39043     config.region = "center";
39044     Roo.bootstrap.layout.Region.call(this, config);
39045     this.visible = true;
39046     this.minWidth = config.minWidth || 20;
39047     this.minHeight = config.minHeight || 20;
39048 };
39049
39050 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39051     hide : function(){
39052         // center panel can't be hidden
39053     },
39054     
39055     show : function(){
39056         // center panel can't be hidden
39057     },
39058     
39059     getMinWidth: function(){
39060         return this.minWidth;
39061     },
39062     
39063     getMinHeight: function(){
39064         return this.minHeight;
39065     }
39066 });
39067
39068
39069
39070
39071  
39072
39073
39074
39075
39076
39077
39078 Roo.bootstrap.layout.North = function(config)
39079 {
39080     config.region = 'north';
39081     config.cursor = 'n-resize';
39082     
39083     Roo.bootstrap.layout.Split.call(this, config);
39084     
39085     
39086     if(this.split){
39087         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39088         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39089         this.split.el.addClass("roo-layout-split-v");
39090     }
39091     var size = config.initialSize || config.height;
39092     if(typeof size != "undefined"){
39093         this.el.setHeight(size);
39094     }
39095 };
39096 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39097 {
39098     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39099     
39100     
39101     
39102     getBox : function(){
39103         if(this.collapsed){
39104             return this.collapsedEl.getBox();
39105         }
39106         var box = this.el.getBox();
39107         if(this.split){
39108             box.height += this.split.el.getHeight();
39109         }
39110         return box;
39111     },
39112     
39113     updateBox : function(box){
39114         if(this.split && !this.collapsed){
39115             box.height -= this.split.el.getHeight();
39116             this.split.el.setLeft(box.x);
39117             this.split.el.setTop(box.y+box.height);
39118             this.split.el.setWidth(box.width);
39119         }
39120         if(this.collapsed){
39121             this.updateBody(box.width, null);
39122         }
39123         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39124     }
39125 });
39126
39127
39128
39129
39130
39131 Roo.bootstrap.layout.South = function(config){
39132     config.region = 'south';
39133     config.cursor = 's-resize';
39134     Roo.bootstrap.layout.Split.call(this, config);
39135     if(this.split){
39136         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39137         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39138         this.split.el.addClass("roo-layout-split-v");
39139     }
39140     var size = config.initialSize || config.height;
39141     if(typeof size != "undefined"){
39142         this.el.setHeight(size);
39143     }
39144 };
39145
39146 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39147     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39148     getBox : function(){
39149         if(this.collapsed){
39150             return this.collapsedEl.getBox();
39151         }
39152         var box = this.el.getBox();
39153         if(this.split){
39154             var sh = this.split.el.getHeight();
39155             box.height += sh;
39156             box.y -= sh;
39157         }
39158         return box;
39159     },
39160     
39161     updateBox : function(box){
39162         if(this.split && !this.collapsed){
39163             var sh = this.split.el.getHeight();
39164             box.height -= sh;
39165             box.y += sh;
39166             this.split.el.setLeft(box.x);
39167             this.split.el.setTop(box.y-sh);
39168             this.split.el.setWidth(box.width);
39169         }
39170         if(this.collapsed){
39171             this.updateBody(box.width, null);
39172         }
39173         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39174     }
39175 });
39176
39177 Roo.bootstrap.layout.East = function(config){
39178     config.region = "east";
39179     config.cursor = "e-resize";
39180     Roo.bootstrap.layout.Split.call(this, config);
39181     if(this.split){
39182         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39183         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39184         this.split.el.addClass("roo-layout-split-h");
39185     }
39186     var size = config.initialSize || config.width;
39187     if(typeof size != "undefined"){
39188         this.el.setWidth(size);
39189     }
39190 };
39191 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39192     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39193     getBox : function(){
39194         if(this.collapsed){
39195             return this.collapsedEl.getBox();
39196         }
39197         var box = this.el.getBox();
39198         if(this.split){
39199             var sw = this.split.el.getWidth();
39200             box.width += sw;
39201             box.x -= sw;
39202         }
39203         return box;
39204     },
39205
39206     updateBox : function(box){
39207         if(this.split && !this.collapsed){
39208             var sw = this.split.el.getWidth();
39209             box.width -= sw;
39210             this.split.el.setLeft(box.x);
39211             this.split.el.setTop(box.y);
39212             this.split.el.setHeight(box.height);
39213             box.x += sw;
39214         }
39215         if(this.collapsed){
39216             this.updateBody(null, box.height);
39217         }
39218         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39219     }
39220 });
39221
39222 Roo.bootstrap.layout.West = function(config){
39223     config.region = "west";
39224     config.cursor = "w-resize";
39225     
39226     Roo.bootstrap.layout.Split.call(this, config);
39227     if(this.split){
39228         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39229         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39230         this.split.el.addClass("roo-layout-split-h");
39231     }
39232     
39233 };
39234 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39235     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39236     
39237     onRender: function(ctr, pos)
39238     {
39239         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39240         var size = this.config.initialSize || this.config.width;
39241         if(typeof size != "undefined"){
39242             this.el.setWidth(size);
39243         }
39244     },
39245     
39246     getBox : function(){
39247         if(this.collapsed){
39248             return this.collapsedEl.getBox();
39249         }
39250         var box = this.el.getBox();
39251         if(this.split){
39252             box.width += this.split.el.getWidth();
39253         }
39254         return box;
39255     },
39256     
39257     updateBox : function(box){
39258         if(this.split && !this.collapsed){
39259             var sw = this.split.el.getWidth();
39260             box.width -= sw;
39261             this.split.el.setLeft(box.x+box.width);
39262             this.split.el.setTop(box.y);
39263             this.split.el.setHeight(box.height);
39264         }
39265         if(this.collapsed){
39266             this.updateBody(null, box.height);
39267         }
39268         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39269     }
39270 });Roo.namespace("Roo.bootstrap.panel");/*
39271  * Based on:
39272  * Ext JS Library 1.1.1
39273  * Copyright(c) 2006-2007, Ext JS, LLC.
39274  *
39275  * Originally Released Under LGPL - original licence link has changed is not relivant.
39276  *
39277  * Fork - LGPL
39278  * <script type="text/javascript">
39279  */
39280 /**
39281  * @class Roo.ContentPanel
39282  * @extends Roo.util.Observable
39283  * A basic ContentPanel element.
39284  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39285  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39286  * @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
39287  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39288  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39289  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39290  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39291  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39292  * @cfg {String} title          The title for this panel
39293  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39294  * @cfg {String} url            Calls {@link #setUrl} with this value
39295  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39296  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39297  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39298  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39299  * @cfg {Boolean} badges render the badges
39300  * @cfg {String} cls  extra classes to use  
39301  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39302
39303  * @constructor
39304  * Create a new ContentPanel.
39305  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39306  * @param {String/Object} config A string to set only the title or a config object
39307  * @param {String} content (optional) Set the HTML content for this panel
39308  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39309  */
39310 Roo.bootstrap.panel.Content = function( config){
39311     
39312     this.tpl = config.tpl || false;
39313     
39314     var el = config.el;
39315     var content = config.content;
39316
39317     if(config.autoCreate){ // xtype is available if this is called from factory
39318         el = Roo.id();
39319     }
39320     this.el = Roo.get(el);
39321     if(!this.el && config && config.autoCreate){
39322         if(typeof config.autoCreate == "object"){
39323             if(!config.autoCreate.id){
39324                 config.autoCreate.id = config.id||el;
39325             }
39326             this.el = Roo.DomHelper.append(document.body,
39327                         config.autoCreate, true);
39328         }else{
39329             var elcfg =  {
39330                 tag: "div",
39331                 cls: (config.cls || '') +
39332                     (config.background ? ' bg-' + config.background : '') +
39333                     " roo-layout-inactive-content",
39334                 id: config.id||el
39335             };
39336             if (config.html) {
39337                 elcfg.html = config.html;
39338                 
39339             }
39340                         
39341             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39342         }
39343     } 
39344     this.closable = false;
39345     this.loaded = false;
39346     this.active = false;
39347    
39348       
39349     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39350         
39351         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39352         
39353         this.wrapEl = this.el; //this.el.wrap();
39354         var ti = [];
39355         if (config.toolbar.items) {
39356             ti = config.toolbar.items ;
39357             delete config.toolbar.items ;
39358         }
39359         
39360         var nitems = [];
39361         this.toolbar.render(this.wrapEl, 'before');
39362         for(var i =0;i < ti.length;i++) {
39363           //  Roo.log(['add child', items[i]]);
39364             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39365         }
39366         this.toolbar.items = nitems;
39367         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39368         delete config.toolbar;
39369         
39370     }
39371     /*
39372     // xtype created footer. - not sure if will work as we normally have to render first..
39373     if (this.footer && !this.footer.el && this.footer.xtype) {
39374         if (!this.wrapEl) {
39375             this.wrapEl = this.el.wrap();
39376         }
39377     
39378         this.footer.container = this.wrapEl.createChild();
39379          
39380         this.footer = Roo.factory(this.footer, Roo);
39381         
39382     }
39383     */
39384     
39385      if(typeof config == "string"){
39386         this.title = config;
39387     }else{
39388         Roo.apply(this, config);
39389     }
39390     
39391     if(this.resizeEl){
39392         this.resizeEl = Roo.get(this.resizeEl, true);
39393     }else{
39394         this.resizeEl = this.el;
39395     }
39396     // handle view.xtype
39397     
39398  
39399     
39400     
39401     this.addEvents({
39402         /**
39403          * @event activate
39404          * Fires when this panel is activated. 
39405          * @param {Roo.ContentPanel} this
39406          */
39407         "activate" : true,
39408         /**
39409          * @event deactivate
39410          * Fires when this panel is activated. 
39411          * @param {Roo.ContentPanel} this
39412          */
39413         "deactivate" : true,
39414
39415         /**
39416          * @event resize
39417          * Fires when this panel is resized if fitToFrame is true.
39418          * @param {Roo.ContentPanel} this
39419          * @param {Number} width The width after any component adjustments
39420          * @param {Number} height The height after any component adjustments
39421          */
39422         "resize" : true,
39423         
39424          /**
39425          * @event render
39426          * Fires when this tab is created
39427          * @param {Roo.ContentPanel} this
39428          */
39429         "render" : true
39430         
39431         
39432         
39433     });
39434     
39435
39436     
39437     
39438     if(this.autoScroll){
39439         this.resizeEl.setStyle("overflow", "auto");
39440     } else {
39441         // fix randome scrolling
39442         //this.el.on('scroll', function() {
39443         //    Roo.log('fix random scolling');
39444         //    this.scrollTo('top',0); 
39445         //});
39446     }
39447     content = content || this.content;
39448     if(content){
39449         this.setContent(content);
39450     }
39451     if(config && config.url){
39452         this.setUrl(this.url, this.params, this.loadOnce);
39453     }
39454     
39455     
39456     
39457     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39458     
39459     if (this.view && typeof(this.view.xtype) != 'undefined') {
39460         this.view.el = this.el.appendChild(document.createElement("div"));
39461         this.view = Roo.factory(this.view); 
39462         this.view.render  &&  this.view.render(false, '');  
39463     }
39464     
39465     
39466     this.fireEvent('render', this);
39467 };
39468
39469 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39470     
39471     cls : '',
39472     background : '',
39473     
39474     tabTip : '',
39475     
39476     setRegion : function(region){
39477         this.region = region;
39478         this.setActiveClass(region && !this.background);
39479     },
39480     
39481     
39482     setActiveClass: function(state)
39483     {
39484         if(state){
39485            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39486            this.el.setStyle('position','relative');
39487         }else{
39488            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39489            this.el.setStyle('position', 'absolute');
39490         } 
39491     },
39492     
39493     /**
39494      * Returns the toolbar for this Panel if one was configured. 
39495      * @return {Roo.Toolbar} 
39496      */
39497     getToolbar : function(){
39498         return this.toolbar;
39499     },
39500     
39501     setActiveState : function(active)
39502     {
39503         this.active = active;
39504         this.setActiveClass(active);
39505         if(!active){
39506             if(this.fireEvent("deactivate", this) === false){
39507                 return false;
39508             }
39509             return true;
39510         }
39511         this.fireEvent("activate", this);
39512         return true;
39513     },
39514     /**
39515      * Updates this panel's element
39516      * @param {String} content The new content
39517      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39518     */
39519     setContent : function(content, loadScripts){
39520         this.el.update(content, loadScripts);
39521     },
39522
39523     ignoreResize : function(w, h){
39524         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39525             return true;
39526         }else{
39527             this.lastSize = {width: w, height: h};
39528             return false;
39529         }
39530     },
39531     /**
39532      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39533      * @return {Roo.UpdateManager} The UpdateManager
39534      */
39535     getUpdateManager : function(){
39536         return this.el.getUpdateManager();
39537     },
39538      /**
39539      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39540      * @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:
39541 <pre><code>
39542 panel.load({
39543     url: "your-url.php",
39544     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39545     callback: yourFunction,
39546     scope: yourObject, //(optional scope)
39547     discardUrl: false,
39548     nocache: false,
39549     text: "Loading...",
39550     timeout: 30,
39551     scripts: false
39552 });
39553 </code></pre>
39554      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39555      * 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.
39556      * @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}
39557      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39558      * @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.
39559      * @return {Roo.ContentPanel} this
39560      */
39561     load : function(){
39562         var um = this.el.getUpdateManager();
39563         um.update.apply(um, arguments);
39564         return this;
39565     },
39566
39567
39568     /**
39569      * 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.
39570      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39571      * @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)
39572      * @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)
39573      * @return {Roo.UpdateManager} The UpdateManager
39574      */
39575     setUrl : function(url, params, loadOnce){
39576         if(this.refreshDelegate){
39577             this.removeListener("activate", this.refreshDelegate);
39578         }
39579         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39580         this.on("activate", this.refreshDelegate);
39581         return this.el.getUpdateManager();
39582     },
39583     
39584     _handleRefresh : function(url, params, loadOnce){
39585         if(!loadOnce || !this.loaded){
39586             var updater = this.el.getUpdateManager();
39587             updater.update(url, params, this._setLoaded.createDelegate(this));
39588         }
39589     },
39590     
39591     _setLoaded : function(){
39592         this.loaded = true;
39593     }, 
39594     
39595     /**
39596      * Returns this panel's id
39597      * @return {String} 
39598      */
39599     getId : function(){
39600         return this.el.id;
39601     },
39602     
39603     /** 
39604      * Returns this panel's element - used by regiosn to add.
39605      * @return {Roo.Element} 
39606      */
39607     getEl : function(){
39608         return this.wrapEl || this.el;
39609     },
39610     
39611    
39612     
39613     adjustForComponents : function(width, height)
39614     {
39615         //Roo.log('adjustForComponents ');
39616         if(this.resizeEl != this.el){
39617             width -= this.el.getFrameWidth('lr');
39618             height -= this.el.getFrameWidth('tb');
39619         }
39620         if(this.toolbar){
39621             var te = this.toolbar.getEl();
39622             te.setWidth(width);
39623             height -= te.getHeight();
39624         }
39625         if(this.footer){
39626             var te = this.footer.getEl();
39627             te.setWidth(width);
39628             height -= te.getHeight();
39629         }
39630         
39631         
39632         if(this.adjustments){
39633             width += this.adjustments[0];
39634             height += this.adjustments[1];
39635         }
39636         return {"width": width, "height": height};
39637     },
39638     
39639     setSize : function(width, height){
39640         if(this.fitToFrame && !this.ignoreResize(width, height)){
39641             if(this.fitContainer && this.resizeEl != this.el){
39642                 this.el.setSize(width, height);
39643             }
39644             var size = this.adjustForComponents(width, height);
39645             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39646             this.fireEvent('resize', this, size.width, size.height);
39647         }
39648     },
39649     
39650     /**
39651      * Returns this panel's title
39652      * @return {String} 
39653      */
39654     getTitle : function(){
39655         
39656         if (typeof(this.title) != 'object') {
39657             return this.title;
39658         }
39659         
39660         var t = '';
39661         for (var k in this.title) {
39662             if (!this.title.hasOwnProperty(k)) {
39663                 continue;
39664             }
39665             
39666             if (k.indexOf('-') >= 0) {
39667                 var s = k.split('-');
39668                 for (var i = 0; i<s.length; i++) {
39669                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39670                 }
39671             } else {
39672                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39673             }
39674         }
39675         return t;
39676     },
39677     
39678     /**
39679      * Set this panel's title
39680      * @param {String} title
39681      */
39682     setTitle : function(title){
39683         this.title = title;
39684         if(this.region){
39685             this.region.updatePanelTitle(this, title);
39686         }
39687     },
39688     
39689     /**
39690      * Returns true is this panel was configured to be closable
39691      * @return {Boolean} 
39692      */
39693     isClosable : function(){
39694         return this.closable;
39695     },
39696     
39697     beforeSlide : function(){
39698         this.el.clip();
39699         this.resizeEl.clip();
39700     },
39701     
39702     afterSlide : function(){
39703         this.el.unclip();
39704         this.resizeEl.unclip();
39705     },
39706     
39707     /**
39708      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39709      *   Will fail silently if the {@link #setUrl} method has not been called.
39710      *   This does not activate the panel, just updates its content.
39711      */
39712     refresh : function(){
39713         if(this.refreshDelegate){
39714            this.loaded = false;
39715            this.refreshDelegate();
39716         }
39717     },
39718     
39719     /**
39720      * Destroys this panel
39721      */
39722     destroy : function(){
39723         this.el.removeAllListeners();
39724         var tempEl = document.createElement("span");
39725         tempEl.appendChild(this.el.dom);
39726         tempEl.innerHTML = "";
39727         this.el.remove();
39728         this.el = null;
39729     },
39730     
39731     /**
39732      * form - if the content panel contains a form - this is a reference to it.
39733      * @type {Roo.form.Form}
39734      */
39735     form : false,
39736     /**
39737      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39738      *    This contains a reference to it.
39739      * @type {Roo.View}
39740      */
39741     view : false,
39742     
39743       /**
39744      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39745      * <pre><code>
39746
39747 layout.addxtype({
39748        xtype : 'Form',
39749        items: [ .... ]
39750    }
39751 );
39752
39753 </code></pre>
39754      * @param {Object} cfg Xtype definition of item to add.
39755      */
39756     
39757     
39758     getChildContainer: function () {
39759         return this.getEl();
39760     }
39761     
39762     
39763     /*
39764         var  ret = new Roo.factory(cfg);
39765         return ret;
39766         
39767         
39768         // add form..
39769         if (cfg.xtype.match(/^Form$/)) {
39770             
39771             var el;
39772             //if (this.footer) {
39773             //    el = this.footer.container.insertSibling(false, 'before');
39774             //} else {
39775                 el = this.el.createChild();
39776             //}
39777
39778             this.form = new  Roo.form.Form(cfg);
39779             
39780             
39781             if ( this.form.allItems.length) {
39782                 this.form.render(el.dom);
39783             }
39784             return this.form;
39785         }
39786         // should only have one of theses..
39787         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39788             // views.. should not be just added - used named prop 'view''
39789             
39790             cfg.el = this.el.appendChild(document.createElement("div"));
39791             // factory?
39792             
39793             var ret = new Roo.factory(cfg);
39794              
39795              ret.render && ret.render(false, ''); // render blank..
39796             this.view = ret;
39797             return ret;
39798         }
39799         return false;
39800     }
39801     \*/
39802 });
39803  
39804 /**
39805  * @class Roo.bootstrap.panel.Grid
39806  * @extends Roo.bootstrap.panel.Content
39807  * @constructor
39808  * Create a new GridPanel.
39809  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39810  * @param {Object} config A the config object
39811   
39812  */
39813
39814
39815
39816 Roo.bootstrap.panel.Grid = function(config)
39817 {
39818     
39819       
39820     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39821         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39822
39823     config.el = this.wrapper;
39824     //this.el = this.wrapper;
39825     
39826       if (config.container) {
39827         // ctor'ed from a Border/panel.grid
39828         
39829         
39830         this.wrapper.setStyle("overflow", "hidden");
39831         this.wrapper.addClass('roo-grid-container');
39832
39833     }
39834     
39835     
39836     if(config.toolbar){
39837         var tool_el = this.wrapper.createChild();    
39838         this.toolbar = Roo.factory(config.toolbar);
39839         var ti = [];
39840         if (config.toolbar.items) {
39841             ti = config.toolbar.items ;
39842             delete config.toolbar.items ;
39843         }
39844         
39845         var nitems = [];
39846         this.toolbar.render(tool_el);
39847         for(var i =0;i < ti.length;i++) {
39848           //  Roo.log(['add child', items[i]]);
39849             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39850         }
39851         this.toolbar.items = nitems;
39852         
39853         delete config.toolbar;
39854     }
39855     
39856     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39857     config.grid.scrollBody = true;;
39858     config.grid.monitorWindowResize = false; // turn off autosizing
39859     config.grid.autoHeight = false;
39860     config.grid.autoWidth = false;
39861     
39862     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39863     
39864     if (config.background) {
39865         // render grid on panel activation (if panel background)
39866         this.on('activate', function(gp) {
39867             if (!gp.grid.rendered) {
39868                 gp.grid.render(this.wrapper);
39869                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39870             }
39871         });
39872             
39873     } else {
39874         this.grid.render(this.wrapper);
39875         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39876
39877     }
39878     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39879     // ??? needed ??? config.el = this.wrapper;
39880     
39881     
39882     
39883   
39884     // xtype created footer. - not sure if will work as we normally have to render first..
39885     if (this.footer && !this.footer.el && this.footer.xtype) {
39886         
39887         var ctr = this.grid.getView().getFooterPanel(true);
39888         this.footer.dataSource = this.grid.dataSource;
39889         this.footer = Roo.factory(this.footer, Roo);
39890         this.footer.render(ctr);
39891         
39892     }
39893     
39894     
39895     
39896     
39897      
39898 };
39899
39900 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39901     getId : function(){
39902         return this.grid.id;
39903     },
39904     
39905     /**
39906      * Returns the grid for this panel
39907      * @return {Roo.bootstrap.Table} 
39908      */
39909     getGrid : function(){
39910         return this.grid;    
39911     },
39912     
39913     setSize : function(width, height){
39914         if(!this.ignoreResize(width, height)){
39915             var grid = this.grid;
39916             var size = this.adjustForComponents(width, height);
39917             // tfoot is not a footer?
39918           
39919             
39920             var gridel = grid.getGridEl();
39921             gridel.setSize(size.width, size.height);
39922             
39923             var tbd = grid.getGridEl().select('tbody', true).first();
39924             var thd = grid.getGridEl().select('thead',true).first();
39925             var tbf= grid.getGridEl().select('tfoot', true).first();
39926
39927             if (tbf) {
39928                 size.height -= thd.getHeight();
39929             }
39930             if (thd) {
39931                 size.height -= thd.getHeight();
39932             }
39933             
39934             tbd.setSize(size.width, size.height );
39935             // this is for the account management tab -seems to work there.
39936             var thd = grid.getGridEl().select('thead',true).first();
39937             //if (tbd) {
39938             //    tbd.setSize(size.width, size.height - thd.getHeight());
39939             //}
39940              
39941             grid.autoSize();
39942         }
39943     },
39944      
39945     
39946     
39947     beforeSlide : function(){
39948         this.grid.getView().scroller.clip();
39949     },
39950     
39951     afterSlide : function(){
39952         this.grid.getView().scroller.unclip();
39953     },
39954     
39955     destroy : function(){
39956         this.grid.destroy();
39957         delete this.grid;
39958         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39959     }
39960 });
39961
39962 /**
39963  * @class Roo.bootstrap.panel.Nest
39964  * @extends Roo.bootstrap.panel.Content
39965  * @constructor
39966  * Create a new Panel, that can contain a layout.Border.
39967  * 
39968  * 
39969  * @param {Roo.BorderLayout} layout The layout for this panel
39970  * @param {String/Object} config A string to set only the title or a config object
39971  */
39972 Roo.bootstrap.panel.Nest = function(config)
39973 {
39974     // construct with only one argument..
39975     /* FIXME - implement nicer consturctors
39976     if (layout.layout) {
39977         config = layout;
39978         layout = config.layout;
39979         delete config.layout;
39980     }
39981     if (layout.xtype && !layout.getEl) {
39982         // then layout needs constructing..
39983         layout = Roo.factory(layout, Roo);
39984     }
39985     */
39986     
39987     config.el =  config.layout.getEl();
39988     
39989     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39990     
39991     config.layout.monitorWindowResize = false; // turn off autosizing
39992     this.layout = config.layout;
39993     this.layout.getEl().addClass("roo-layout-nested-layout");
39994     this.layout.parent = this;
39995     
39996     
39997     
39998     
39999 };
40000
40001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40002
40003     setSize : function(width, height){
40004         if(!this.ignoreResize(width, height)){
40005             var size = this.adjustForComponents(width, height);
40006             var el = this.layout.getEl();
40007             if (size.height < 1) {
40008                 el.setWidth(size.width);   
40009             } else {
40010                 el.setSize(size.width, size.height);
40011             }
40012             var touch = el.dom.offsetWidth;
40013             this.layout.layout();
40014             // ie requires a double layout on the first pass
40015             if(Roo.isIE && !this.initialized){
40016                 this.initialized = true;
40017                 this.layout.layout();
40018             }
40019         }
40020     },
40021     
40022     // activate all subpanels if not currently active..
40023     
40024     setActiveState : function(active){
40025         this.active = active;
40026         this.setActiveClass(active);
40027         
40028         if(!active){
40029             this.fireEvent("deactivate", this);
40030             return;
40031         }
40032         
40033         this.fireEvent("activate", this);
40034         // not sure if this should happen before or after..
40035         if (!this.layout) {
40036             return; // should not happen..
40037         }
40038         var reg = false;
40039         for (var r in this.layout.regions) {
40040             reg = this.layout.getRegion(r);
40041             if (reg.getActivePanel()) {
40042                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40043                 reg.setActivePanel(reg.getActivePanel());
40044                 continue;
40045             }
40046             if (!reg.panels.length) {
40047                 continue;
40048             }
40049             reg.showPanel(reg.getPanel(0));
40050         }
40051         
40052         
40053         
40054         
40055     },
40056     
40057     /**
40058      * Returns the nested BorderLayout for this panel
40059      * @return {Roo.BorderLayout} 
40060      */
40061     getLayout : function(){
40062         return this.layout;
40063     },
40064     
40065      /**
40066      * Adds a xtype elements to the layout of the nested panel
40067      * <pre><code>
40068
40069 panel.addxtype({
40070        xtype : 'ContentPanel',
40071        region: 'west',
40072        items: [ .... ]
40073    }
40074 );
40075
40076 panel.addxtype({
40077         xtype : 'NestedLayoutPanel',
40078         region: 'west',
40079         layout: {
40080            center: { },
40081            west: { }   
40082         },
40083         items : [ ... list of content panels or nested layout panels.. ]
40084    }
40085 );
40086 </code></pre>
40087      * @param {Object} cfg Xtype definition of item to add.
40088      */
40089     addxtype : function(cfg) {
40090         return this.layout.addxtype(cfg);
40091     
40092     }
40093 });/*
40094  * Based on:
40095  * Ext JS Library 1.1.1
40096  * Copyright(c) 2006-2007, Ext JS, LLC.
40097  *
40098  * Originally Released Under LGPL - original licence link has changed is not relivant.
40099  *
40100  * Fork - LGPL
40101  * <script type="text/javascript">
40102  */
40103 /**
40104  * @class Roo.TabPanel
40105  * @extends Roo.util.Observable
40106  * A lightweight tab container.
40107  * <br><br>
40108  * Usage:
40109  * <pre><code>
40110 // basic tabs 1, built from existing content
40111 var tabs = new Roo.TabPanel("tabs1");
40112 tabs.addTab("script", "View Script");
40113 tabs.addTab("markup", "View Markup");
40114 tabs.activate("script");
40115
40116 // more advanced tabs, built from javascript
40117 var jtabs = new Roo.TabPanel("jtabs");
40118 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40119
40120 // set up the UpdateManager
40121 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40122 var updater = tab2.getUpdateManager();
40123 updater.setDefaultUrl("ajax1.htm");
40124 tab2.on('activate', updater.refresh, updater, true);
40125
40126 // Use setUrl for Ajax loading
40127 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40128 tab3.setUrl("ajax2.htm", null, true);
40129
40130 // Disabled tab
40131 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40132 tab4.disable();
40133
40134 jtabs.activate("jtabs-1");
40135  * </code></pre>
40136  * @constructor
40137  * Create a new TabPanel.
40138  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40139  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40140  */
40141 Roo.bootstrap.panel.Tabs = function(config){
40142     /**
40143     * The container element for this TabPanel.
40144     * @type Roo.Element
40145     */
40146     this.el = Roo.get(config.el);
40147     delete config.el;
40148     if(config){
40149         if(typeof config == "boolean"){
40150             this.tabPosition = config ? "bottom" : "top";
40151         }else{
40152             Roo.apply(this, config);
40153         }
40154     }
40155     
40156     if(this.tabPosition == "bottom"){
40157         // if tabs are at the bottom = create the body first.
40158         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40159         this.el.addClass("roo-tabs-bottom");
40160     }
40161     // next create the tabs holders
40162     
40163     if (this.tabPosition == "west"){
40164         
40165         var reg = this.region; // fake it..
40166         while (reg) {
40167             if (!reg.mgr.parent) {
40168                 break;
40169             }
40170             reg = reg.mgr.parent.region;
40171         }
40172         Roo.log("got nest?");
40173         Roo.log(reg);
40174         if (reg.mgr.getRegion('west')) {
40175             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40176             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40177             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40178             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40179             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40180         
40181             
40182         }
40183         
40184         
40185     } else {
40186      
40187         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40188         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40189         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40190         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40191     }
40192     
40193     
40194     if(Roo.isIE){
40195         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40196     }
40197     
40198     // finally - if tabs are at the top, then create the body last..
40199     if(this.tabPosition != "bottom"){
40200         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40201          * @type Roo.Element
40202          */
40203         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40204         this.el.addClass("roo-tabs-top");
40205     }
40206     this.items = [];
40207
40208     this.bodyEl.setStyle("position", "relative");
40209
40210     this.active = null;
40211     this.activateDelegate = this.activate.createDelegate(this);
40212
40213     this.addEvents({
40214         /**
40215          * @event tabchange
40216          * Fires when the active tab changes
40217          * @param {Roo.TabPanel} this
40218          * @param {Roo.TabPanelItem} activePanel The new active tab
40219          */
40220         "tabchange": true,
40221         /**
40222          * @event beforetabchange
40223          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40224          * @param {Roo.TabPanel} this
40225          * @param {Object} e Set cancel to true on this object to cancel the tab change
40226          * @param {Roo.TabPanelItem} tab The tab being changed to
40227          */
40228         "beforetabchange" : true
40229     });
40230
40231     Roo.EventManager.onWindowResize(this.onResize, this);
40232     this.cpad = this.el.getPadding("lr");
40233     this.hiddenCount = 0;
40234
40235
40236     // toolbar on the tabbar support...
40237     if (this.toolbar) {
40238         alert("no toolbar support yet");
40239         this.toolbar  = false;
40240         /*
40241         var tcfg = this.toolbar;
40242         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40243         this.toolbar = new Roo.Toolbar(tcfg);
40244         if (Roo.isSafari) {
40245             var tbl = tcfg.container.child('table', true);
40246             tbl.setAttribute('width', '100%');
40247         }
40248         */
40249         
40250     }
40251    
40252
40253
40254     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40255 };
40256
40257 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40258     /*
40259      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40260      */
40261     tabPosition : "top",
40262     /*
40263      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40264      */
40265     currentTabWidth : 0,
40266     /*
40267      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40268      */
40269     minTabWidth : 40,
40270     /*
40271      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40272      */
40273     maxTabWidth : 250,
40274     /*
40275      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40276      */
40277     preferredTabWidth : 175,
40278     /*
40279      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40280      */
40281     resizeTabs : false,
40282     /*
40283      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40284      */
40285     monitorResize : true,
40286     /*
40287      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40288      */
40289     toolbar : false,  // set by caller..
40290     
40291     region : false, /// set by caller
40292     
40293     disableTooltips : true, // not used yet...
40294
40295     /**
40296      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40297      * @param {String} id The id of the div to use <b>or create</b>
40298      * @param {String} text The text for the tab
40299      * @param {String} content (optional) Content to put in the TabPanelItem body
40300      * @param {Boolean} closable (optional) True to create a close icon on the tab
40301      * @return {Roo.TabPanelItem} The created TabPanelItem
40302      */
40303     addTab : function(id, text, content, closable, tpl)
40304     {
40305         var item = new Roo.bootstrap.panel.TabItem({
40306             panel: this,
40307             id : id,
40308             text : text,
40309             closable : closable,
40310             tpl : tpl
40311         });
40312         this.addTabItem(item);
40313         if(content){
40314             item.setContent(content);
40315         }
40316         return item;
40317     },
40318
40319     /**
40320      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40321      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40322      * @return {Roo.TabPanelItem}
40323      */
40324     getTab : function(id){
40325         return this.items[id];
40326     },
40327
40328     /**
40329      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40330      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40331      */
40332     hideTab : function(id){
40333         var t = this.items[id];
40334         if(!t.isHidden()){
40335            t.setHidden(true);
40336            this.hiddenCount++;
40337            this.autoSizeTabs();
40338         }
40339     },
40340
40341     /**
40342      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40343      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40344      */
40345     unhideTab : function(id){
40346         var t = this.items[id];
40347         if(t.isHidden()){
40348            t.setHidden(false);
40349            this.hiddenCount--;
40350            this.autoSizeTabs();
40351         }
40352     },
40353
40354     /**
40355      * Adds an existing {@link Roo.TabPanelItem}.
40356      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40357      */
40358     addTabItem : function(item)
40359     {
40360         this.items[item.id] = item;
40361         this.items.push(item);
40362         this.autoSizeTabs();
40363       //  if(this.resizeTabs){
40364     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40365   //         this.autoSizeTabs();
40366 //        }else{
40367 //            item.autoSize();
40368        // }
40369     },
40370
40371     /**
40372      * Removes a {@link Roo.TabPanelItem}.
40373      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40374      */
40375     removeTab : function(id){
40376         var items = this.items;
40377         var tab = items[id];
40378         if(!tab) { return; }
40379         var index = items.indexOf(tab);
40380         if(this.active == tab && items.length > 1){
40381             var newTab = this.getNextAvailable(index);
40382             if(newTab) {
40383                 newTab.activate();
40384             }
40385         }
40386         this.stripEl.dom.removeChild(tab.pnode.dom);
40387         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40388             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40389         }
40390         items.splice(index, 1);
40391         delete this.items[tab.id];
40392         tab.fireEvent("close", tab);
40393         tab.purgeListeners();
40394         this.autoSizeTabs();
40395     },
40396
40397     getNextAvailable : function(start){
40398         var items = this.items;
40399         var index = start;
40400         // look for a next tab that will slide over to
40401         // replace the one being removed
40402         while(index < items.length){
40403             var item = items[++index];
40404             if(item && !item.isHidden()){
40405                 return item;
40406             }
40407         }
40408         // if one isn't found select the previous tab (on the left)
40409         index = start;
40410         while(index >= 0){
40411             var item = items[--index];
40412             if(item && !item.isHidden()){
40413                 return item;
40414             }
40415         }
40416         return null;
40417     },
40418
40419     /**
40420      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40421      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40422      */
40423     disableTab : function(id){
40424         var tab = this.items[id];
40425         if(tab && this.active != tab){
40426             tab.disable();
40427         }
40428     },
40429
40430     /**
40431      * Enables a {@link Roo.TabPanelItem} that is disabled.
40432      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40433      */
40434     enableTab : function(id){
40435         var tab = this.items[id];
40436         tab.enable();
40437     },
40438
40439     /**
40440      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40441      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40442      * @return {Roo.TabPanelItem} The TabPanelItem.
40443      */
40444     activate : function(id)
40445     {
40446         //Roo.log('activite:'  + id);
40447         
40448         var tab = this.items[id];
40449         if(!tab){
40450             return null;
40451         }
40452         if(tab == this.active || tab.disabled){
40453             return tab;
40454         }
40455         var e = {};
40456         this.fireEvent("beforetabchange", this, e, tab);
40457         if(e.cancel !== true && !tab.disabled){
40458             if(this.active){
40459                 this.active.hide();
40460             }
40461             this.active = this.items[id];
40462             this.active.show();
40463             this.fireEvent("tabchange", this, this.active);
40464         }
40465         return tab;
40466     },
40467
40468     /**
40469      * Gets the active {@link Roo.TabPanelItem}.
40470      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40471      */
40472     getActiveTab : function(){
40473         return this.active;
40474     },
40475
40476     /**
40477      * Updates the tab body element to fit the height of the container element
40478      * for overflow scrolling
40479      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40480      */
40481     syncHeight : function(targetHeight){
40482         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40483         var bm = this.bodyEl.getMargins();
40484         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40485         this.bodyEl.setHeight(newHeight);
40486         return newHeight;
40487     },
40488
40489     onResize : function(){
40490         if(this.monitorResize){
40491             this.autoSizeTabs();
40492         }
40493     },
40494
40495     /**
40496      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40497      */
40498     beginUpdate : function(){
40499         this.updating = true;
40500     },
40501
40502     /**
40503      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40504      */
40505     endUpdate : function(){
40506         this.updating = false;
40507         this.autoSizeTabs();
40508     },
40509
40510     /**
40511      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40512      */
40513     autoSizeTabs : function()
40514     {
40515         var count = this.items.length;
40516         var vcount = count - this.hiddenCount;
40517         
40518         if (vcount < 2) {
40519             this.stripEl.hide();
40520         } else {
40521             this.stripEl.show();
40522         }
40523         
40524         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40525             return;
40526         }
40527         
40528         
40529         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40530         var availWidth = Math.floor(w / vcount);
40531         var b = this.stripBody;
40532         if(b.getWidth() > w){
40533             var tabs = this.items;
40534             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40535             if(availWidth < this.minTabWidth){
40536                 /*if(!this.sleft){    // incomplete scrolling code
40537                     this.createScrollButtons();
40538                 }
40539                 this.showScroll();
40540                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40541             }
40542         }else{
40543             if(this.currentTabWidth < this.preferredTabWidth){
40544                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40545             }
40546         }
40547     },
40548
40549     /**
40550      * Returns the number of tabs in this TabPanel.
40551      * @return {Number}
40552      */
40553      getCount : function(){
40554          return this.items.length;
40555      },
40556
40557     /**
40558      * Resizes all the tabs to the passed width
40559      * @param {Number} The new width
40560      */
40561     setTabWidth : function(width){
40562         this.currentTabWidth = width;
40563         for(var i = 0, len = this.items.length; i < len; i++) {
40564                 if(!this.items[i].isHidden()) {
40565                 this.items[i].setWidth(width);
40566             }
40567         }
40568     },
40569
40570     /**
40571      * Destroys this TabPanel
40572      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40573      */
40574     destroy : function(removeEl){
40575         Roo.EventManager.removeResizeListener(this.onResize, this);
40576         for(var i = 0, len = this.items.length; i < len; i++){
40577             this.items[i].purgeListeners();
40578         }
40579         if(removeEl === true){
40580             this.el.update("");
40581             this.el.remove();
40582         }
40583     },
40584     
40585     createStrip : function(container)
40586     {
40587         var strip = document.createElement("nav");
40588         strip.className = Roo.bootstrap.version == 4 ?
40589             "navbar-light bg-light" : 
40590             "navbar navbar-default"; //"x-tabs-wrap";
40591         container.appendChild(strip);
40592         return strip;
40593     },
40594     
40595     createStripList : function(strip)
40596     {
40597         // div wrapper for retard IE
40598         // returns the "tr" element.
40599         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40600         //'<div class="x-tabs-strip-wrap">'+
40601           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40602           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40603         return strip.firstChild; //.firstChild.firstChild.firstChild;
40604     },
40605     createBody : function(container)
40606     {
40607         var body = document.createElement("div");
40608         Roo.id(body, "tab-body");
40609         //Roo.fly(body).addClass("x-tabs-body");
40610         Roo.fly(body).addClass("tab-content");
40611         container.appendChild(body);
40612         return body;
40613     },
40614     createItemBody :function(bodyEl, id){
40615         var body = Roo.getDom(id);
40616         if(!body){
40617             body = document.createElement("div");
40618             body.id = id;
40619         }
40620         //Roo.fly(body).addClass("x-tabs-item-body");
40621         Roo.fly(body).addClass("tab-pane");
40622          bodyEl.insertBefore(body, bodyEl.firstChild);
40623         return body;
40624     },
40625     /** @private */
40626     createStripElements :  function(stripEl, text, closable, tpl)
40627     {
40628         var td = document.createElement("li"); // was td..
40629         td.className = 'nav-item';
40630         
40631         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40632         
40633         
40634         stripEl.appendChild(td);
40635         /*if(closable){
40636             td.className = "x-tabs-closable";
40637             if(!this.closeTpl){
40638                 this.closeTpl = new Roo.Template(
40639                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40640                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40641                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40642                 );
40643             }
40644             var el = this.closeTpl.overwrite(td, {"text": text});
40645             var close = el.getElementsByTagName("div")[0];
40646             var inner = el.getElementsByTagName("em")[0];
40647             return {"el": el, "close": close, "inner": inner};
40648         } else {
40649         */
40650         // not sure what this is..
40651 //            if(!this.tabTpl){
40652                 //this.tabTpl = new Roo.Template(
40653                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40654                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40655                 //);
40656 //                this.tabTpl = new Roo.Template(
40657 //                   '<a href="#">' +
40658 //                   '<span unselectable="on"' +
40659 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40660 //                            ' >{text}</span></a>'
40661 //                );
40662 //                
40663 //            }
40664
40665
40666             var template = tpl || this.tabTpl || false;
40667             
40668             if(!template){
40669                 template =  new Roo.Template(
40670                         Roo.bootstrap.version == 4 ? 
40671                             (
40672                                 '<a class="nav-link" href="#" unselectable="on"' +
40673                                      (this.disableTooltips ? '' : ' title="{text}"') +
40674                                      ' >{text}</a>'
40675                             ) : (
40676                                 '<a class="nav-link" href="#">' +
40677                                 '<span unselectable="on"' +
40678                                          (this.disableTooltips ? '' : ' title="{text}"') +
40679                                     ' >{text}</span></a>'
40680                             )
40681                 );
40682             }
40683             
40684             switch (typeof(template)) {
40685                 case 'object' :
40686                     break;
40687                 case 'string' :
40688                     template = new Roo.Template(template);
40689                     break;
40690                 default :
40691                     break;
40692             }
40693             
40694             var el = template.overwrite(td, {"text": text});
40695             
40696             var inner = el.getElementsByTagName("span")[0];
40697             
40698             return {"el": el, "inner": inner};
40699             
40700     }
40701         
40702     
40703 });
40704
40705 /**
40706  * @class Roo.TabPanelItem
40707  * @extends Roo.util.Observable
40708  * Represents an individual item (tab plus body) in a TabPanel.
40709  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40710  * @param {String} id The id of this TabPanelItem
40711  * @param {String} text The text for the tab of this TabPanelItem
40712  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40713  */
40714 Roo.bootstrap.panel.TabItem = function(config){
40715     /**
40716      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40717      * @type Roo.TabPanel
40718      */
40719     this.tabPanel = config.panel;
40720     /**
40721      * The id for this TabPanelItem
40722      * @type String
40723      */
40724     this.id = config.id;
40725     /** @private */
40726     this.disabled = false;
40727     /** @private */
40728     this.text = config.text;
40729     /** @private */
40730     this.loaded = false;
40731     this.closable = config.closable;
40732
40733     /**
40734      * The body element for this TabPanelItem.
40735      * @type Roo.Element
40736      */
40737     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40738     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40739     this.bodyEl.setStyle("display", "block");
40740     this.bodyEl.setStyle("zoom", "1");
40741     //this.hideAction();
40742
40743     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40744     /** @private */
40745     this.el = Roo.get(els.el);
40746     this.inner = Roo.get(els.inner, true);
40747      this.textEl = Roo.bootstrap.version == 4 ?
40748         this.el : Roo.get(this.el.dom.firstChild, true);
40749
40750     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40751     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40752
40753     
40754 //    this.el.on("mousedown", this.onTabMouseDown, this);
40755     this.el.on("click", this.onTabClick, this);
40756     /** @private */
40757     if(config.closable){
40758         var c = Roo.get(els.close, true);
40759         c.dom.title = this.closeText;
40760         c.addClassOnOver("close-over");
40761         c.on("click", this.closeClick, this);
40762      }
40763
40764     this.addEvents({
40765          /**
40766          * @event activate
40767          * Fires when this tab becomes the active tab.
40768          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40769          * @param {Roo.TabPanelItem} this
40770          */
40771         "activate": true,
40772         /**
40773          * @event beforeclose
40774          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40775          * @param {Roo.TabPanelItem} this
40776          * @param {Object} e Set cancel to true on this object to cancel the close.
40777          */
40778         "beforeclose": true,
40779         /**
40780          * @event close
40781          * Fires when this tab is closed.
40782          * @param {Roo.TabPanelItem} this
40783          */
40784          "close": true,
40785         /**
40786          * @event deactivate
40787          * Fires when this tab is no longer the active tab.
40788          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40789          * @param {Roo.TabPanelItem} this
40790          */
40791          "deactivate" : true
40792     });
40793     this.hidden = false;
40794
40795     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40796 };
40797
40798 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40799            {
40800     purgeListeners : function(){
40801        Roo.util.Observable.prototype.purgeListeners.call(this);
40802        this.el.removeAllListeners();
40803     },
40804     /**
40805      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40806      */
40807     show : function(){
40808         this.status_node.addClass("active");
40809         this.showAction();
40810         if(Roo.isOpera){
40811             this.tabPanel.stripWrap.repaint();
40812         }
40813         this.fireEvent("activate", this.tabPanel, this);
40814     },
40815
40816     /**
40817      * Returns true if this tab is the active tab.
40818      * @return {Boolean}
40819      */
40820     isActive : function(){
40821         return this.tabPanel.getActiveTab() == this;
40822     },
40823
40824     /**
40825      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40826      */
40827     hide : function(){
40828         this.status_node.removeClass("active");
40829         this.hideAction();
40830         this.fireEvent("deactivate", this.tabPanel, this);
40831     },
40832
40833     hideAction : function(){
40834         this.bodyEl.hide();
40835         this.bodyEl.setStyle("position", "absolute");
40836         this.bodyEl.setLeft("-20000px");
40837         this.bodyEl.setTop("-20000px");
40838     },
40839
40840     showAction : function(){
40841         this.bodyEl.setStyle("position", "relative");
40842         this.bodyEl.setTop("");
40843         this.bodyEl.setLeft("");
40844         this.bodyEl.show();
40845     },
40846
40847     /**
40848      * Set the tooltip for the tab.
40849      * @param {String} tooltip The tab's tooltip
40850      */
40851     setTooltip : function(text){
40852         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40853             this.textEl.dom.qtip = text;
40854             this.textEl.dom.removeAttribute('title');
40855         }else{
40856             this.textEl.dom.title = text;
40857         }
40858     },
40859
40860     onTabClick : function(e){
40861         e.preventDefault();
40862         this.tabPanel.activate(this.id);
40863     },
40864
40865     onTabMouseDown : function(e){
40866         e.preventDefault();
40867         this.tabPanel.activate(this.id);
40868     },
40869 /*
40870     getWidth : function(){
40871         return this.inner.getWidth();
40872     },
40873
40874     setWidth : function(width){
40875         var iwidth = width - this.linode.getPadding("lr");
40876         this.inner.setWidth(iwidth);
40877         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40878         this.linode.setWidth(width);
40879     },
40880 */
40881     /**
40882      * Show or hide the tab
40883      * @param {Boolean} hidden True to hide or false to show.
40884      */
40885     setHidden : function(hidden){
40886         this.hidden = hidden;
40887         this.linode.setStyle("display", hidden ? "none" : "");
40888     },
40889
40890     /**
40891      * Returns true if this tab is "hidden"
40892      * @return {Boolean}
40893      */
40894     isHidden : function(){
40895         return this.hidden;
40896     },
40897
40898     /**
40899      * Returns the text for this tab
40900      * @return {String}
40901      */
40902     getText : function(){
40903         return this.text;
40904     },
40905     /*
40906     autoSize : function(){
40907         //this.el.beginMeasure();
40908         this.textEl.setWidth(1);
40909         /*
40910          *  #2804 [new] Tabs in Roojs
40911          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40912          */
40913         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40914         //this.el.endMeasure();
40915     //},
40916
40917     /**
40918      * Sets the text for the tab (Note: this also sets the tooltip text)
40919      * @param {String} text The tab's text and tooltip
40920      */
40921     setText : function(text){
40922         this.text = text;
40923         this.textEl.update(text);
40924         this.setTooltip(text);
40925         //if(!this.tabPanel.resizeTabs){
40926         //    this.autoSize();
40927         //}
40928     },
40929     /**
40930      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40931      */
40932     activate : function(){
40933         this.tabPanel.activate(this.id);
40934     },
40935
40936     /**
40937      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40938      */
40939     disable : function(){
40940         if(this.tabPanel.active != this){
40941             this.disabled = true;
40942             this.status_node.addClass("disabled");
40943         }
40944     },
40945
40946     /**
40947      * Enables this TabPanelItem if it was previously disabled.
40948      */
40949     enable : function(){
40950         this.disabled = false;
40951         this.status_node.removeClass("disabled");
40952     },
40953
40954     /**
40955      * Sets the content for this TabPanelItem.
40956      * @param {String} content The content
40957      * @param {Boolean} loadScripts true to look for and load scripts
40958      */
40959     setContent : function(content, loadScripts){
40960         this.bodyEl.update(content, loadScripts);
40961     },
40962
40963     /**
40964      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40965      * @return {Roo.UpdateManager} The UpdateManager
40966      */
40967     getUpdateManager : function(){
40968         return this.bodyEl.getUpdateManager();
40969     },
40970
40971     /**
40972      * Set a URL to be used to load the content for this TabPanelItem.
40973      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40974      * @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)
40975      * @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)
40976      * @return {Roo.UpdateManager} The UpdateManager
40977      */
40978     setUrl : function(url, params, loadOnce){
40979         if(this.refreshDelegate){
40980             this.un('activate', this.refreshDelegate);
40981         }
40982         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40983         this.on("activate", this.refreshDelegate);
40984         return this.bodyEl.getUpdateManager();
40985     },
40986
40987     /** @private */
40988     _handleRefresh : function(url, params, loadOnce){
40989         if(!loadOnce || !this.loaded){
40990             var updater = this.bodyEl.getUpdateManager();
40991             updater.update(url, params, this._setLoaded.createDelegate(this));
40992         }
40993     },
40994
40995     /**
40996      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40997      *   Will fail silently if the setUrl method has not been called.
40998      *   This does not activate the panel, just updates its content.
40999      */
41000     refresh : function(){
41001         if(this.refreshDelegate){
41002            this.loaded = false;
41003            this.refreshDelegate();
41004         }
41005     },
41006
41007     /** @private */
41008     _setLoaded : function(){
41009         this.loaded = true;
41010     },
41011
41012     /** @private */
41013     closeClick : function(e){
41014         var o = {};
41015         e.stopEvent();
41016         this.fireEvent("beforeclose", this, o);
41017         if(o.cancel !== true){
41018             this.tabPanel.removeTab(this.id);
41019         }
41020     },
41021     /**
41022      * The text displayed in the tooltip for the close icon.
41023      * @type String
41024      */
41025     closeText : "Close this tab"
41026 });
41027 /**
41028 *    This script refer to:
41029 *    Title: International Telephone Input
41030 *    Author: Jack O'Connor
41031 *    Code version:  v12.1.12
41032 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41033 **/
41034
41035 Roo.bootstrap.PhoneInputData = function() {
41036     var d = [
41037       [
41038         "Afghanistan (‫افغانستان‬‎)",
41039         "af",
41040         "93"
41041       ],
41042       [
41043         "Albania (Shqipëri)",
41044         "al",
41045         "355"
41046       ],
41047       [
41048         "Algeria (‫الجزائر‬‎)",
41049         "dz",
41050         "213"
41051       ],
41052       [
41053         "American Samoa",
41054         "as",
41055         "1684"
41056       ],
41057       [
41058         "Andorra",
41059         "ad",
41060         "376"
41061       ],
41062       [
41063         "Angola",
41064         "ao",
41065         "244"
41066       ],
41067       [
41068         "Anguilla",
41069         "ai",
41070         "1264"
41071       ],
41072       [
41073         "Antigua and Barbuda",
41074         "ag",
41075         "1268"
41076       ],
41077       [
41078         "Argentina",
41079         "ar",
41080         "54"
41081       ],
41082       [
41083         "Armenia (Հայաստան)",
41084         "am",
41085         "374"
41086       ],
41087       [
41088         "Aruba",
41089         "aw",
41090         "297"
41091       ],
41092       [
41093         "Australia",
41094         "au",
41095         "61",
41096         0
41097       ],
41098       [
41099         "Austria (Österreich)",
41100         "at",
41101         "43"
41102       ],
41103       [
41104         "Azerbaijan (Azərbaycan)",
41105         "az",
41106         "994"
41107       ],
41108       [
41109         "Bahamas",
41110         "bs",
41111         "1242"
41112       ],
41113       [
41114         "Bahrain (‫البحرين‬‎)",
41115         "bh",
41116         "973"
41117       ],
41118       [
41119         "Bangladesh (বাংলাদেশ)",
41120         "bd",
41121         "880"
41122       ],
41123       [
41124         "Barbados",
41125         "bb",
41126         "1246"
41127       ],
41128       [
41129         "Belarus (Беларусь)",
41130         "by",
41131         "375"
41132       ],
41133       [
41134         "Belgium (België)",
41135         "be",
41136         "32"
41137       ],
41138       [
41139         "Belize",
41140         "bz",
41141         "501"
41142       ],
41143       [
41144         "Benin (Bénin)",
41145         "bj",
41146         "229"
41147       ],
41148       [
41149         "Bermuda",
41150         "bm",
41151         "1441"
41152       ],
41153       [
41154         "Bhutan (འབྲུག)",
41155         "bt",
41156         "975"
41157       ],
41158       [
41159         "Bolivia",
41160         "bo",
41161         "591"
41162       ],
41163       [
41164         "Bosnia and Herzegovina (Босна и Херцеговина)",
41165         "ba",
41166         "387"
41167       ],
41168       [
41169         "Botswana",
41170         "bw",
41171         "267"
41172       ],
41173       [
41174         "Brazil (Brasil)",
41175         "br",
41176         "55"
41177       ],
41178       [
41179         "British Indian Ocean Territory",
41180         "io",
41181         "246"
41182       ],
41183       [
41184         "British Virgin Islands",
41185         "vg",
41186         "1284"
41187       ],
41188       [
41189         "Brunei",
41190         "bn",
41191         "673"
41192       ],
41193       [
41194         "Bulgaria (България)",
41195         "bg",
41196         "359"
41197       ],
41198       [
41199         "Burkina Faso",
41200         "bf",
41201         "226"
41202       ],
41203       [
41204         "Burundi (Uburundi)",
41205         "bi",
41206         "257"
41207       ],
41208       [
41209         "Cambodia (កម្ពុជា)",
41210         "kh",
41211         "855"
41212       ],
41213       [
41214         "Cameroon (Cameroun)",
41215         "cm",
41216         "237"
41217       ],
41218       [
41219         "Canada",
41220         "ca",
41221         "1",
41222         1,
41223         ["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"]
41224       ],
41225       [
41226         "Cape Verde (Kabu Verdi)",
41227         "cv",
41228         "238"
41229       ],
41230       [
41231         "Caribbean Netherlands",
41232         "bq",
41233         "599",
41234         1
41235       ],
41236       [
41237         "Cayman Islands",
41238         "ky",
41239         "1345"
41240       ],
41241       [
41242         "Central African Republic (République centrafricaine)",
41243         "cf",
41244         "236"
41245       ],
41246       [
41247         "Chad (Tchad)",
41248         "td",
41249         "235"
41250       ],
41251       [
41252         "Chile",
41253         "cl",
41254         "56"
41255       ],
41256       [
41257         "China (中国)",
41258         "cn",
41259         "86"
41260       ],
41261       [
41262         "Christmas Island",
41263         "cx",
41264         "61",
41265         2
41266       ],
41267       [
41268         "Cocos (Keeling) Islands",
41269         "cc",
41270         "61",
41271         1
41272       ],
41273       [
41274         "Colombia",
41275         "co",
41276         "57"
41277       ],
41278       [
41279         "Comoros (‫جزر القمر‬‎)",
41280         "km",
41281         "269"
41282       ],
41283       [
41284         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41285         "cd",
41286         "243"
41287       ],
41288       [
41289         "Congo (Republic) (Congo-Brazzaville)",
41290         "cg",
41291         "242"
41292       ],
41293       [
41294         "Cook Islands",
41295         "ck",
41296         "682"
41297       ],
41298       [
41299         "Costa Rica",
41300         "cr",
41301         "506"
41302       ],
41303       [
41304         "Côte d’Ivoire",
41305         "ci",
41306         "225"
41307       ],
41308       [
41309         "Croatia (Hrvatska)",
41310         "hr",
41311         "385"
41312       ],
41313       [
41314         "Cuba",
41315         "cu",
41316         "53"
41317       ],
41318       [
41319         "Curaçao",
41320         "cw",
41321         "599",
41322         0
41323       ],
41324       [
41325         "Cyprus (Κύπρος)",
41326         "cy",
41327         "357"
41328       ],
41329       [
41330         "Czech Republic (Česká republika)",
41331         "cz",
41332         "420"
41333       ],
41334       [
41335         "Denmark (Danmark)",
41336         "dk",
41337         "45"
41338       ],
41339       [
41340         "Djibouti",
41341         "dj",
41342         "253"
41343       ],
41344       [
41345         "Dominica",
41346         "dm",
41347         "1767"
41348       ],
41349       [
41350         "Dominican Republic (República Dominicana)",
41351         "do",
41352         "1",
41353         2,
41354         ["809", "829", "849"]
41355       ],
41356       [
41357         "Ecuador",
41358         "ec",
41359         "593"
41360       ],
41361       [
41362         "Egypt (‫مصر‬‎)",
41363         "eg",
41364         "20"
41365       ],
41366       [
41367         "El Salvador",
41368         "sv",
41369         "503"
41370       ],
41371       [
41372         "Equatorial Guinea (Guinea Ecuatorial)",
41373         "gq",
41374         "240"
41375       ],
41376       [
41377         "Eritrea",
41378         "er",
41379         "291"
41380       ],
41381       [
41382         "Estonia (Eesti)",
41383         "ee",
41384         "372"
41385       ],
41386       [
41387         "Ethiopia",
41388         "et",
41389         "251"
41390       ],
41391       [
41392         "Falkland Islands (Islas Malvinas)",
41393         "fk",
41394         "500"
41395       ],
41396       [
41397         "Faroe Islands (Føroyar)",
41398         "fo",
41399         "298"
41400       ],
41401       [
41402         "Fiji",
41403         "fj",
41404         "679"
41405       ],
41406       [
41407         "Finland (Suomi)",
41408         "fi",
41409         "358",
41410         0
41411       ],
41412       [
41413         "France",
41414         "fr",
41415         "33"
41416       ],
41417       [
41418         "French Guiana (Guyane française)",
41419         "gf",
41420         "594"
41421       ],
41422       [
41423         "French Polynesia (Polynésie française)",
41424         "pf",
41425         "689"
41426       ],
41427       [
41428         "Gabon",
41429         "ga",
41430         "241"
41431       ],
41432       [
41433         "Gambia",
41434         "gm",
41435         "220"
41436       ],
41437       [
41438         "Georgia (საქართველო)",
41439         "ge",
41440         "995"
41441       ],
41442       [
41443         "Germany (Deutschland)",
41444         "de",
41445         "49"
41446       ],
41447       [
41448         "Ghana (Gaana)",
41449         "gh",
41450         "233"
41451       ],
41452       [
41453         "Gibraltar",
41454         "gi",
41455         "350"
41456       ],
41457       [
41458         "Greece (Ελλάδα)",
41459         "gr",
41460         "30"
41461       ],
41462       [
41463         "Greenland (Kalaallit Nunaat)",
41464         "gl",
41465         "299"
41466       ],
41467       [
41468         "Grenada",
41469         "gd",
41470         "1473"
41471       ],
41472       [
41473         "Guadeloupe",
41474         "gp",
41475         "590",
41476         0
41477       ],
41478       [
41479         "Guam",
41480         "gu",
41481         "1671"
41482       ],
41483       [
41484         "Guatemala",
41485         "gt",
41486         "502"
41487       ],
41488       [
41489         "Guernsey",
41490         "gg",
41491         "44",
41492         1
41493       ],
41494       [
41495         "Guinea (Guinée)",
41496         "gn",
41497         "224"
41498       ],
41499       [
41500         "Guinea-Bissau (Guiné Bissau)",
41501         "gw",
41502         "245"
41503       ],
41504       [
41505         "Guyana",
41506         "gy",
41507         "592"
41508       ],
41509       [
41510         "Haiti",
41511         "ht",
41512         "509"
41513       ],
41514       [
41515         "Honduras",
41516         "hn",
41517         "504"
41518       ],
41519       [
41520         "Hong Kong (香港)",
41521         "hk",
41522         "852"
41523       ],
41524       [
41525         "Hungary (Magyarország)",
41526         "hu",
41527         "36"
41528       ],
41529       [
41530         "Iceland (Ísland)",
41531         "is",
41532         "354"
41533       ],
41534       [
41535         "India (भारत)",
41536         "in",
41537         "91"
41538       ],
41539       [
41540         "Indonesia",
41541         "id",
41542         "62"
41543       ],
41544       [
41545         "Iran (‫ایران‬‎)",
41546         "ir",
41547         "98"
41548       ],
41549       [
41550         "Iraq (‫العراق‬‎)",
41551         "iq",
41552         "964"
41553       ],
41554       [
41555         "Ireland",
41556         "ie",
41557         "353"
41558       ],
41559       [
41560         "Isle of Man",
41561         "im",
41562         "44",
41563         2
41564       ],
41565       [
41566         "Israel (‫ישראל‬‎)",
41567         "il",
41568         "972"
41569       ],
41570       [
41571         "Italy (Italia)",
41572         "it",
41573         "39",
41574         0
41575       ],
41576       [
41577         "Jamaica",
41578         "jm",
41579         "1876"
41580       ],
41581       [
41582         "Japan (日本)",
41583         "jp",
41584         "81"
41585       ],
41586       [
41587         "Jersey",
41588         "je",
41589         "44",
41590         3
41591       ],
41592       [
41593         "Jordan (‫الأردن‬‎)",
41594         "jo",
41595         "962"
41596       ],
41597       [
41598         "Kazakhstan (Казахстан)",
41599         "kz",
41600         "7",
41601         1
41602       ],
41603       [
41604         "Kenya",
41605         "ke",
41606         "254"
41607       ],
41608       [
41609         "Kiribati",
41610         "ki",
41611         "686"
41612       ],
41613       [
41614         "Kosovo",
41615         "xk",
41616         "383"
41617       ],
41618       [
41619         "Kuwait (‫الكويت‬‎)",
41620         "kw",
41621         "965"
41622       ],
41623       [
41624         "Kyrgyzstan (Кыргызстан)",
41625         "kg",
41626         "996"
41627       ],
41628       [
41629         "Laos (ລາວ)",
41630         "la",
41631         "856"
41632       ],
41633       [
41634         "Latvia (Latvija)",
41635         "lv",
41636         "371"
41637       ],
41638       [
41639         "Lebanon (‫لبنان‬‎)",
41640         "lb",
41641         "961"
41642       ],
41643       [
41644         "Lesotho",
41645         "ls",
41646         "266"
41647       ],
41648       [
41649         "Liberia",
41650         "lr",
41651         "231"
41652       ],
41653       [
41654         "Libya (‫ليبيا‬‎)",
41655         "ly",
41656         "218"
41657       ],
41658       [
41659         "Liechtenstein",
41660         "li",
41661         "423"
41662       ],
41663       [
41664         "Lithuania (Lietuva)",
41665         "lt",
41666         "370"
41667       ],
41668       [
41669         "Luxembourg",
41670         "lu",
41671         "352"
41672       ],
41673       [
41674         "Macau (澳門)",
41675         "mo",
41676         "853"
41677       ],
41678       [
41679         "Macedonia (FYROM) (Македонија)",
41680         "mk",
41681         "389"
41682       ],
41683       [
41684         "Madagascar (Madagasikara)",
41685         "mg",
41686         "261"
41687       ],
41688       [
41689         "Malawi",
41690         "mw",
41691         "265"
41692       ],
41693       [
41694         "Malaysia",
41695         "my",
41696         "60"
41697       ],
41698       [
41699         "Maldives",
41700         "mv",
41701         "960"
41702       ],
41703       [
41704         "Mali",
41705         "ml",
41706         "223"
41707       ],
41708       [
41709         "Malta",
41710         "mt",
41711         "356"
41712       ],
41713       [
41714         "Marshall Islands",
41715         "mh",
41716         "692"
41717       ],
41718       [
41719         "Martinique",
41720         "mq",
41721         "596"
41722       ],
41723       [
41724         "Mauritania (‫موريتانيا‬‎)",
41725         "mr",
41726         "222"
41727       ],
41728       [
41729         "Mauritius (Moris)",
41730         "mu",
41731         "230"
41732       ],
41733       [
41734         "Mayotte",
41735         "yt",
41736         "262",
41737         1
41738       ],
41739       [
41740         "Mexico (México)",
41741         "mx",
41742         "52"
41743       ],
41744       [
41745         "Micronesia",
41746         "fm",
41747         "691"
41748       ],
41749       [
41750         "Moldova (Republica Moldova)",
41751         "md",
41752         "373"
41753       ],
41754       [
41755         "Monaco",
41756         "mc",
41757         "377"
41758       ],
41759       [
41760         "Mongolia (Монгол)",
41761         "mn",
41762         "976"
41763       ],
41764       [
41765         "Montenegro (Crna Gora)",
41766         "me",
41767         "382"
41768       ],
41769       [
41770         "Montserrat",
41771         "ms",
41772         "1664"
41773       ],
41774       [
41775         "Morocco (‫المغرب‬‎)",
41776         "ma",
41777         "212",
41778         0
41779       ],
41780       [
41781         "Mozambique (Moçambique)",
41782         "mz",
41783         "258"
41784       ],
41785       [
41786         "Myanmar (Burma) (မြန်မာ)",
41787         "mm",
41788         "95"
41789       ],
41790       [
41791         "Namibia (Namibië)",
41792         "na",
41793         "264"
41794       ],
41795       [
41796         "Nauru",
41797         "nr",
41798         "674"
41799       ],
41800       [
41801         "Nepal (नेपाल)",
41802         "np",
41803         "977"
41804       ],
41805       [
41806         "Netherlands (Nederland)",
41807         "nl",
41808         "31"
41809       ],
41810       [
41811         "New Caledonia (Nouvelle-Calédonie)",
41812         "nc",
41813         "687"
41814       ],
41815       [
41816         "New Zealand",
41817         "nz",
41818         "64"
41819       ],
41820       [
41821         "Nicaragua",
41822         "ni",
41823         "505"
41824       ],
41825       [
41826         "Niger (Nijar)",
41827         "ne",
41828         "227"
41829       ],
41830       [
41831         "Nigeria",
41832         "ng",
41833         "234"
41834       ],
41835       [
41836         "Niue",
41837         "nu",
41838         "683"
41839       ],
41840       [
41841         "Norfolk Island",
41842         "nf",
41843         "672"
41844       ],
41845       [
41846         "North Korea (조선 민주주의 인민 공화국)",
41847         "kp",
41848         "850"
41849       ],
41850       [
41851         "Northern Mariana Islands",
41852         "mp",
41853         "1670"
41854       ],
41855       [
41856         "Norway (Norge)",
41857         "no",
41858         "47",
41859         0
41860       ],
41861       [
41862         "Oman (‫عُمان‬‎)",
41863         "om",
41864         "968"
41865       ],
41866       [
41867         "Pakistan (‫پاکستان‬‎)",
41868         "pk",
41869         "92"
41870       ],
41871       [
41872         "Palau",
41873         "pw",
41874         "680"
41875       ],
41876       [
41877         "Palestine (‫فلسطين‬‎)",
41878         "ps",
41879         "970"
41880       ],
41881       [
41882         "Panama (Panamá)",
41883         "pa",
41884         "507"
41885       ],
41886       [
41887         "Papua New Guinea",
41888         "pg",
41889         "675"
41890       ],
41891       [
41892         "Paraguay",
41893         "py",
41894         "595"
41895       ],
41896       [
41897         "Peru (Perú)",
41898         "pe",
41899         "51"
41900       ],
41901       [
41902         "Philippines",
41903         "ph",
41904         "63"
41905       ],
41906       [
41907         "Poland (Polska)",
41908         "pl",
41909         "48"
41910       ],
41911       [
41912         "Portugal",
41913         "pt",
41914         "351"
41915       ],
41916       [
41917         "Puerto Rico",
41918         "pr",
41919         "1",
41920         3,
41921         ["787", "939"]
41922       ],
41923       [
41924         "Qatar (‫قطر‬‎)",
41925         "qa",
41926         "974"
41927       ],
41928       [
41929         "Réunion (La Réunion)",
41930         "re",
41931         "262",
41932         0
41933       ],
41934       [
41935         "Romania (România)",
41936         "ro",
41937         "40"
41938       ],
41939       [
41940         "Russia (Россия)",
41941         "ru",
41942         "7",
41943         0
41944       ],
41945       [
41946         "Rwanda",
41947         "rw",
41948         "250"
41949       ],
41950       [
41951         "Saint Barthélemy",
41952         "bl",
41953         "590",
41954         1
41955       ],
41956       [
41957         "Saint Helena",
41958         "sh",
41959         "290"
41960       ],
41961       [
41962         "Saint Kitts and Nevis",
41963         "kn",
41964         "1869"
41965       ],
41966       [
41967         "Saint Lucia",
41968         "lc",
41969         "1758"
41970       ],
41971       [
41972         "Saint Martin (Saint-Martin (partie française))",
41973         "mf",
41974         "590",
41975         2
41976       ],
41977       [
41978         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41979         "pm",
41980         "508"
41981       ],
41982       [
41983         "Saint Vincent and the Grenadines",
41984         "vc",
41985         "1784"
41986       ],
41987       [
41988         "Samoa",
41989         "ws",
41990         "685"
41991       ],
41992       [
41993         "San Marino",
41994         "sm",
41995         "378"
41996       ],
41997       [
41998         "São Tomé and Príncipe (São Tomé e Príncipe)",
41999         "st",
42000         "239"
42001       ],
42002       [
42003         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42004         "sa",
42005         "966"
42006       ],
42007       [
42008         "Senegal (Sénégal)",
42009         "sn",
42010         "221"
42011       ],
42012       [
42013         "Serbia (Србија)",
42014         "rs",
42015         "381"
42016       ],
42017       [
42018         "Seychelles",
42019         "sc",
42020         "248"
42021       ],
42022       [
42023         "Sierra Leone",
42024         "sl",
42025         "232"
42026       ],
42027       [
42028         "Singapore",
42029         "sg",
42030         "65"
42031       ],
42032       [
42033         "Sint Maarten",
42034         "sx",
42035         "1721"
42036       ],
42037       [
42038         "Slovakia (Slovensko)",
42039         "sk",
42040         "421"
42041       ],
42042       [
42043         "Slovenia (Slovenija)",
42044         "si",
42045         "386"
42046       ],
42047       [
42048         "Solomon Islands",
42049         "sb",
42050         "677"
42051       ],
42052       [
42053         "Somalia (Soomaaliya)",
42054         "so",
42055         "252"
42056       ],
42057       [
42058         "South Africa",
42059         "za",
42060         "27"
42061       ],
42062       [
42063         "South Korea (대한민국)",
42064         "kr",
42065         "82"
42066       ],
42067       [
42068         "South Sudan (‫جنوب السودان‬‎)",
42069         "ss",
42070         "211"
42071       ],
42072       [
42073         "Spain (España)",
42074         "es",
42075         "34"
42076       ],
42077       [
42078         "Sri Lanka (ශ්‍රී ලංකාව)",
42079         "lk",
42080         "94"
42081       ],
42082       [
42083         "Sudan (‫السودان‬‎)",
42084         "sd",
42085         "249"
42086       ],
42087       [
42088         "Suriname",
42089         "sr",
42090         "597"
42091       ],
42092       [
42093         "Svalbard and Jan Mayen",
42094         "sj",
42095         "47",
42096         1
42097       ],
42098       [
42099         "Swaziland",
42100         "sz",
42101         "268"
42102       ],
42103       [
42104         "Sweden (Sverige)",
42105         "se",
42106         "46"
42107       ],
42108       [
42109         "Switzerland (Schweiz)",
42110         "ch",
42111         "41"
42112       ],
42113       [
42114         "Syria (‫سوريا‬‎)",
42115         "sy",
42116         "963"
42117       ],
42118       [
42119         "Taiwan (台灣)",
42120         "tw",
42121         "886"
42122       ],
42123       [
42124         "Tajikistan",
42125         "tj",
42126         "992"
42127       ],
42128       [
42129         "Tanzania",
42130         "tz",
42131         "255"
42132       ],
42133       [
42134         "Thailand (ไทย)",
42135         "th",
42136         "66"
42137       ],
42138       [
42139         "Timor-Leste",
42140         "tl",
42141         "670"
42142       ],
42143       [
42144         "Togo",
42145         "tg",
42146         "228"
42147       ],
42148       [
42149         "Tokelau",
42150         "tk",
42151         "690"
42152       ],
42153       [
42154         "Tonga",
42155         "to",
42156         "676"
42157       ],
42158       [
42159         "Trinidad and Tobago",
42160         "tt",
42161         "1868"
42162       ],
42163       [
42164         "Tunisia (‫تونس‬‎)",
42165         "tn",
42166         "216"
42167       ],
42168       [
42169         "Turkey (Türkiye)",
42170         "tr",
42171         "90"
42172       ],
42173       [
42174         "Turkmenistan",
42175         "tm",
42176         "993"
42177       ],
42178       [
42179         "Turks and Caicos Islands",
42180         "tc",
42181         "1649"
42182       ],
42183       [
42184         "Tuvalu",
42185         "tv",
42186         "688"
42187       ],
42188       [
42189         "U.S. Virgin Islands",
42190         "vi",
42191         "1340"
42192       ],
42193       [
42194         "Uganda",
42195         "ug",
42196         "256"
42197       ],
42198       [
42199         "Ukraine (Україна)",
42200         "ua",
42201         "380"
42202       ],
42203       [
42204         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42205         "ae",
42206         "971"
42207       ],
42208       [
42209         "United Kingdom",
42210         "gb",
42211         "44",
42212         0
42213       ],
42214       [
42215         "United States",
42216         "us",
42217         "1",
42218         0
42219       ],
42220       [
42221         "Uruguay",
42222         "uy",
42223         "598"
42224       ],
42225       [
42226         "Uzbekistan (Oʻzbekiston)",
42227         "uz",
42228         "998"
42229       ],
42230       [
42231         "Vanuatu",
42232         "vu",
42233         "678"
42234       ],
42235       [
42236         "Vatican City (Città del Vaticano)",
42237         "va",
42238         "39",
42239         1
42240       ],
42241       [
42242         "Venezuela",
42243         "ve",
42244         "58"
42245       ],
42246       [
42247         "Vietnam (Việt Nam)",
42248         "vn",
42249         "84"
42250       ],
42251       [
42252         "Wallis and Futuna (Wallis-et-Futuna)",
42253         "wf",
42254         "681"
42255       ],
42256       [
42257         "Western Sahara (‫الصحراء الغربية‬‎)",
42258         "eh",
42259         "212",
42260         1
42261       ],
42262       [
42263         "Yemen (‫اليمن‬‎)",
42264         "ye",
42265         "967"
42266       ],
42267       [
42268         "Zambia",
42269         "zm",
42270         "260"
42271       ],
42272       [
42273         "Zimbabwe",
42274         "zw",
42275         "263"
42276       ],
42277       [
42278         "Åland Islands",
42279         "ax",
42280         "358",
42281         1
42282       ]
42283   ];
42284   
42285   return d;
42286 }/**
42287 *    This script refer to:
42288 *    Title: International Telephone Input
42289 *    Author: Jack O'Connor
42290 *    Code version:  v12.1.12
42291 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42292 **/
42293
42294 /**
42295  * @class Roo.bootstrap.PhoneInput
42296  * @extends Roo.bootstrap.TriggerField
42297  * An input with International dial-code selection
42298  
42299  * @cfg {String} defaultDialCode default '+852'
42300  * @cfg {Array} preferedCountries default []
42301   
42302  * @constructor
42303  * Create a new PhoneInput.
42304  * @param {Object} config Configuration options
42305  */
42306
42307 Roo.bootstrap.PhoneInput = function(config) {
42308     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42309 };
42310
42311 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42312         
42313         listWidth: undefined,
42314         
42315         selectedClass: 'active',
42316         
42317         invalidClass : "has-warning",
42318         
42319         validClass: 'has-success',
42320         
42321         allowed: '0123456789',
42322         
42323         max_length: 15,
42324         
42325         /**
42326          * @cfg {String} defaultDialCode The default dial code when initializing the input
42327          */
42328         defaultDialCode: '+852',
42329         
42330         /**
42331          * @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
42332          */
42333         preferedCountries: false,
42334         
42335         getAutoCreate : function()
42336         {
42337             var data = Roo.bootstrap.PhoneInputData();
42338             var align = this.labelAlign || this.parentLabelAlign();
42339             var id = Roo.id();
42340             
42341             this.allCountries = [];
42342             this.dialCodeMapping = [];
42343             
42344             for (var i = 0; i < data.length; i++) {
42345               var c = data[i];
42346               this.allCountries[i] = {
42347                 name: c[0],
42348                 iso2: c[1],
42349                 dialCode: c[2],
42350                 priority: c[3] || 0,
42351                 areaCodes: c[4] || null
42352               };
42353               this.dialCodeMapping[c[2]] = {
42354                   name: c[0],
42355                   iso2: c[1],
42356                   priority: c[3] || 0,
42357                   areaCodes: c[4] || null
42358               };
42359             }
42360             
42361             var cfg = {
42362                 cls: 'form-group',
42363                 cn: []
42364             };
42365             
42366             var input =  {
42367                 tag: 'input',
42368                 id : id,
42369                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42370                 maxlength: this.max_length,
42371                 cls : 'form-control tel-input',
42372                 autocomplete: 'new-password'
42373             };
42374             
42375             var hiddenInput = {
42376                 tag: 'input',
42377                 type: 'hidden',
42378                 cls: 'hidden-tel-input'
42379             };
42380             
42381             if (this.name) {
42382                 hiddenInput.name = this.name;
42383             }
42384             
42385             if (this.disabled) {
42386                 input.disabled = true;
42387             }
42388             
42389             var flag_container = {
42390                 tag: 'div',
42391                 cls: 'flag-box',
42392                 cn: [
42393                     {
42394                         tag: 'div',
42395                         cls: 'flag'
42396                     },
42397                     {
42398                         tag: 'div',
42399                         cls: 'caret'
42400                     }
42401                 ]
42402             };
42403             
42404             var box = {
42405                 tag: 'div',
42406                 cls: this.hasFeedback ? 'has-feedback' : '',
42407                 cn: [
42408                     hiddenInput,
42409                     input,
42410                     {
42411                         tag: 'input',
42412                         cls: 'dial-code-holder',
42413                         disabled: true
42414                     }
42415                 ]
42416             };
42417             
42418             var container = {
42419                 cls: 'roo-select2-container input-group',
42420                 cn: [
42421                     flag_container,
42422                     box
42423                 ]
42424             };
42425             
42426             if (this.fieldLabel.length) {
42427                 var indicator = {
42428                     tag: 'i',
42429                     tooltip: 'This field is required'
42430                 };
42431                 
42432                 var label = {
42433                     tag: 'label',
42434                     'for':  id,
42435                     cls: 'control-label',
42436                     cn: []
42437                 };
42438                 
42439                 var label_text = {
42440                     tag: 'span',
42441                     html: this.fieldLabel
42442                 };
42443                 
42444                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42445                 label.cn = [
42446                     indicator,
42447                     label_text
42448                 ];
42449                 
42450                 if(this.indicatorpos == 'right') {
42451                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42452                     label.cn = [
42453                         label_text,
42454                         indicator
42455                     ];
42456                 }
42457                 
42458                 if(align == 'left') {
42459                     container = {
42460                         tag: 'div',
42461                         cn: [
42462                             container
42463                         ]
42464                     };
42465                     
42466                     if(this.labelWidth > 12){
42467                         label.style = "width: " + this.labelWidth + 'px';
42468                     }
42469                     if(this.labelWidth < 13 && this.labelmd == 0){
42470                         this.labelmd = this.labelWidth;
42471                     }
42472                     if(this.labellg > 0){
42473                         label.cls += ' col-lg-' + this.labellg;
42474                         input.cls += ' col-lg-' + (12 - this.labellg);
42475                     }
42476                     if(this.labelmd > 0){
42477                         label.cls += ' col-md-' + this.labelmd;
42478                         container.cls += ' col-md-' + (12 - this.labelmd);
42479                     }
42480                     if(this.labelsm > 0){
42481                         label.cls += ' col-sm-' + this.labelsm;
42482                         container.cls += ' col-sm-' + (12 - this.labelsm);
42483                     }
42484                     if(this.labelxs > 0){
42485                         label.cls += ' col-xs-' + this.labelxs;
42486                         container.cls += ' col-xs-' + (12 - this.labelxs);
42487                     }
42488                 }
42489             }
42490             
42491             cfg.cn = [
42492                 label,
42493                 container
42494             ];
42495             
42496             var settings = this;
42497             
42498             ['xs','sm','md','lg'].map(function(size){
42499                 if (settings[size]) {
42500                     cfg.cls += ' col-' + size + '-' + settings[size];
42501                 }
42502             });
42503             
42504             this.store = new Roo.data.Store({
42505                 proxy : new Roo.data.MemoryProxy({}),
42506                 reader : new Roo.data.JsonReader({
42507                     fields : [
42508                         {
42509                             'name' : 'name',
42510                             'type' : 'string'
42511                         },
42512                         {
42513                             'name' : 'iso2',
42514                             'type' : 'string'
42515                         },
42516                         {
42517                             'name' : 'dialCode',
42518                             'type' : 'string'
42519                         },
42520                         {
42521                             'name' : 'priority',
42522                             'type' : 'string'
42523                         },
42524                         {
42525                             'name' : 'areaCodes',
42526                             'type' : 'string'
42527                         }
42528                     ]
42529                 })
42530             });
42531             
42532             if(!this.preferedCountries) {
42533                 this.preferedCountries = [
42534                     'hk',
42535                     'gb',
42536                     'us'
42537                 ];
42538             }
42539             
42540             var p = this.preferedCountries.reverse();
42541             
42542             if(p) {
42543                 for (var i = 0; i < p.length; i++) {
42544                     for (var j = 0; j < this.allCountries.length; j++) {
42545                         if(this.allCountries[j].iso2 == p[i]) {
42546                             var t = this.allCountries[j];
42547                             this.allCountries.splice(j,1);
42548                             this.allCountries.unshift(t);
42549                         }
42550                     } 
42551                 }
42552             }
42553             
42554             this.store.proxy.data = {
42555                 success: true,
42556                 data: this.allCountries
42557             };
42558             
42559             return cfg;
42560         },
42561         
42562         initEvents : function()
42563         {
42564             this.createList();
42565             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42566             
42567             this.indicator = this.indicatorEl();
42568             this.flag = this.flagEl();
42569             this.dialCodeHolder = this.dialCodeHolderEl();
42570             
42571             this.trigger = this.el.select('div.flag-box',true).first();
42572             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42573             
42574             var _this = this;
42575             
42576             (function(){
42577                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42578                 _this.list.setWidth(lw);
42579             }).defer(100);
42580             
42581             this.list.on('mouseover', this.onViewOver, this);
42582             this.list.on('mousemove', this.onViewMove, this);
42583             this.inputEl().on("keyup", this.onKeyUp, this);
42584             this.inputEl().on("keypress", this.onKeyPress, this);
42585             
42586             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42587
42588             this.view = new Roo.View(this.list, this.tpl, {
42589                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42590             });
42591             
42592             this.view.on('click', this.onViewClick, this);
42593             this.setValue(this.defaultDialCode);
42594         },
42595         
42596         onTriggerClick : function(e)
42597         {
42598             Roo.log('trigger click');
42599             if(this.disabled){
42600                 return;
42601             }
42602             
42603             if(this.isExpanded()){
42604                 this.collapse();
42605                 this.hasFocus = false;
42606             }else {
42607                 this.store.load({});
42608                 this.hasFocus = true;
42609                 this.expand();
42610             }
42611         },
42612         
42613         isExpanded : function()
42614         {
42615             return this.list.isVisible();
42616         },
42617         
42618         collapse : function()
42619         {
42620             if(!this.isExpanded()){
42621                 return;
42622             }
42623             this.list.hide();
42624             Roo.get(document).un('mousedown', this.collapseIf, this);
42625             Roo.get(document).un('mousewheel', this.collapseIf, this);
42626             this.fireEvent('collapse', this);
42627             this.validate();
42628         },
42629         
42630         expand : function()
42631         {
42632             Roo.log('expand');
42633
42634             if(this.isExpanded() || !this.hasFocus){
42635                 return;
42636             }
42637             
42638             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42639             this.list.setWidth(lw);
42640             
42641             this.list.show();
42642             this.restrictHeight();
42643             
42644             Roo.get(document).on('mousedown', this.collapseIf, this);
42645             Roo.get(document).on('mousewheel', this.collapseIf, this);
42646             
42647             this.fireEvent('expand', this);
42648         },
42649         
42650         restrictHeight : function()
42651         {
42652             this.list.alignTo(this.inputEl(), this.listAlign);
42653             this.list.alignTo(this.inputEl(), this.listAlign);
42654         },
42655         
42656         onViewOver : function(e, t)
42657         {
42658             if(this.inKeyMode){
42659                 return;
42660             }
42661             var item = this.view.findItemFromChild(t);
42662             
42663             if(item){
42664                 var index = this.view.indexOf(item);
42665                 this.select(index, false);
42666             }
42667         },
42668
42669         // private
42670         onViewClick : function(view, doFocus, el, e)
42671         {
42672             var index = this.view.getSelectedIndexes()[0];
42673             
42674             var r = this.store.getAt(index);
42675             
42676             if(r){
42677                 this.onSelect(r, index);
42678             }
42679             if(doFocus !== false && !this.blockFocus){
42680                 this.inputEl().focus();
42681             }
42682         },
42683         
42684         onViewMove : function(e, t)
42685         {
42686             this.inKeyMode = false;
42687         },
42688         
42689         select : function(index, scrollIntoView)
42690         {
42691             this.selectedIndex = index;
42692             this.view.select(index);
42693             if(scrollIntoView !== false){
42694                 var el = this.view.getNode(index);
42695                 if(el){
42696                     this.list.scrollChildIntoView(el, false);
42697                 }
42698             }
42699         },
42700         
42701         createList : function()
42702         {
42703             this.list = Roo.get(document.body).createChild({
42704                 tag: 'ul',
42705                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42706                 style: 'display:none'
42707             });
42708             
42709             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42710         },
42711         
42712         collapseIf : function(e)
42713         {
42714             var in_combo  = e.within(this.el);
42715             var in_list =  e.within(this.list);
42716             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42717             
42718             if (in_combo || in_list || is_list) {
42719                 return;
42720             }
42721             this.collapse();
42722         },
42723         
42724         onSelect : function(record, index)
42725         {
42726             if(this.fireEvent('beforeselect', this, record, index) !== false){
42727                 
42728                 this.setFlagClass(record.data.iso2);
42729                 this.setDialCode(record.data.dialCode);
42730                 this.hasFocus = false;
42731                 this.collapse();
42732                 this.fireEvent('select', this, record, index);
42733             }
42734         },
42735         
42736         flagEl : function()
42737         {
42738             var flag = this.el.select('div.flag',true).first();
42739             if(!flag){
42740                 return false;
42741             }
42742             return flag;
42743         },
42744         
42745         dialCodeHolderEl : function()
42746         {
42747             var d = this.el.select('input.dial-code-holder',true).first();
42748             if(!d){
42749                 return false;
42750             }
42751             return d;
42752         },
42753         
42754         setDialCode : function(v)
42755         {
42756             this.dialCodeHolder.dom.value = '+'+v;
42757         },
42758         
42759         setFlagClass : function(n)
42760         {
42761             this.flag.dom.className = 'flag '+n;
42762         },
42763         
42764         getValue : function()
42765         {
42766             var v = this.inputEl().getValue();
42767             if(this.dialCodeHolder) {
42768                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42769             }
42770             return v;
42771         },
42772         
42773         setValue : function(v)
42774         {
42775             var d = this.getDialCode(v);
42776             
42777             //invalid dial code
42778             if(v.length == 0 || !d || d.length == 0) {
42779                 if(this.rendered){
42780                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42781                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42782                 }
42783                 return;
42784             }
42785             
42786             //valid dial code
42787             this.setFlagClass(this.dialCodeMapping[d].iso2);
42788             this.setDialCode(d);
42789             this.inputEl().dom.value = v.replace('+'+d,'');
42790             this.hiddenEl().dom.value = this.getValue();
42791             
42792             this.validate();
42793         },
42794         
42795         getDialCode : function(v)
42796         {
42797             v = v ||  '';
42798             
42799             if (v.length == 0) {
42800                 return this.dialCodeHolder.dom.value;
42801             }
42802             
42803             var dialCode = "";
42804             if (v.charAt(0) != "+") {
42805                 return false;
42806             }
42807             var numericChars = "";
42808             for (var i = 1; i < v.length; i++) {
42809               var c = v.charAt(i);
42810               if (!isNaN(c)) {
42811                 numericChars += c;
42812                 if (this.dialCodeMapping[numericChars]) {
42813                   dialCode = v.substr(1, i);
42814                 }
42815                 if (numericChars.length == 4) {
42816                   break;
42817                 }
42818               }
42819             }
42820             return dialCode;
42821         },
42822         
42823         reset : function()
42824         {
42825             this.setValue(this.defaultDialCode);
42826             this.validate();
42827         },
42828         
42829         hiddenEl : function()
42830         {
42831             return this.el.select('input.hidden-tel-input',true).first();
42832         },
42833         
42834         // after setting val
42835         onKeyUp : function(e){
42836             this.setValue(this.getValue());
42837         },
42838         
42839         onKeyPress : function(e){
42840             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42841                 e.stopEvent();
42842             }
42843         }
42844         
42845 });
42846 /**
42847  * @class Roo.bootstrap.MoneyField
42848  * @extends Roo.bootstrap.ComboBox
42849  * Bootstrap MoneyField class
42850  * 
42851  * @constructor
42852  * Create a new MoneyField.
42853  * @param {Object} config Configuration options
42854  */
42855
42856 Roo.bootstrap.MoneyField = function(config) {
42857     
42858     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42859     
42860 };
42861
42862 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42863     
42864     /**
42865      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42866      */
42867     allowDecimals : true,
42868     /**
42869      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42870      */
42871     decimalSeparator : ".",
42872     /**
42873      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42874      */
42875     decimalPrecision : 0,
42876     /**
42877      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42878      */
42879     allowNegative : true,
42880     /**
42881      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42882      */
42883     allowZero: true,
42884     /**
42885      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42886      */
42887     minValue : Number.NEGATIVE_INFINITY,
42888     /**
42889      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42890      */
42891     maxValue : Number.MAX_VALUE,
42892     /**
42893      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42894      */
42895     minText : "The minimum value for this field is {0}",
42896     /**
42897      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42898      */
42899     maxText : "The maximum value for this field is {0}",
42900     /**
42901      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42902      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42903      */
42904     nanText : "{0} is not a valid number",
42905     /**
42906      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42907      */
42908     castInt : true,
42909     /**
42910      * @cfg {String} defaults currency of the MoneyField
42911      * value should be in lkey
42912      */
42913     defaultCurrency : false,
42914     /**
42915      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42916      */
42917     thousandsDelimiter : false,
42918     /**
42919      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42920      */
42921     max_length: false,
42922     
42923     inputlg : 9,
42924     inputmd : 9,
42925     inputsm : 9,
42926     inputxs : 6,
42927     
42928     store : false,
42929     
42930     getAutoCreate : function()
42931     {
42932         var align = this.labelAlign || this.parentLabelAlign();
42933         
42934         var id = Roo.id();
42935
42936         var cfg = {
42937             cls: 'form-group',
42938             cn: []
42939         };
42940
42941         var input =  {
42942             tag: 'input',
42943             id : id,
42944             cls : 'form-control roo-money-amount-input',
42945             autocomplete: 'new-password'
42946         };
42947         
42948         var hiddenInput = {
42949             tag: 'input',
42950             type: 'hidden',
42951             id: Roo.id(),
42952             cls: 'hidden-number-input'
42953         };
42954         
42955         if(this.max_length) {
42956             input.maxlength = this.max_length; 
42957         }
42958         
42959         if (this.name) {
42960             hiddenInput.name = this.name;
42961         }
42962
42963         if (this.disabled) {
42964             input.disabled = true;
42965         }
42966
42967         var clg = 12 - this.inputlg;
42968         var cmd = 12 - this.inputmd;
42969         var csm = 12 - this.inputsm;
42970         var cxs = 12 - this.inputxs;
42971         
42972         var container = {
42973             tag : 'div',
42974             cls : 'row roo-money-field',
42975             cn : [
42976                 {
42977                     tag : 'div',
42978                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42979                     cn : [
42980                         {
42981                             tag : 'div',
42982                             cls: 'roo-select2-container input-group',
42983                             cn: [
42984                                 {
42985                                     tag : 'input',
42986                                     cls : 'form-control roo-money-currency-input',
42987                                     autocomplete: 'new-password',
42988                                     readOnly : 1,
42989                                     name : this.currencyName
42990                                 },
42991                                 {
42992                                     tag :'span',
42993                                     cls : 'input-group-addon',
42994                                     cn : [
42995                                         {
42996                                             tag: 'span',
42997                                             cls: 'caret'
42998                                         }
42999                                     ]
43000                                 }
43001                             ]
43002                         }
43003                     ]
43004                 },
43005                 {
43006                     tag : 'div',
43007                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43008                     cn : [
43009                         {
43010                             tag: 'div',
43011                             cls: this.hasFeedback ? 'has-feedback' : '',
43012                             cn: [
43013                                 input
43014                             ]
43015                         }
43016                     ]
43017                 }
43018             ]
43019             
43020         };
43021         
43022         if (this.fieldLabel.length) {
43023             var indicator = {
43024                 tag: 'i',
43025                 tooltip: 'This field is required'
43026             };
43027
43028             var label = {
43029                 tag: 'label',
43030                 'for':  id,
43031                 cls: 'control-label',
43032                 cn: []
43033             };
43034
43035             var label_text = {
43036                 tag: 'span',
43037                 html: this.fieldLabel
43038             };
43039
43040             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43041             label.cn = [
43042                 indicator,
43043                 label_text
43044             ];
43045
43046             if(this.indicatorpos == 'right') {
43047                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43048                 label.cn = [
43049                     label_text,
43050                     indicator
43051                 ];
43052             }
43053
43054             if(align == 'left') {
43055                 container = {
43056                     tag: 'div',
43057                     cn: [
43058                         container
43059                     ]
43060                 };
43061
43062                 if(this.labelWidth > 12){
43063                     label.style = "width: " + this.labelWidth + 'px';
43064                 }
43065                 if(this.labelWidth < 13 && this.labelmd == 0){
43066                     this.labelmd = this.labelWidth;
43067                 }
43068                 if(this.labellg > 0){
43069                     label.cls += ' col-lg-' + this.labellg;
43070                     input.cls += ' col-lg-' + (12 - this.labellg);
43071                 }
43072                 if(this.labelmd > 0){
43073                     label.cls += ' col-md-' + this.labelmd;
43074                     container.cls += ' col-md-' + (12 - this.labelmd);
43075                 }
43076                 if(this.labelsm > 0){
43077                     label.cls += ' col-sm-' + this.labelsm;
43078                     container.cls += ' col-sm-' + (12 - this.labelsm);
43079                 }
43080                 if(this.labelxs > 0){
43081                     label.cls += ' col-xs-' + this.labelxs;
43082                     container.cls += ' col-xs-' + (12 - this.labelxs);
43083                 }
43084             }
43085         }
43086
43087         cfg.cn = [
43088             label,
43089             container,
43090             hiddenInput
43091         ];
43092         
43093         var settings = this;
43094
43095         ['xs','sm','md','lg'].map(function(size){
43096             if (settings[size]) {
43097                 cfg.cls += ' col-' + size + '-' + settings[size];
43098             }
43099         });
43100         
43101         return cfg;
43102     },
43103     
43104     initEvents : function()
43105     {
43106         this.indicator = this.indicatorEl();
43107         
43108         this.initCurrencyEvent();
43109         
43110         this.initNumberEvent();
43111     },
43112     
43113     initCurrencyEvent : function()
43114     {
43115         if (!this.store) {
43116             throw "can not find store for combo";
43117         }
43118         
43119         this.store = Roo.factory(this.store, Roo.data);
43120         this.store.parent = this;
43121         
43122         this.createList();
43123         
43124         this.triggerEl = this.el.select('.input-group-addon', true).first();
43125         
43126         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43127         
43128         var _this = this;
43129         
43130         (function(){
43131             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43132             _this.list.setWidth(lw);
43133         }).defer(100);
43134         
43135         this.list.on('mouseover', this.onViewOver, this);
43136         this.list.on('mousemove', this.onViewMove, this);
43137         this.list.on('scroll', this.onViewScroll, this);
43138         
43139         if(!this.tpl){
43140             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43141         }
43142         
43143         this.view = new Roo.View(this.list, this.tpl, {
43144             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43145         });
43146         
43147         this.view.on('click', this.onViewClick, this);
43148         
43149         this.store.on('beforeload', this.onBeforeLoad, this);
43150         this.store.on('load', this.onLoad, this);
43151         this.store.on('loadexception', this.onLoadException, this);
43152         
43153         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43154             "up" : function(e){
43155                 this.inKeyMode = true;
43156                 this.selectPrev();
43157             },
43158
43159             "down" : function(e){
43160                 if(!this.isExpanded()){
43161                     this.onTriggerClick();
43162                 }else{
43163                     this.inKeyMode = true;
43164                     this.selectNext();
43165                 }
43166             },
43167
43168             "enter" : function(e){
43169                 this.collapse();
43170                 
43171                 if(this.fireEvent("specialkey", this, e)){
43172                     this.onViewClick(false);
43173                 }
43174                 
43175                 return true;
43176             },
43177
43178             "esc" : function(e){
43179                 this.collapse();
43180             },
43181
43182             "tab" : function(e){
43183                 this.collapse();
43184                 
43185                 if(this.fireEvent("specialkey", this, e)){
43186                     this.onViewClick(false);
43187                 }
43188                 
43189                 return true;
43190             },
43191
43192             scope : this,
43193
43194             doRelay : function(foo, bar, hname){
43195                 if(hname == 'down' || this.scope.isExpanded()){
43196                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43197                 }
43198                 return true;
43199             },
43200
43201             forceKeyDown: true
43202         });
43203         
43204         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43205         
43206     },
43207     
43208     initNumberEvent : function(e)
43209     {
43210         this.inputEl().on("keydown" , this.fireKey,  this);
43211         this.inputEl().on("focus", this.onFocus,  this);
43212         this.inputEl().on("blur", this.onBlur,  this);
43213         
43214         this.inputEl().relayEvent('keyup', this);
43215         
43216         if(this.indicator){
43217             this.indicator.addClass('invisible');
43218         }
43219  
43220         this.originalValue = this.getValue();
43221         
43222         if(this.validationEvent == 'keyup'){
43223             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43224             this.inputEl().on('keyup', this.filterValidation, this);
43225         }
43226         else if(this.validationEvent !== false){
43227             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43228         }
43229         
43230         if(this.selectOnFocus){
43231             this.on("focus", this.preFocus, this);
43232             
43233         }
43234         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43235             this.inputEl().on("keypress", this.filterKeys, this);
43236         } else {
43237             this.inputEl().relayEvent('keypress', this);
43238         }
43239         
43240         var allowed = "0123456789";
43241         
43242         if(this.allowDecimals){
43243             allowed += this.decimalSeparator;
43244         }
43245         
43246         if(this.allowNegative){
43247             allowed += "-";
43248         }
43249         
43250         if(this.thousandsDelimiter) {
43251             allowed += ",";
43252         }
43253         
43254         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43255         
43256         var keyPress = function(e){
43257             
43258             var k = e.getKey();
43259             
43260             var c = e.getCharCode();
43261             
43262             if(
43263                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43264                     allowed.indexOf(String.fromCharCode(c)) === -1
43265             ){
43266                 e.stopEvent();
43267                 return;
43268             }
43269             
43270             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43271                 return;
43272             }
43273             
43274             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43275                 e.stopEvent();
43276             }
43277         };
43278         
43279         this.inputEl().on("keypress", keyPress, this);
43280         
43281     },
43282     
43283     onTriggerClick : function(e)
43284     {   
43285         if(this.disabled){
43286             return;
43287         }
43288         
43289         this.page = 0;
43290         this.loadNext = false;
43291         
43292         if(this.isExpanded()){
43293             this.collapse();
43294             return;
43295         }
43296         
43297         this.hasFocus = true;
43298         
43299         if(this.triggerAction == 'all') {
43300             this.doQuery(this.allQuery, true);
43301             return;
43302         }
43303         
43304         this.doQuery(this.getRawValue());
43305     },
43306     
43307     getCurrency : function()
43308     {   
43309         var v = this.currencyEl().getValue();
43310         
43311         return v;
43312     },
43313     
43314     restrictHeight : function()
43315     {
43316         this.list.alignTo(this.currencyEl(), this.listAlign);
43317         this.list.alignTo(this.currencyEl(), this.listAlign);
43318     },
43319     
43320     onViewClick : function(view, doFocus, el, e)
43321     {
43322         var index = this.view.getSelectedIndexes()[0];
43323         
43324         var r = this.store.getAt(index);
43325         
43326         if(r){
43327             this.onSelect(r, index);
43328         }
43329     },
43330     
43331     onSelect : function(record, index){
43332         
43333         if(this.fireEvent('beforeselect', this, record, index) !== false){
43334         
43335             this.setFromCurrencyData(index > -1 ? record.data : false);
43336             
43337             this.collapse();
43338             
43339             this.fireEvent('select', this, record, index);
43340         }
43341     },
43342     
43343     setFromCurrencyData : function(o)
43344     {
43345         var currency = '';
43346         
43347         this.lastCurrency = o;
43348         
43349         if (this.currencyField) {
43350             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43351         } else {
43352             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43353         }
43354         
43355         this.lastSelectionText = currency;
43356         
43357         //setting default currency
43358         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43359             this.setCurrency(this.defaultCurrency);
43360             return;
43361         }
43362         
43363         this.setCurrency(currency);
43364     },
43365     
43366     setFromData : function(o)
43367     {
43368         var c = {};
43369         
43370         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43371         
43372         this.setFromCurrencyData(c);
43373         
43374         var value = '';
43375         
43376         if (this.name) {
43377             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43378         } else {
43379             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43380         }
43381         
43382         this.setValue(value);
43383         
43384     },
43385     
43386     setCurrency : function(v)
43387     {   
43388         this.currencyValue = v;
43389         
43390         if(this.rendered){
43391             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43392             this.validate();
43393         }
43394     },
43395     
43396     setValue : function(v)
43397     {
43398         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43399         
43400         this.value = v;
43401         
43402         if(this.rendered){
43403             
43404             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43405             
43406             this.inputEl().dom.value = (v == '') ? '' :
43407                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43408             
43409             if(!this.allowZero && v === '0') {
43410                 this.hiddenEl().dom.value = '';
43411                 this.inputEl().dom.value = '';
43412             }
43413             
43414             this.validate();
43415         }
43416     },
43417     
43418     getRawValue : function()
43419     {
43420         var v = this.inputEl().getValue();
43421         
43422         return v;
43423     },
43424     
43425     getValue : function()
43426     {
43427         return this.fixPrecision(this.parseValue(this.getRawValue()));
43428     },
43429     
43430     parseValue : function(value)
43431     {
43432         if(this.thousandsDelimiter) {
43433             value += "";
43434             r = new RegExp(",", "g");
43435             value = value.replace(r, "");
43436         }
43437         
43438         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43439         return isNaN(value) ? '' : value;
43440         
43441     },
43442     
43443     fixPrecision : function(value)
43444     {
43445         if(this.thousandsDelimiter) {
43446             value += "";
43447             r = new RegExp(",", "g");
43448             value = value.replace(r, "");
43449         }
43450         
43451         var nan = isNaN(value);
43452         
43453         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43454             return nan ? '' : value;
43455         }
43456         return parseFloat(value).toFixed(this.decimalPrecision);
43457     },
43458     
43459     decimalPrecisionFcn : function(v)
43460     {
43461         return Math.floor(v);
43462     },
43463     
43464     validateValue : function(value)
43465     {
43466         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43467             return false;
43468         }
43469         
43470         var num = this.parseValue(value);
43471         
43472         if(isNaN(num)){
43473             this.markInvalid(String.format(this.nanText, value));
43474             return false;
43475         }
43476         
43477         if(num < this.minValue){
43478             this.markInvalid(String.format(this.minText, this.minValue));
43479             return false;
43480         }
43481         
43482         if(num > this.maxValue){
43483             this.markInvalid(String.format(this.maxText, this.maxValue));
43484             return false;
43485         }
43486         
43487         return true;
43488     },
43489     
43490     validate : function()
43491     {
43492         if(this.disabled || this.allowBlank){
43493             this.markValid();
43494             return true;
43495         }
43496         
43497         var currency = this.getCurrency();
43498         
43499         if(this.validateValue(this.getRawValue()) && currency.length){
43500             this.markValid();
43501             return true;
43502         }
43503         
43504         this.markInvalid();
43505         return false;
43506     },
43507     
43508     getName: function()
43509     {
43510         return this.name;
43511     },
43512     
43513     beforeBlur : function()
43514     {
43515         if(!this.castInt){
43516             return;
43517         }
43518         
43519         var v = this.parseValue(this.getRawValue());
43520         
43521         if(v || v == 0){
43522             this.setValue(v);
43523         }
43524     },
43525     
43526     onBlur : function()
43527     {
43528         this.beforeBlur();
43529         
43530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43531             //this.el.removeClass(this.focusClass);
43532         }
43533         
43534         this.hasFocus = false;
43535         
43536         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43537             this.validate();
43538         }
43539         
43540         var v = this.getValue();
43541         
43542         if(String(v) !== String(this.startValue)){
43543             this.fireEvent('change', this, v, this.startValue);
43544         }
43545         
43546         this.fireEvent("blur", this);
43547     },
43548     
43549     inputEl : function()
43550     {
43551         return this.el.select('.roo-money-amount-input', true).first();
43552     },
43553     
43554     currencyEl : function()
43555     {
43556         return this.el.select('.roo-money-currency-input', true).first();
43557     },
43558     
43559     hiddenEl : function()
43560     {
43561         return this.el.select('input.hidden-number-input',true).first();
43562     }
43563     
43564 });/**
43565  * @class Roo.bootstrap.BezierSignature
43566  * @extends Roo.bootstrap.Component
43567  * Bootstrap BezierSignature class
43568  * This script refer to:
43569  *    Title: Signature Pad
43570  *    Author: szimek
43571  *    Availability: https://github.com/szimek/signature_pad
43572  *
43573  * @constructor
43574  * Create a new BezierSignature
43575  * @param {Object} config The config object
43576  */
43577
43578 Roo.bootstrap.BezierSignature = function(config){
43579     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43580     this.addEvents({
43581         "resize" : true
43582     });
43583 };
43584
43585 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43586 {
43587      
43588     curve_data: [],
43589     
43590     is_empty: true,
43591     
43592     mouse_btn_down: true,
43593     
43594     /**
43595      * @cfg {int} canvas height
43596      */
43597     canvas_height: '200px',
43598     
43599     /**
43600      * @cfg {float|function} Radius of a single dot.
43601      */ 
43602     dot_size: false,
43603     
43604     /**
43605      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43606      */
43607     min_width: 0.5,
43608     
43609     /**
43610      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43611      */
43612     max_width: 2.5,
43613     
43614     /**
43615      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43616      */
43617     throttle: 16,
43618     
43619     /**
43620      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43621      */
43622     min_distance: 5,
43623     
43624     /**
43625      * @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.
43626      */
43627     bg_color: 'rgba(0, 0, 0, 0)',
43628     
43629     /**
43630      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43631      */
43632     dot_color: 'black',
43633     
43634     /**
43635      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43636      */ 
43637     velocity_filter_weight: 0.7,
43638     
43639     /**
43640      * @cfg {function} Callback when stroke begin. 
43641      */
43642     onBegin: false,
43643     
43644     /**
43645      * @cfg {function} Callback when stroke end.
43646      */
43647     onEnd: false,
43648     
43649     getAutoCreate : function()
43650     {
43651         var cls = 'roo-signature column';
43652         
43653         if(this.cls){
43654             cls += ' ' + this.cls;
43655         }
43656         
43657         var col_sizes = [
43658             'lg',
43659             'md',
43660             'sm',
43661             'xs'
43662         ];
43663         
43664         for(var i = 0; i < col_sizes.length; i++) {
43665             if(this[col_sizes[i]]) {
43666                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43667             }
43668         }
43669         
43670         var cfg = {
43671             tag: 'div',
43672             cls: cls,
43673             cn: [
43674                 {
43675                     tag: 'div',
43676                     cls: 'roo-signature-body',
43677                     cn: [
43678                         {
43679                             tag: 'canvas',
43680                             cls: 'roo-signature-body-canvas',
43681                             height: this.canvas_height,
43682                             width: this.canvas_width
43683                         }
43684                     ]
43685                 },
43686                 {
43687                     tag: 'input',
43688                     type: 'file',
43689                     style: 'display: none'
43690                 }
43691             ]
43692         };
43693         
43694         return cfg;
43695     },
43696     
43697     initEvents: function() 
43698     {
43699         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43700         
43701         var canvas = this.canvasEl();
43702         
43703         // mouse && touch event swapping...
43704         canvas.dom.style.touchAction = 'none';
43705         canvas.dom.style.msTouchAction = 'none';
43706         
43707         this.mouse_btn_down = false;
43708         canvas.on('mousedown', this._handleMouseDown, this);
43709         canvas.on('mousemove', this._handleMouseMove, this);
43710         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43711         
43712         if (window.PointerEvent) {
43713             canvas.on('pointerdown', this._handleMouseDown, this);
43714             canvas.on('pointermove', this._handleMouseMove, this);
43715             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43716         }
43717         
43718         if ('ontouchstart' in window) {
43719             canvas.on('touchstart', this._handleTouchStart, this);
43720             canvas.on('touchmove', this._handleTouchMove, this);
43721             canvas.on('touchend', this._handleTouchEnd, this);
43722         }
43723         
43724         Roo.EventManager.onWindowResize(this.resize, this, true);
43725         
43726         // file input event
43727         this.fileEl().on('change', this.uploadImage, this);
43728         
43729         this.clear();
43730         
43731         this.resize();
43732     },
43733     
43734     resize: function(){
43735         
43736         var canvas = this.canvasEl().dom;
43737         var ctx = this.canvasElCtx();
43738         var img_data = false;
43739         
43740         if(canvas.width > 0) {
43741             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43742         }
43743         // setting canvas width will clean img data
43744         canvas.width = 0;
43745         
43746         var style = window.getComputedStyle ? 
43747             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43748             
43749         var padding_left = parseInt(style.paddingLeft) || 0;
43750         var padding_right = parseInt(style.paddingRight) || 0;
43751         
43752         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43753         
43754         if(img_data) {
43755             ctx.putImageData(img_data, 0, 0);
43756         }
43757     },
43758     
43759     _handleMouseDown: function(e)
43760     {
43761         if (e.browserEvent.which === 1) {
43762             this.mouse_btn_down = true;
43763             this.strokeBegin(e);
43764         }
43765     },
43766     
43767     _handleMouseMove: function (e)
43768     {
43769         if (this.mouse_btn_down) {
43770             this.strokeMoveUpdate(e);
43771         }
43772     },
43773     
43774     _handleMouseUp: function (e)
43775     {
43776         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43777             this.mouse_btn_down = false;
43778             this.strokeEnd(e);
43779         }
43780     },
43781     
43782     _handleTouchStart: function (e) {
43783         
43784         e.preventDefault();
43785         if (e.browserEvent.targetTouches.length === 1) {
43786             // var touch = e.browserEvent.changedTouches[0];
43787             // this.strokeBegin(touch);
43788             
43789              this.strokeBegin(e); // assume e catching the correct xy...
43790         }
43791     },
43792     
43793     _handleTouchMove: function (e) {
43794         e.preventDefault();
43795         // var touch = event.targetTouches[0];
43796         // _this._strokeMoveUpdate(touch);
43797         this.strokeMoveUpdate(e);
43798     },
43799     
43800     _handleTouchEnd: function (e) {
43801         var wasCanvasTouched = e.target === this.canvasEl().dom;
43802         if (wasCanvasTouched) {
43803             e.preventDefault();
43804             // var touch = event.changedTouches[0];
43805             // _this._strokeEnd(touch);
43806             this.strokeEnd(e);
43807         }
43808     },
43809     
43810     reset: function () {
43811         this._lastPoints = [];
43812         this._lastVelocity = 0;
43813         this._lastWidth = (this.min_width + this.max_width) / 2;
43814         this.canvasElCtx().fillStyle = this.dot_color;
43815     },
43816     
43817     strokeMoveUpdate: function(e)
43818     {
43819         this.strokeUpdate(e);
43820         
43821         if (this.throttle) {
43822             this.throttleStroke(this.strokeUpdate, this.throttle);
43823         }
43824         else {
43825             this.strokeUpdate(e);
43826         }
43827     },
43828     
43829     strokeBegin: function(e)
43830     {
43831         var newPointGroup = {
43832             color: this.dot_color,
43833             points: []
43834         };
43835         
43836         if (typeof this.onBegin === 'function') {
43837             this.onBegin(e);
43838         }
43839         
43840         this.curve_data.push(newPointGroup);
43841         this.reset();
43842         this.strokeUpdate(e);
43843     },
43844     
43845     strokeUpdate: function(e)
43846     {
43847         var rect = this.canvasEl().dom.getBoundingClientRect();
43848         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43849         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43850         var lastPoints = lastPointGroup.points;
43851         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43852         var isLastPointTooClose = lastPoint
43853             ? point.distanceTo(lastPoint) <= this.min_distance
43854             : false;
43855         var color = lastPointGroup.color;
43856         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43857             var curve = this.addPoint(point);
43858             if (!lastPoint) {
43859                 this.drawDot({color: color, point: point});
43860             }
43861             else if (curve) {
43862                 this.drawCurve({color: color, curve: curve});
43863             }
43864             lastPoints.push({
43865                 time: point.time,
43866                 x: point.x,
43867                 y: point.y
43868             });
43869         }
43870     },
43871     
43872     strokeEnd: function(e)
43873     {
43874         this.strokeUpdate(e);
43875         if (typeof this.onEnd === 'function') {
43876             this.onEnd(e);
43877         }
43878     },
43879     
43880     addPoint:  function (point) {
43881         var _lastPoints = this._lastPoints;
43882         _lastPoints.push(point);
43883         if (_lastPoints.length > 2) {
43884             if (_lastPoints.length === 3) {
43885                 _lastPoints.unshift(_lastPoints[0]);
43886             }
43887             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43888             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43889             _lastPoints.shift();
43890             return curve;
43891         }
43892         return null;
43893     },
43894     
43895     calculateCurveWidths: function (startPoint, endPoint) {
43896         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43897             (1 - this.velocity_filter_weight) * this._lastVelocity;
43898
43899         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43900         var widths = {
43901             end: newWidth,
43902             start: this._lastWidth
43903         };
43904         
43905         this._lastVelocity = velocity;
43906         this._lastWidth = newWidth;
43907         return widths;
43908     },
43909     
43910     drawDot: function (_a) {
43911         var color = _a.color, point = _a.point;
43912         var ctx = this.canvasElCtx();
43913         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43914         ctx.beginPath();
43915         this.drawCurveSegment(point.x, point.y, width);
43916         ctx.closePath();
43917         ctx.fillStyle = color;
43918         ctx.fill();
43919     },
43920     
43921     drawCurve: function (_a) {
43922         var color = _a.color, curve = _a.curve;
43923         var ctx = this.canvasElCtx();
43924         var widthDelta = curve.endWidth - curve.startWidth;
43925         var drawSteps = Math.floor(curve.length()) * 2;
43926         ctx.beginPath();
43927         ctx.fillStyle = color;
43928         for (var i = 0; i < drawSteps; i += 1) {
43929         var t = i / drawSteps;
43930         var tt = t * t;
43931         var ttt = tt * t;
43932         var u = 1 - t;
43933         var uu = u * u;
43934         var uuu = uu * u;
43935         var x = uuu * curve.startPoint.x;
43936         x += 3 * uu * t * curve.control1.x;
43937         x += 3 * u * tt * curve.control2.x;
43938         x += ttt * curve.endPoint.x;
43939         var y = uuu * curve.startPoint.y;
43940         y += 3 * uu * t * curve.control1.y;
43941         y += 3 * u * tt * curve.control2.y;
43942         y += ttt * curve.endPoint.y;
43943         var width = curve.startWidth + ttt * widthDelta;
43944         this.drawCurveSegment(x, y, width);
43945         }
43946         ctx.closePath();
43947         ctx.fill();
43948     },
43949     
43950     drawCurveSegment: function (x, y, width) {
43951         var ctx = this.canvasElCtx();
43952         ctx.moveTo(x, y);
43953         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43954         this.is_empty = false;
43955     },
43956     
43957     clear: function()
43958     {
43959         var ctx = this.canvasElCtx();
43960         var canvas = this.canvasEl().dom;
43961         ctx.fillStyle = this.bg_color;
43962         ctx.clearRect(0, 0, canvas.width, canvas.height);
43963         ctx.fillRect(0, 0, canvas.width, canvas.height);
43964         this.curve_data = [];
43965         this.reset();
43966         this.is_empty = true;
43967     },
43968     
43969     fileEl: function()
43970     {
43971         return  this.el.select('input',true).first();
43972     },
43973     
43974     canvasEl: function()
43975     {
43976         return this.el.select('canvas',true).first();
43977     },
43978     
43979     canvasElCtx: function()
43980     {
43981         return this.el.select('canvas',true).first().dom.getContext('2d');
43982     },
43983     
43984     getImage: function(type)
43985     {
43986         if(this.is_empty) {
43987             return false;
43988         }
43989         
43990         // encryption ?
43991         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43992     },
43993     
43994     drawFromImage: function(img_src)
43995     {
43996         var img = new Image();
43997         
43998         img.onload = function(){
43999             this.canvasElCtx().drawImage(img, 0, 0);
44000         }.bind(this);
44001         
44002         img.src = img_src;
44003         
44004         this.is_empty = false;
44005     },
44006     
44007     selectImage: function()
44008     {
44009         this.fileEl().dom.click();
44010     },
44011     
44012     uploadImage: function(e)
44013     {
44014         var reader = new FileReader();
44015         
44016         reader.onload = function(e){
44017             var img = new Image();
44018             img.onload = function(){
44019                 this.reset();
44020                 this.canvasElCtx().drawImage(img, 0, 0);
44021             }.bind(this);
44022             img.src = e.target.result;
44023         }.bind(this);
44024         
44025         reader.readAsDataURL(e.target.files[0]);
44026     },
44027     
44028     // Bezier Point Constructor
44029     Point: (function () {
44030         function Point(x, y, time) {
44031             this.x = x;
44032             this.y = y;
44033             this.time = time || Date.now();
44034         }
44035         Point.prototype.distanceTo = function (start) {
44036             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44037         };
44038         Point.prototype.equals = function (other) {
44039             return this.x === other.x && this.y === other.y && this.time === other.time;
44040         };
44041         Point.prototype.velocityFrom = function (start) {
44042             return this.time !== start.time
44043             ? this.distanceTo(start) / (this.time - start.time)
44044             : 0;
44045         };
44046         return Point;
44047     }()),
44048     
44049     
44050     // Bezier Constructor
44051     Bezier: (function () {
44052         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44053             this.startPoint = startPoint;
44054             this.control2 = control2;
44055             this.control1 = control1;
44056             this.endPoint = endPoint;
44057             this.startWidth = startWidth;
44058             this.endWidth = endWidth;
44059         }
44060         Bezier.fromPoints = function (points, widths, scope) {
44061             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44062             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44063             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44064         };
44065         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44066             var dx1 = s1.x - s2.x;
44067             var dy1 = s1.y - s2.y;
44068             var dx2 = s2.x - s3.x;
44069             var dy2 = s2.y - s3.y;
44070             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44071             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44072             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44073             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44074             var dxm = m1.x - m2.x;
44075             var dym = m1.y - m2.y;
44076             var k = l2 / (l1 + l2);
44077             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44078             var tx = s2.x - cm.x;
44079             var ty = s2.y - cm.y;
44080             return {
44081                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44082                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44083             };
44084         };
44085         Bezier.prototype.length = function () {
44086             var steps = 10;
44087             var length = 0;
44088             var px;
44089             var py;
44090             for (var i = 0; i <= steps; i += 1) {
44091                 var t = i / steps;
44092                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44093                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44094                 if (i > 0) {
44095                     var xdiff = cx - px;
44096                     var ydiff = cy - py;
44097                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44098                 }
44099                 px = cx;
44100                 py = cy;
44101             }
44102             return length;
44103         };
44104         Bezier.prototype.point = function (t, start, c1, c2, end) {
44105             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44106             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44107             + (3.0 * c2 * (1.0 - t) * t * t)
44108             + (end * t * t * t);
44109         };
44110         return Bezier;
44111     }()),
44112     
44113     throttleStroke: function(fn, wait) {
44114       if (wait === void 0) { wait = 250; }
44115       var previous = 0;
44116       var timeout = null;
44117       var result;
44118       var storedContext;
44119       var storedArgs;
44120       var later = function () {
44121           previous = Date.now();
44122           timeout = null;
44123           result = fn.apply(storedContext, storedArgs);
44124           if (!timeout) {
44125               storedContext = null;
44126               storedArgs = [];
44127           }
44128       };
44129       return function wrapper() {
44130           var args = [];
44131           for (var _i = 0; _i < arguments.length; _i++) {
44132               args[_i] = arguments[_i];
44133           }
44134           var now = Date.now();
44135           var remaining = wait - (now - previous);
44136           storedContext = this;
44137           storedArgs = args;
44138           if (remaining <= 0 || remaining > wait) {
44139               if (timeout) {
44140                   clearTimeout(timeout);
44141                   timeout = null;
44142               }
44143               previous = now;
44144               result = fn.apply(storedContext, storedArgs);
44145               if (!timeout) {
44146                   storedContext = null;
44147                   storedArgs = [];
44148               }
44149           }
44150           else if (!timeout) {
44151               timeout = window.setTimeout(later, remaining);
44152           }
44153           return result;
44154       };
44155   }
44156   
44157 });
44158
44159  
44160
44161